CatGPT vs GPT-3 AI/GPT 2023. 2. 15. 11:02

GPT-3

- OpenAI의 3세대 언어모델

- 언어 번역, 텍스트 요약 및 질문 답변을 포함하여 광범위한 자연어 처리 작업에 맞게 미세 조정할 수 있다.

 

 

ChatGPT

- 챗봇 애플리케이션용으로 특별히 설계된 GPT-3 모델의 변형

- 대화형 텍스트의 대규모 데이터 세트에 대한 학습되었다.

- 챗봇 컨텍스트에서 사용하기 더 적합한 응답을 생성할 수 있다.

- 대화에 적절한 상황별 응답을 삽입할 수 있어 일괄된 대화를 유지하는데 더 효과적이다.

- 성능은 GPT-3와 대등하지만, 챗봇에 적합하게 되어 있다. 

 

두 모델 모두 강력한 언어 모델(LLM - Large Language Model) 이지만, 서로 다른 목적을 위해 설계되었다.

 

 

출처:

https://dev.to/ben/the-difference-between-chatgpt-and-gpt-3-19dh

 

The Difference Between ChatGPT and GPT-3

ChatGPT was announced a few days ago, and it has been a big topic of discussion in the tech world,...

dev.to

 

 

아래 내용은 ChatGPT에 문의 한 결과 이다.

 

ChatGPT API와 GPT-3 API는 다음과 같은 차이점이 있습니다.

  1. 훈련된 모델의 크기: ChatGPT API는 OpenAI가 훈련시킨 작은 규모의 GPT 모델을 기반으로 합니다. 반면에 GPT-3 API는 OpenAI가 개발한 대규모 GPT-3 모델을 사용합니다.
  2. 기능의 범위: ChatGPT API는 간단한 대화와 답변 생성을 위한 API입니다. 반면에 GPT-3 API는 ChatGPT API와 유사한 대화 기능을 제공하면서도 더 넓은 범위의 작업, 예를 들면 번역, 요약, 자동 작문 등 다양한 기능을 제공합니다.
  3. 접근 가능성: GPT-3 API는 라이선스 비용이 발생하며, 일부 제한이 있을 수 있습니다. 반면에 ChatGPT API는 무료로 사용할 수 있습니다.
  4. 요청 제한: GPT-3 API는 사용자가 일정 기간 내에 할 수 있는 요청 수에 제한이 있습니다. ChatGPT API의 경우 요청 제한이 없지만, 과도한 사용으로 인한 부하 발생을 막기 위해 OpenAI가 제한을 둘 수 있습니다.
  5. 대화 품질: GPT-3 API의 대화 품질이 ChatGPT API보다 더 우수합니다. 이는 GPT-3 모델의 크기와 더 많은 데이터를 사용하여 훈련되었기 때문입니다.




An error occurred while filtering resources


http://kpopbloging.tistory.com/143



실전 Unity3D Engine Programing 과정 9일차 (2013.08.01 (목))



## 일정치 않은 네트워크 데이터 보간법

0.2초 이내로 온 데이터는 믿을 수 있는 데이터 그 이상 차이난 데이터는 오래전 데이터 이다.

interpolation (보간법)  

- 유효한 네트워크 데이터 수신시 데이터를 부드럽게 보간하는 방법

extrapolation (보외법)

- 일정 시간내에 들어오는 데이터가 없을 경우 데이터의 변화를 부드럽게 보간하는 법




최근에 들어온 위치, 회전각도    time, position, rotation

다음에 들어온 위치, 회전각도    time, position, rotation


## 데디니콘.. 

속도는 시간분의 거리 (거리/시간)

거리는 속도 곱하기 시간

얼마나 빠른 속도로 얼마의 시간동안 움직였냐가 지금 내 위치이다. 

현재 위치가 중요하다..... 

데디니콘.. 

- 네트워크 전송 구간의 로스타임을 감안해서 데이터 수신 측에서 위치를 좀더 이동 시켜 주는 방법..



## 개발하자.

- 2DShooting(7월26일)  게임 프로젝트의 내용을 다시 불러서 이어서 처리한다.

- 아셋 > Prefabs > Player 프리팝을 하나 만든다.

- 하이어아키에서 Player 오브젝트를 선택한다.

  - 컨포너트를 붙인다.  Network View..

  - Tag를 Player로 바꾼다.

  - 이 Player오브젝트를 Player 프리팝에 저장한다.   

  - player를 지운다.

- Save scene, project한다.

- 스크립트에 Netowrk 폴더 만든다.

- PlayerRemote 스크립트를 만든다.


using UnityEngine;

using System.Collections;


public class PlayerRemote : MonoBehaviour {

public class State : System.Object {

public Vector3 p;

public Quaternion r;

public float t;

//스트리밍에사용할클래스.

public State(Vector3 p, Quaternion r, float t)

{

//포지션 타임 로테이션값으로만들었다.

this.p = p;

this.r = r;

this.t = t;

}

}

// 물리인터폴리에션이있다. 이것을사용한다.

public bool simulatePhysics = true;

public bool updatePostion = true;

public float phyInterp = 0.1f;

public float netInterp = 0.2f;

public float ping;

public float jitter;  //고른데이터가아니라서.

public bool isResponding = false;

public string netCode = "(No Connections)";

private int m;

private Vector3 p;

private Quaternion r;

private State[] states = new State[15];

private int stateCount = 0;

private Vector3 velocity;

private State nullstate;

//Network View를적용할수있는것 오브젝트, 카메라, 스크립트.

// Use this for initialization

void Start () {

networkView.observed = this;

nullstate = new State(Vector3.zero, Quaternion.identity, 0.0f);

for(int i=0;i<states.Length;i++)

{

states[i] = new State(Vector3.zero, Quaternion.identity, 0.0f);

}

velocity = Vector3.zero;

}

//시스템의성능이든나쁜성은이드udpate는다르다

//따라서FixedUpdate()를사용한다

//시간이오르걸리는것을사용하면안된다. 물리실험만코딩한다.

void FixedUpdate()

{

if(!updatePostion || states[10].Equals(nullstate))

{

//최소데이터가들어오지않았다면. 

return;

}

//jitter, ping formula

float diffTime = (float)Network.time - states[0].t;

//불필요한것을업애기에떨림현상은제거할수있다.

jitter = Mathf.Lerp(jitter, Mathf.Abs(ping-diffTime), Time.deltaTime*0.3f);

//jitter값보다너무높은것은무시한다는것이다.

ping = Mathf.Lerp(ping, (float)Network.time - states[0].t, Time.fixedDeltaTime *0.3f);

//인터포리레이션 익스폴리레이션이중요하다.

//보간타임이필요하다.

float interpolationTime = (float)Network.time - netInterp;

if(states[0].t > interpolationTime)

{

//시간내에온경우이다.

int i;

for(i=0;i<stateCount;i++)

{

//시간내에들어온것이면.

if(states[i] != null && (states[i].t <= interpolationTime || i == stateCount -1))

{

//최근에들어온것과나중에들어온것을비교한다.

State rhs = states[Mathf.Max(i-1, 0)]; //last ex.0

State lhs = states[i]; //current ex.1

float l = rhs.t -lhs.t;

float t = 0.0f;

if(l > 0.0001f)

{

//잘모르면수학책을봐라.

t= ((interpolationTime - lhs.t)/1);

}

if(simulatePhysics){

//정상적인데이터이다.

   //0.5f는반반씩보간한다는뜻.

transform.position = Vector3.Lerp(transform.position, Vector3.Lerp(lhs.p, rhs.p, t), phyInterp);

transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.Slerp(lhs.r, rhs.r, t), phyInterp);

velocity = ((rhs.p - states[i+1].p)/(rhs.t - states[i+1].t)); //속도는시간분의거리.

} else{

//물리적으로 박는다 너무늦게와서.

transform.position = Vector3.Lerp(lhs.p, rhs.p, t);

transform.rotation = Quaternion.Slerp(lhs.r, rhs.r, t);

}

//응답이있었고에러코드는없었다.

isResponding = true;

netCode = " ";

return;

}

}

}else{

//시간외의경우너무늦게왔다. 외선보간.

float extrapolationLenght = (interpolationTime - states[0].t);

if(extrapolationLenght < 1.0f && states[0].Equals(nullstate)==false && states[1].Equals(nullstate)==false)

{

if(!simulatePhysics)

{

transform.position = states[0].p + (((states[0].p - states[1].p) / (states[0].t - states[1].t))*extrapolationLenght);

transform.rotation = states[0].r;

}

isResponding = true;

if(extrapolationLenght < 0.5f) 

{

//윺효한응답이다.

netCode = ">allowed";

} else

{

//지연응답이다.

netCode = "(Delayed)";

} else{

netCode = "(Not Responding)";

isResponding = false;

}

}

if(simulatePhysics && states[0].t > states[2].t)

{

velocity = ((states[0].t -states[2].t));

}

//유니티의버그로스케일값이사라진다.스케일이사라지면보간한다.

if(transform.localScale == Vector3.zero)

{

//MakePerfectSize함수를호출해서보간한다.

gameObject.SendMessage("MakePerfectSize");

}

//여기까지가이동이다.

}

//위치와이동을15초간보내야한다.

void OnSerializeNetworkView(BitStream stream, NetworkMessageInfo info)

{

//두가지가있다 send, receive(쓸때..

if(stream.isWriting)

{

p= gameObject.transform.position;

r = gameObject.transform.rotation;

m = stateCount == 0? 0:(int)(((float)Network.time - states[0].t)*1000);

//아래순서가중용하다 동일하게받아야한다.

stream.Serialize(ref p);

stream.Serialize(ref r);

stream.Serialize(ref m);

}else

{

stream.Serialize(ref p);

stream.Serialize(ref r);

stream.Serialize(ref m);

State state = new State(p, r, (float)(info.timestamp - (m >0?(m/1000):0.0f)));

if(stateCount == 0)

{

states[0] = new State(state.p, state.r, state.t);

} else if(state.t > states[0].t)

{

for(int k= state.Length-1; k > 0;k--)

{

state[k] = states[k-1];

}

states[0] = new State(state.p, state.r, state.t);

} else

{

Debug.Log("Out-of-Order state received and ignored ~");

}

stateCount = Mathf.Min(stateCount +1, states.Length);

}

}

public Vector3 GetVelocity()

{

return this.velocity;

}

}



PlayerSprite 스프라이트를 수정한다. (Alt+Shift+O)

using UnityEngine;

using System.Collections;


public class PlayerSprite : SpriteBase {

public float Speed = 10.0f;

private float LastShootTime;

public float ShootDelay = 0.2f;

public GameObject bullet;

//new code add

public NetworkPlayer owner;

[RPC]

void SetPlayer(NetworkPlayer player)

{

Debug.Log("SetPlayer Called~");

owner = player;

//PlayerRemote 컴포너트를붙여준다.

gameObject.AddComponent("PlayerRemote");

}

// Use this for initialization

void Start () {

LastShootTime = Time.time;

}

// Update is called once per frame

void Update () {

float moveAmt = Input.GetAxis("Horizontal")

*Speed * Time.deltaTime;

transform.Translate(Vector3.right * moveAmt);

if(Time.time > LastShootTime + ShootDelay)

{

LastShootTime = Time.time;

Instantiate(bullet,transform.position,

transform.rotation);

}

}

}



UnityNetworkCs 스크립트만든다.





EnemySprite 수정하자.

using UnityEngine;

using System.Collections;


public class EnemySprite : SpriteBase {

public float Speed = 10.0f;

public GameObject explosion;

// Use this for initialization

void Start () {

gameObject.AddComponent<BoxCollider>();

}

// Update is called once per frame

void Update () {

//add

//시작한적이없으면아무것도하지않는다.

if(GameObject.FindWithTag("Player") == null) return;

float moveAmt = Speed * Time.deltaTime;

transform.Translate(Vector3.down * moveAmt);

if(transform.position.y < -4.0f)

{

InitPosition();

}

}

void InitPosition()

{

transform.position = new Vector3(

Random.Range(-3.0f,3.0f), //x

5.0f, //y

0.0f);//z

}

void OnTriggerEnter(Collider other)

{

if(other.tag == "bullet")

{

Instantiate(explosion,transform.position,

transform.rotation);

InitPosition();

GameObject main = GameObject.Find("Main");

main.SendMessage("Hit",50);

}

}

}


mainScript도 열자..

using UnityEngine;
using System.Collections;

public class Main : MonoBehaviour {
public GameObject SkyCamera;
public float CameraRotateSpeed = 0.1f;
public GameObject Distance;
public GameObject Life;
public GameObject Point;
private int playerLife = 3;
private int playerPoint = 0;
private float AccelSpeed = 100.0f;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
//add
//시작한적이없으면아무것도하지않는다.
if(GameObject.FindWithTag("Player") == null) return;
SkyCamera.transform.RotateAround(
Vector3.up,Time.deltaTime*CameraRotateSpeed);
Life.SendMessage("SetText",
"X"+playerLife.ToString());
Point.SendMessage("SetText",
"Point:"+playerPoint.ToString());
Distance.SendMessage("SetText",
(Time.time*AccelSpeed).ToString("N0")+" m");
}
void Hit(int p)
{
this.playerPoint += p;
}
void Bomb()
{
Debug.Log("Bomb Button Pressed");
}
}

player 스크립트...에서 다시 만들자.

using UnityEngine;

using System.Collections;


public class PlayerSprite : SpriteBase {

public float Speed = 10.0f;

private float LastShootTime;

public float ShootDelay = 0.2f;

public GameObject bullet;

//new code add

public NetworkPlayer owner;

[RPC]

void SetPlayer(NetworkPlayer player)

{

Debug.Log("SetPlayer Called~");

owner = player;

//PlayerRemote 컴포너트를붙여준다.

gameObject.AddComponent("PlayerRemote");

}

// Use this for initialization

void Start () {

LastShootTime = Time.time;

}

// Update is called once per frame

void Update () {

if(Network.peerType == NetworkPeerType.Disconnected && Network.player == owner)

{

//내것일때만제어가능하다.

float moveAmt = Input.GetAxis("Horizontal")

*Speed * Time.deltaTime;

transform.Translate(Vector3.right * moveAmt);

if(Time.time > LastShootTime + ShootDelay)

{

LastShootTime = Time.time;

Instantiate(bullet,transform.position,

transform.rotation);

}

}

}

}




main 오브젝트에 콤포너트부착한다.

- networkView 상태동기화는 off

- Unity Network 스크립트를 붙인다.

   - player prefabs을 링크를 건다.

   - Spawn Position도 0, -2, 0


실험한다.


New 3D Text를 만든다. 

- 포지션은 0.0.0

- 이것을 Player의 촬일드한다.

- Chareacer Size 0.3

- Anchor upper center

- Alig center

- Bold

-  이름은 Name



정상적으로 진행하지 못했다.

클라우드올린 자료를 봐라...

game_20130801_my



















실전 Unity3D Engine Programing 과정 8일차 (2013.07.31 (수))



복습

터랫

어탯, 충돌, 테크 지정

몬스터 쫒아다니기

카메라 3인칭


카메라 3인칭은 카메라가 공유하고 높이도 알아서 캐릭터를 바라보게 했다.

댐핑을 써서 카메라를 늦게 동작하게 된다...



네트워크 관련된 것 이전에 설명했다.


이제 두개의 스크립트 만든다.

- 네트워크 화면 스크립트

- 출력용 로그 스크립트



@@Tip

앗셋에 폴더 두개 만든다.

- Scripts

   - ScriptsAttribute

     - 클래스의 속성을 정해 줄 수 있다. C#에만 있는 기능이다. 이것을 유니티에서 가능하다.

- Skins

   마우스에서 create > GUI Skin 만든다.



클래스에 속성 주기 스크립트에 기록한다.

using UnityEngine;

using System.Collections;


[ExecuteInEditMode]

public class ScriptsAttribute : MonoBehaviour {

public Rect myRect;

void OnGUI()

{

GUI.Button(myRect,"BUTTON");

}

// Use this for initialization

void Start () {

}

// Update is called once per frame

void Update () {

}

}


빈오브젝트 Test만들고 스크립트를 붙인다.
인스팩트의 MyRect를 클릭해서 좌표를 지정하면
OnGUI
Update는 play하지도 않고 Game화면에서 볼 수 잇다.







[RequireComponent(typeof(Rigidbody))] 속성을 붙여주여주면

리지디바디가 없으면 자동으로 붙여 준다.


using UnityEngine;

using System.Collections;


[RequireComponent(typeof(Rigidbody))]

[ExecuteInEditMode]

public class ScriptsAttribute : MonoBehaviour {

public Rect myRect;

void OnGUI()

{

GUI.Button(myRect,"BUTTON");

}

// Use this for initialization

void Start () {

}

// Update is called once per frame

void Update () {

}

}



##새로 하자.

1. 로그 출력 스크립트를 만든다.

    - 스크롤과 버튼 등 만든다.

    - RPC로 로그를 받는다.

[RPC]  //함수의 속성을 지정하며 이렇게 RPC로 하면 네트워크모듈이 특별관리한다.

public void ScreenLog(string text)

{

}

using UnityEngine;
using System.Collections;

public class Log : MonoBehaviour {
public GUISkin skin;
public int ScrollSize; // 스크롤에차일드 갯수 꼬리에붙여준다.
private int index = 0;
private Vector2 scrollPosition = Vector2.zero;
private string[] logtext;
public static Log logInstance; //싱글톤으로하자.
public bool IsOn = true;  //로그출력On/Off.
void Awake()
{
//신이호출되자마자불린다.
logInstance = this;
//신이바뀌이도로그화면은 계속나와야한다. 
// 아래 것을지정하면신이바뀌어도계속존재한다.
DontDestroyOnLoad(gameObject);
}
// Use this for initialization
void Start () {
//초기화.
if(ScrollSize == 0)
{
ScrollSize = 1000;
}
logtext = new string[ScrollSize];
for(int i =0;i<ScrollSize;i++)
{
logtext[i] = " ";
}
}
void OnGUI()
{
//초기화버튼.
if(GUI.Button(new Rect(0, 0, 48,16), "X"))
{
index =0;
for(int i =0;i<ScrollSize;i++)
{
logtext[i] = " ";
}
scrollPosition = Vector2.zero;
}
IsOn = GUI.Toggle(new Rect(100,0,100,24), IsOn, "On/Off");
if(IsOn.Equals(true))
{
GUI.skin = skin;
//스크롤영역을잡고그안에스크롤바를넣게한다 BeginScrollView와EndScrollView를지정하면그안에영역을잡는다는것이다
scrollPosition = GUI.BeginScrollView(new Rect(10,50,Screen.width - 10, Screen.height -100),scrollPosition, new Rect(0,0,1000,ScrollSize *24));
GUI.color = Color.white;
for(int i=0;i<ScrollSize;i++)
{
GUI.Label(new Rect(0,i*28, 700,28), logtext[i]);
}
GUI.EndScrollView();
}
}
public void SetOnOff(bool onoff)
{
IsOn = onoff;
}
[RPC]
public void ScreenLog(string text)
{
if(logtext == null)
{
return;
logtext[index] = text;
index++;
if(index > ScrollSize -1)
{
for(int i=0;i<ScrollSize;i++)
{
logtext[i] = logtext[i+1];
}
index = ScrollSize + 1;
}
}
}


빈오브제트 만들어 Chat으로 하고 여기에 상기 스크립트를 붙이다. 

NetChat 스크립트를 만든다.

using UnityEngine;

using System.Collections;


public class NetChat : MonoBehaviour {

public string ConnectToIP = "127,0,0,1";

public int connectPort = 12345;

public ArrayList clientScripts = new ArrayList();

private string chatMessage = "";

void Awake() {

DontDestroyOnLoad(gameObject);

Application.runInBackground = true; //포커스를일어도백그라운드에서도는것 정의. 

}

void OnServerInitialized(){

//소켓준비가완료되면호출된다.

Debug.Log("Server Start ~ ");

}

void OnPlayerConnected(NetworkPlayer newPlayer)

{

Debug.Log("A Client connected to me(server) " + newPlayer.ipAddress.ToString());

}

void OnPlayerDisconnected(NetworkPlayer player)

{

Debug.Log("Player Quit~ " + player.ipAddress.ToString());

}

void OnDisconnectedFromServer(NetworkDisconnection info)

{

Debug.Log("Connecting Failed~ "+info.ToString());

}

void OnConnectedToServer()

{

Debug.Log("Connecting Success ~");

}

void OnGUI()

{

if(Network.peerType == NetworkPeerType.Disconnected)

{

//disconnected

GUILayout.BeginArea(new Rect(Screen.width/2-100, Screen.height/2-129, 200, 256));

GUILayout.Label("Connection Sttus: Disconnected");

ConnectToIP = GUILayout.TextField(ConnectToIP, GUILayout.MinWidth(100));

connectPort = int.Parse(GUILayout.TextField(connectPort.ToString()));

//button 2개만들자.

//서버에접속하는클라이언트.

if(GUILayout.Button("Connect as Client", GUILayout.MinHeight(40)))

{

Debug.Log("Click Connect as Client~");

Network.Connect(ConnectToIP, connectPort);

}

//서버로동작하는버튼.

if(GUILayout.Button("Start as Server", GUILayout.MinHeight(40)))

{

Debug.Log("Click Start as Server~");

//32 user수

Network.InitializeServer(32, connectPort, false);

}

GUILayout.EndArea();

} else

{

   //connected

GUILayout.BeginArea(new Rect(Screen.width-150, 20, 150, 200));

if(Network.peerType == NetworkPeerType.Connecting)

{

GUILayout.Label("Connection Status: Connecting");

}else if(Network.peerType == NetworkPeerType.Client)

{

GUILayout.Label("Connection Status: Client!");

GUILayout.Label("Ping to Server "+Network.GetAveragePing(Network.connections[0]));

}else if(Network.peerType == NetworkPeerType.Server)

{

GUILayout.Label("Connection Status: Server!");

GUILayout.Label("Connections: "+Network.connections.Length.ToString());

if(Network.connections.Length > 0)

{

GUILayout.Label("Ping : "+Network.GetAveragePing(Network.connections[0]));

}

}

if(GUILayout.Button("Disconnect"))

{

Debug.Log("Click Disconnect");

Network.Disconnect(200);

}

GUILayout.EndArea();

//채팅.

chatMessage = GUI.TextField(new Rect(Screen.width / 2 -200, Screen.height -30, 400,30),chatMessage);

if(GUI.Button(new Rect(Screen.width/2+220, Screen.height -30,100,30),"SEND"))

{

if(chatMessage != " ")

{

//SendMsg함수가호출되고서버만전달된다.

networkView.RPC("SendMsg ",RPCMode.Server, Network.player.ipAddress + ":" +chatMessage);

chatMessage = " "; // 초기화.

}

}

}

}

[RPC]

public void SendMsg(string msg)

{

Debug.Log("Called SendMsg");

networkView.RPC("ScreenLog", RPCMode.All, msg);

}

}



UI만들때 자동으로 영역을 채우는 방법도 있다. 거의 쓸일은 없다.

GUILayout.BeginArea(new Rect(Screen.width/2-100, Screen.height/2-129, 200, 256));

GUILayout.EndArea();

Rect가 필요없다.



메인카메라 백그라운칼라를 바꿔준다.


Chat 오브젝트 선택하고 menu > componet > miscellonuse > Network View  그리고 syncronization 은 off한다.

NetChat 스크립트 붙인다.

- IP주소 확인한다. 자신의 컴퓨터 것

실행해 본다.



menu > File > Buld Setting 을 누르고 만든 Scene을 넣어 주다.

빌드후 파일명을 넣어 준다. Build.exe

빌드버튼을 누른다.


탐색기에서 해당 파일을 실행한다. 해상도 지정해 준다.

이것은 클라이언트로 실행한다. 


유니티는 다시 플레이해서 서버로 실행한다.

이제 상호 통신해 본다.



실행 순서는

서버를 킨후 클라이언트를 연결 요청한다.




실전 Unity3D Engine Programing 과정 8일차 (2013.07.31 (수))


네트워크 강의를 한다.

아래 내용은 6개월치인데 간단히 하겠다.


네트워크란 통신, 온라인게임 등.....

네트워크에 연결된 기기는 모두 IP로 통신하고 이것이 주소이다.   Infomation Provider

IP는 공유가능하나, 포트는 유일하다. 



소켓 프로그램

- 네트워크를 연결하는 입구..

- 시스템 안에 



http://www.nettention.com/ 

사이트에 가보자.. 

- 유니티 상용서버   

- 프로젝트당 1900만원

- 샘플을 보자.

  http://www.nettention.com/sample

- 이것의 좋은 점은 한글 네트워크 엔진으로 설명문이 한글이다.. 전화로 요청하면 출장서비를 한다.

- 평가판으로 할 수 있다. 


RakNet  http://www.jenkinssoftware.com/

- 오픈 소스

- 멀티플레이어 게임

- P2P 기능이 뛰어나다.

- 이것이 유니티에 기본 포함되어 있다.

- 그렇지만 최근에는 유니티에서 더이상의 업데이트는 하지 않는다고 선언함


유니티  menu > Edit > Project Setting > Network

두개 항목이 나온다.

 Debug Level  : off, infomation, Full 의 조건이 있다.

Sendrate : 15 <== 서버와 통신을 1초에 15회 한다. 



RPC - Remote Procedure Call 원격 제어 콜

- 서로 다른 시스템에서 유니티의 함수를 호출할 수 있다. 별도의 패킷이 없어도 된다.



네트워크 뷰(Network Views)

- State Synchronizaton

   - 두 시스템이 매번 동기화 되어 있어야 한다. 이동, 상태등이..

   - tranforma, 리지디바디, 등(물리시뮬레이션 정보를 추가할 수 있다. 스크립트도 가능) 초당 15번 주고 받는다..

   - 이것은 RCP 정보와 별개로 동기화 한다.

- Reliable Delta Compressed

   

- Observerd

- GameObject

- View ID 

   게임오브젝트에 대해서 아디를 부여해서 필요한 것만. 주고 받을 수 있다.



적용방법은

   GameObject 선택 menu > Componet > Relatvie > Network 

   이렇게 하면 통신이 가능하게 된다.




네트워크_강의.doc



유니티 네트워크

 

한때 PC플랫폼에서의 전유물이었던 대규모 온라인게임은 모바일 하드웨어와 통신 인프라의 발전으로 수많은 현대인들의 필수품이 된 스마트폰에까지 MMO(massive multi-user online)의 개발 열풍이 불고 있습니다.

그런 흐름에 맞추어 유니티3D엔진에서도 많은 MMO게임이 개발되었고 더더욱 많은 MMO 게임이 개발되고 있습니다. 이제 게임 개발에 있어서 필수불가결 항목이 된 네티워크 프로그래밍과 유니티에서 할 수 있는 네트워크 프로그래밍 기법들에 대해서 일부나마 소개하고자 합니다.

 

유니티의 네트워크 프로그래밍에 대해서 소개하기에 앞서 네트워크 프로그래밍이 생소한 개발자들과 독자를 위해 소켓 프로그래밍에 대해서 간단히 소개를 하고 실제 유니티에 돌아가는 소켓 프로그래밍 예제를 설명하고자 합니다.

그리고 나서 유니티에서 제공하는 NetworkView에 대해서 소개하고 실무에 사용되었던 예제 코드를 설명한 후에, 실무에서 주로 사용한 상용 네트워크 라이브러리들에 대해서 소개하고 그중 포톤(Photon)에 대한 사용법과 실무에서 개발하면서의 노하우를 소개하도록 하겠습니다.

 

 

1.  소켓 프로그래밍

 

소켓(Socket)은 소프트웨어로 작성된 통신 접속점입니다. 네트워크 응용 프로그램은 그 소켓을 이용하여 통신망으로 IP 패킷(Packet)을 송수신하게 됩니다. 소켓은 응용 프로그램에서 TCP/IP 계층을 이용하는 창구 역할을 하고 있으며 응용 프로그램과 소켓 사이의 인터페이스를 소켓 인터페이스라고 합니다.

 

유닉스의 예를 들면, 유닉스의 경우 파일을 열면(open) int 타입의 정수를 리턴하는데 이것을 파일 기술자(file descriptor)라고 합니다. 응용프로그램에서 파일을 액세스할 때 해당 파일 기술자를 사용하면 됩니다.

 

파일 기술자는 기술자 테이블(descriptor table) index 번호입니다. 기술자 테이블이란, 현재 open되어 있는 파일의 각종 정보를 포함하고 있는 구조체를 가리키는 포인터들로 구성된 테이블입니다.

 

프로그램에서 소켓을 개설하면 파일 기술자와 똑 같은 기능을하는 소켓 기술자(socket descriptor)가 리턴됩니다. 응용프로그램에서 이 소켓을 통하여 목적지 호스트와 패킷을 송수신할 때 이 소켓 기술자(소켓번호)를 사용하게 됩니다.

 

소켓을 이용하는 네트워크 응용 프로그램에서 상대방 세션과 IP 패킷을 주고 받기 위해서는 다음의 다섯 가지 정보가 정해져야 합니다.

1.     통신에 사용할 프로토콜(TCP 또는 UDP)

2.     자신의IP주소

3.     자신의 Port 번호

4.     상대방의 IP 주소

5.     상대방의 Port 번호

 

이 소켓을 윈도우 환경에서 사용할 수 있도록 한 것을 윈도우 소켓(WinSock)l라 부릅니다.

 

1.     유닉스 소켓과 다른점

A.     윈도우 소켓은 DLL을 통해 대부분의 기능이 제공되므로DLL초기화와 종료 작업을 위한 함수가 필요합니다.

B.     보통 GUI 를 기반으로 하기 때문에 확장 함수가 존재합니다.

C.     멀티쓰레드를 지원하므로 멀티쓰레드 환경에서 안정적으로 동작하기 위한 구조와 함수가 필요합니다.

2.     윈도우 소켓의 장점

A.     호환성이 높아 기존 프로그램을 포팅하기 쉽습니다.

B.     가장 널리 쓰이는 네트워크 프로그래밍 인터페이스

C.     TCP/IP 외에도 다양한 종류의 프로토콜을 지원

D.     저수준, 중간 정도의 프로그래밍 인터페이스므로, 세부적인 제어가 가능

3.     윈도우 소켓의 단점

A.     어플리케이션 수준의 프로토콜을 프로그래머가 직접 설계해야 합니다. 즉 데이터 포맷, 전송 절차등을 고려해야하며 ,프로토콜 변경시 코드 수정이 불가피 합니다.

B.     서로 다른 바이트 정렬방식을 사용하거나 데이터 처리 단위가 서로 다른 중단 시스템간 통신을 할 경우 어플리케이션 수준에서 데이터 변환을 처리해야 합니다.

4.     윈도우 소켓의 구조

A.     대부분의 기능은 WS2_32.DLL MSWSOCK.DLL 로 제공됩니다.

B.    어느 경우든 어플리케이션은 소수의 DLL과 링크되어 실행됩니다.

 

TCP 소켓 프로그래밍 절차

 

TCP 소켓 프로그래밍의 절차는 아래 그림을 참고하시면 됩니다

 

[그림]1 TCP 소켓 통신 순서도

클라이언트-서버(CS) 통신 모델에서는 항상 서버 프로그램이 먼저 수행되고 있어야 하는데, 서버는 Socket()함수를 호출하여 통신에 사용할 소켓을 개설하고 이때 리턴된 소켓 번호와 자신의 소켓 주소를 Bind()를 호출하여 서로 연결 시켜 둡니다. 서버에서 Bind()가 필요한 이유는 소켓 번호는 응용 프로그램이 알고 있는 통신 창구 번호이고, 소켓 주소는 네트워크 시스템이 알고 있는 주소 이므로 이들의 관계를 묶어 두어야(Bind) 응용 프로세스와 네트워크 시스템 간의 패킷 전달이 가능하기 때문입니다.

 

다음에 서버는 Listing()을 호출하여 클라이언트로 부터의 연결 요청을 기다리는 수동 대기 모드로 들어가며, 클라이언트로부터 연결 요청이 왔을 때 이를 처리하기 위하여 Accept()를 호출합니다. 서버는 Accept() 시스템 콜에서 기다리고 있다가 클라이언트가 Connect()를 호출하여 연결 요청을 해오면 이를 처리합니다. 이때 연결이 성공적으로 이루어지면 서버와 클라이언트가 데이터를 송수신할 수 있게 됩니다.

 

한편 클라이언트는 Socket()을 호출하여 소켓을 만든 후 Bind()를 부를 필요 없이, 서버에게 연결 요청을 보내기 위하여 Connect()를 호출합니다. 이때 클라이언트는 접속할 상대방의 서버의 소켓 주소 구조체를 만들어 Connect()의 인자로 주어야 합니다.

 

클라이언트에서 Bind()를 호출할 필요가 없는 이유는 클라이언트 프로그램은 자신의 IP 주소나 포트 번호를 다른 클라이언트 또는 서버가 미리 알고 있을 필요가 없기 때문입니다. , 대부분의 클라이언트는 포트 번호를 특정한 값으로 지정할 필요가 없습니다. 그러나 서버는 미리 지정한 포트 번호를 통하여 클라이언트가 알고 있는 포트 번호를 Bind()로 연결해 두는 것이 필수 적입니다.

 

클라이언트가 Bind()를 사용하면 오히려 클라이언트 프로그램의 범용성이 떨어지게 되는데 왜냐하면 같은 포트번호를 사용하는 클라이언트 프로그램이 하나의 컴퓨터에 두 개 이상 실행되면 포트 번호 중복 사용으로 인하여 에러가 발생하기 때문입니다. 하지만 경우에 따라 이것은 게임 유저의 클라이언트 중복 사용을 막기 위해서 사용되기도 합니다.

 

클라이언트는 Connect()를 호출하기 전에 연결하고자 하는 서버의 주소를 지정하여야 하는데 string 타입의 IP주소를 이용하여 IPAddress 클래스의 IP, 정수 타입의 소켓번호를 이용하여 IPEndPoint라는 클래스를 생성하여 Connect()를 호출합니다.

 

자세한 예제는 아래와 같습니다.

 

코드1의 서버소스를 살펴보시면 Awake()함수에서 소켓을 생성하고 Bind()함수를 호출하고 Listen()함수가 호출된 것을 확인 하실 수 있습니다. 그리고 Update() 함수에서 Select함수를 정기적으로 호출하면서 연결 요청이 왔다면 Accept()함수를 호출해서 클라이언트와 연결을 합니다.

코드2의 클라이언트의 소스를 살펴보시면 Awake()함수에서 소켓을 생성하고 Connect()함수를 호출해서 서버에 연결 요청을 하는 것을 확인 하실 수 있습니다. Update()함수에서는 마우스 클릭을 하면 서버로의 마우스 위치정보를 Send()함수를 호출해서 전송합니다.

코드3 MessageData.cs 를 살펴보시면 MessageData라는 패킷 클래스를 보실 수 있습니다. Serializable 속성을 주어서 MessageData는 객체직렬화 되도록 합니다. 다른 패킷을 만드시거나 테스트를 할 시에 참고하시면 됩니다.

[그림] 2. SimpleNetwork Server Client가 실행된 화면

 

 

 

MMORPG서버개발패턴

|

Game Programming

서버 구조

서버분산패턴

·        MMORPG는 많은 사람들이 접속해서 플레이하는 게임이기 때문에 CPU 부하의 측면에서나 네트워크 전송량의 측면에서나 한대의 머신으로는 충분한 수의 동시접속자를 받아들이기 어렵다. 따라서, 여러대의 머신이 역할을 분담하게 하는 분산처리가 필수적인데, 이 섹션에서는 어떤 방식으로 각 머신들이 역할을 분담하게 할 것인가에 대한 패턴을 소개하고자 한다.

·        Spatial distributed server - 공간단위로 분할된 서버

·        agent thru server - 에이전트서버를 거치는 접속

·        single login server - 단일 로그인 서버

·        multiple login server - 복수개로 분할된 로그인 서버

·        integrated NPC server - 게임서버와 NPC처리 서버가 통합된 형태

·        Separated NPC server - 게임서버와 NPC처리 서버가 분리된 형태

버프로그램구조패턴

·        게임 서버 프로그램은 크게 (분산되어 있는 경우, 각각의 프로그램을 의미한다) 네트워크 처리와 메인 로직처리, DB 처리의 3 가지 역할을 담당해야 한다. 이러한 각각의 역할을 효율적인 방법 (여러개의 CPU를 풀활용하면서, CPU, 메모리, 네트워크 대역폭등의 자원을 최적화하는 것이 목적이다)과 향후 업데이트등에 쉽게 적응할 수 있는 유연한 구조를 본 섹션에서 다루고자 한다.

·        modular structure - 모듈화된 구조

·        acceptor/connector/logic thread - 접속처리기/접속기/로직처리기

·        proactor - 적극적 송수신처리

·        message queue - 메시지 큐

·        ref-counted packet - 참조카운트 패킷

·        ref-counted packet buffer - 참조카운트 패킷버퍼

오브젝트 구조

오브젝트구조패턴

·        게임에 등장하는 오브젝트에는 여러가지 종류가 있다. 그러한 오브젝트를 서버프로그램 안에서 구체적인 클래스로 나타내는 방법을 다룬다.

·        dynamic property object - 가변적 오브젝트 속성

·        static property object - 고정적 오브젝트 속성

·        schema based type - xml 스키마로부터 속성 타입을 정의

·        index based handle - 핸들을 오브젝트 컨테이너 내의 인덱스 기준으로 발급

·        sequential handle - 핸들을 일렬로 발급

·        GUID handle - 글로벌 유니크 핸들 발급

·        ref-counted long reference - 장기적 참조 관계를 카운팅하기

오브젝트관리패턴

·        게임 안에는 복수개의 다양한 오브젝트들이 존재한다. 그러한 오브젝트들을 어떤 방식으로 다뤄야 효율적이고 정확하게 다룰 수 있는지를 논한다.

·        object manager - 오브젝트 관리자

·        pooled object factory - 풀 기반의 오브젝트 생성기

·        section map - 지역단위로 세분화된 맵

·        unique object - 유니크 오브젝트

·        type-safety handle - 타입정보가 포함된 핸들

·        double dispatch - 2중 관계처리

오브젝트행동패턴

·        게임 내의 오브젝트는 유저의 요청 또는 게임적 흐름에 따라 다양한 일을 수행하고, 다른 오브젝트와 상호작용한다. 그러한 행동을 어떻게 구체적으로 정의할 것인지와 어떻게 서로 상호작용하게 할 것인지를 논한다.

·        synchronous messaging - 동기화된 메시지 전송

·        asynchronous messaging - 비동기식 메시지 전송

·        scripted calculation - 계산식의 스크립트화

·        state machine script - 상태머신의 스크립트 기술

·        latent script function - 스크립트상에서의 레이턴트 함수 구현

·        transaction script - 스크립트에 트랜잭션 개념 포함

오브젝트저장패턴

·        MMORPG의 게임 세션은 지속적으로 유지되어야 하므로 게임 서버상의 내용을 DB에 저장하고 다시 복원하여야 하며, 그러한 모든 작업은 사용자의 플레이를 방해하지 않도록 실시간으로 이루어져야 한다. 그러한 효율적인 처리가 가능하도록 DB를 구성하고 억세스하는 방식에 대해 논한다.

·        row data gateway - 테이블 행단위 연결

·        CLOB serialized objects - 인코딩/디코딩 과정을 이용해 다수의 오브젝트를 LOB필드에 저장

·        periodic dumping - 주기적 덤프

·        offline access - 오프라인 액세스

·        middle server - 미들 서버

커뮤니케이션 구조

클라이언트-서버 커뮤니케이션패턴

·        사용자는 주로 TCP/IP 의 클라이언트 서버 모델로 이루어진 네트워크 환경하에서 게임에 접속하는데, 이에는 네트워크 지연과 대역폭제한등의 제약조건들이 수반한다. 그러한 제약조건하에서 최대한 자연스러운 게임적 처리를 가능케 하는 클라이언트와 서버간의 연결방식에 대해 논한다.

·        bit-condensed packet

·        start-end movement

·        dead reckoning movement

·        Client based Collison Detection

·        client proxy

·        neighborhood cache

·        appearance cache

·        string table

서버-서버 커뮤니케이션패턴

·        global/local daemon

·        publisher/subscriber

·        DB based session

·        zone-cross arbiter

기타

보안/치트방지패턴

·        thin client

·        double login prevention

·        direct login prevention

·        speed hack prevention

·        object unique code

·        packet tempering prevention

분류되지 않은 패턴/Idiom

 

 

프라우드넷 샘플 보기

http://www.nettention.com/proudnet/index.aspx?service=sample

 

 

2.  유니티 네트워크

네트워크(Network) 클래스

유니티의 Network 클래스는 네트워크의 핵심 클래스이며 기본 기능들을 제공하는 클래스입니다. 네트워크에 사용되는 많은 인터페이스와 파라메터를 가지고 있으면 서버와 클라이언트를 세팅할 때 쓰이는 클래스입니다. 네트워크 클래스에 많이 쓰이는 함수와 인자를 알아보면 아래와 같습니다.

이벤트에 따라 호출되는 함수

동작 내용

OnPlayerConnected

새로운 플레이어가 접속하면 서버에서 호출 됨

OnServerInitialized

네크워크 초기화가 완료되면 서버에서 호출 됨

OnConnectedToServer

서버로의 연결이 완료되면 클라이언트에서 호출 됨

OnPlayerDisconnected

서버로부터 플레이어의 연결이 끊어지면 서버에서 호출 됨

OnDisconnectedFromServer

서버로의 연결이 끊어지면 클라이언트에서 호출 됨

OnFailedToConnect

서버로의 연결이 실패할 시에 클라이언트에서 호출 됨

OnNetworkInstantiate

서버에서 Network.Instantiate가 호출되면 클라이언트에서 호출 됨

자주 사용되는 변수

변수 설명

connections

모든 연결된 Player

isClient

현재 작동하는 네트워크 모드가 클라이언트인지, 아닌지

isServer

현재 작동하는 네트워크 모드가 서버인지, 아닌지

중요 클래스 함수

함수 동작 설명

InitializeServer

서버로써의 초기화

Connect

특정 주소와 포트를 이용하여 연결

Disconnect

모든 열려있는 연결들을 닫고 네트워크 인터페이스를 종료

AllocateViewID

이용 가능한 네트워크 View ID 숫자를 가져와서 할당한다.

Instantiate

프리팝(Prefab) 네트워크 인스턴스화

[]1. Network 클래스

네트워크 매니저(Network Manager) 소개

유니티3D Edit ->Project Settings -> Network 를 선택하면 Inspector 창에서 아래와 같은 항목을 볼 수 있습니다.

[그림]3 Network Manager

Debug Level 에서 Off를 선택하면 네트워크 에러 메세지만 볼 수 있고, Informational을 선택하면 통신을 하면서 발생하는 이벤트들을 메시지로 볼 수 있고, Full을 선택하면 모든 이벤트들을 확인 할 수 있습니다.

SendRate는 초당 몇회까지 통신을 하는지 조절할 수 있는 파라메터입니다. 이를 높일시 양질의 네트웤 통신이나 데드레커닝 같은 네트워크 클라이언트간의 위치 보간 등이 쉬우나 그만큼 초당 패킷량이 늘어 부하가 발생하게 됩니다.

 

네트워크 뷰(Network Views) RPC(Remote Procedure Call) 소개

네트워크 뷰는 네트워크 간의 데이터를 공유하게 해주는 운송수단이라고 할 수 있습니다. 통신 방법은 상태동기(State Synchronization)와 원격 프로시져 호출(Remote Procedure Calls)이 있습니다.

많은 클라이언트가 같은 게임을 실행시키고 있을경우, 각 클라이언트는 게임을 이루는 오브젝트들을 각각 가지고 있게 됩니다. 이 둘이상의 클라이언트들이 동일하도록 보이도록 할려면 오브젝트들의 데이터들이 공유로 인해서 동기화가 이루어져야 합니다. 이것을 상태 동기화(State Synchronization)라고 합니다. 상태동기화를 위해서는 많은 양의 데이터들을 주고 받아야 하며 게임 플레이가 가능하도록 네트워크를 통해서 데이터를 신뢰성있고 견고하게 유지시켜주어야 합니다.

유니티의 네트워크 뷰(Network View)에서는 이런 상태 동기화를 위해 특정 부분을 공유하도록 할 수 있게 해주고, 각각의 오브젝트들을 동기화 시켜줍니다.

이렇게 위치라든가, 상태라든가 항상 지켜보고 즉각적으로 동기화 시켜주어야 하는게 있는 반면, 그때 그때 이벤트에 따라 일회성 동기화가 필요한 경우가 있습니다. 예를 들어, 어떤 플레이어가 아이템을 얻었다고 한다면 다른 클라이언트에게 먹었다는 것을 한번만(여러 번 알리게 되면 한번에 여러 개의 아이템을 얻은 셈이 된다)알리어서 다른 클라이언트들 화면에 똑같이 먹은 아이템을 사라지게 하는 이벤트 같은 것이 일어나야 합니다. 이런 경우, 항상 일어나는 이벤트도 아닐뿐더러 일회성이어서 항상 아이템의 상태를 지켜보고 있을 순 없는 일이다. 보통의 경우 이럴 때는 서버에서 주변 클라이언트에게 패킷을 날리어 화면에 있는 아이템을 어떤 클라이언트 얻었으니 알아서 지워라 라는 명령을 내립니다.

유니티의 RPC(Remote Procedure Call)를 사용하게 되면 서버 또는 클라이언트에서 다른 서버 또는 클라이언트들이 갖고 있는 특정 함수를 호출하게 하여 RPC를 받은 서버 또는 클라이언트들의 상태나 이벤트등을 동기화 할 수 있습니다.

상세 설명

유니티의 네트워크 뷰는 네트워크뷰아이디(NetworkViewID)라는 것을 통해 구분을 짓게 됩니다. 한 네트워크뷰아이디는 기본적으로 모든 네트웤에 연결된 머신들 사이에서 유일한(Unique) 아이디입니다. 128비트 정수로 구현됐지만 네트워크 전송시에는 자동으로 16비트로 압축되어 보내지게 됩니다. 그렇게 보내지는 각 패킷은 네트워크 뷰가 적용되어 있는 클라이언트 사이드 측으로 도착하게 되고 네브워크뷰아이디를 통해 네트워크뷰를 구분지어서 네트워크뷰아이디에 해당하는 적절한 오브젝트에 패킷으로 날라온 데이터를 적용시키게 됩니다.

만약에 Network.Instantiate()함수를 사용해서 네트워크 오브젝트를 인스턴스화한다면, 네트워크뷰 아이디에 대한 걱정은 할 필요가 없습니다. 인스턴스화 하면서, 즉 생성되면서 고유의 네트워크 아이디를 갖도록 되어 있기 때문입니다.

또한, 수동적으로 네트워크뷰아이디를 각각의 네트워크 뷰에 Network.AllocateViewID를 활용해서 설정하는 것도 가능합니다.

네트워크 뷰(Network View) 컴포넌트

유니티의 네트워크뷰 컴포넌트에 대해서 상세히 알아보도록 하겠습니다. 유니티 네트워크 시스템을 쓴다고 하면 네트워크 뷰 컴포넌트는 반드시 잘 알아두셔야 합니다. 좀더 상세한 정보는 유니티 홈페이지 매뉴얼 페이지를 참조하시고 이 책에서는 꼭 알아 두어야 할 것만 짚고 넘어가도록 하겠습니다.


[그림]4. 네트워크 뷰 컴포넌트

상태동기화(State Synchronization) 또는 원격 프로시져 호출(Remote Procedure Calls)을 쓸려고 한다면 반드시 해당 게임 오브젝트에 네트워크뷰(Network View) 컴포넌트를 붙여주어야 합니다.

위의 그림을 보시면 네트워크 뷰에는 상태동기(State Synchronization)라는 속성과 관찰자(Observed)라는 속성 , View ID라는 속성이 있습니다. 상태 동기 속성의 타입엔 Off Reliable DeltaCompressed Unreliable 이라는 것들이 있는데 각각 상태 동기를 끄는 것과 지난 상태와 달라진 점만 압축해서 보내는 것과 모든 상태를 보내주어서 좀더 많은 네트워크의 대역폭이 필요하지만, 중요한 패킷 손실은 적습니다. Off의 경우는 실무에서 보통 RPC만 사용할 경우 Off로 설정하고 사용하고 있습니다.

상세 설명

게임오브젝트에 네트워크 뷰를 붙여줄 때 크게 두 가지를 고려해야 합니다.

1.    무슨 종류의 데이터를 네트워크 뷰를 통해 주고 받을 것인지

2.    데이터를 어떻게 보낼 것인지

보낼 데이터를 정하기

네트워크 뷰의 관찰자 속성은 단일 컴포넌트를 가르킬 수 있습니다. , 어떤 하나의 컴포넌트만 골라서 네트워크뷰가 주시하고 있는 것입니다. 여기서 주시가 가능한 컴포넌트는 Transform, Animation, RigidBody 또는 스크립트 입니다. 주시하고 있는 컴포넌트가 어떤 것이든지 네트워크를 통해 데이터를 주고 받게 될 것입니다. 만약에 직접적으로 데이터를 주고 받고 싶지 않다면 RPC의 호출을 통해서도 데이터 전송이 가능합니다.

어떻게 데이터를 보낼 것인지

주시하고 있는 컴포넌트의 데이터를 보내는 두 가지 방법은 앞에서도 말했다시피 상태동기화(State Synchronization)RPC(Remote Procedure Calls)입니다.

상태동기화를 사용한다면 타입을 Reliable Delta Compressed Unreliable, 이 둘 중 하나를 선택하면 됩니다. 그렇게 주시하고 있는 컴포넌트의 데이터들은 자동적으로 데이터를 주고 받게 됩니다.

Reliable Delta Compressed 타입은 순서 지향형입니다. 패킷을 받은 순서대로 보내주기 때문인데만약 패킷이 유실이 된다면 그 패킷은 재전송하게 됩니다. 어떤 패킷이 도착했을 때 기존에 받은 패킷이 있었다면 패킷큐에 자동으로 쌓이게 됩니다지난 전송시에 보낸 상태와 다른 상태만이 전송이 되므로 주시하고 있는 컴포넌트의 상태가 바뀐 것이 없다면 보내는 데이터가 없게 됩니다.

만약 스크립트를 주시하고 있다면 반드시 주시하고자 하는 변수를 시리얼라이즈화 해야하는 데 이것은  OnSerializeNetworkView() 함수를 통해 할 수 있습니다. 다음의 예제를 살펴보도록 하죠, 다음의 예제들은 유니티 홈페이지를 참고한 것입니다.

[코드] 4. SerializeNetworkView 예제 - 1

function OnSerializeNetworkView (stream : BitStream, info : NetworkMessageInfo) {

             var horizontalInput : float = Input.GetAxis ("Horizontal");

             stream.Serialize (horizontalInput);

}

위에 함수는 Horizontal 입력 값을 받아서 매 프레임 들어오는 stream에 입력 값을 더해서 보내는 함수입니다. 만약 받는 것과 보내는 스트림 데이터를 다르게 하고 싶다면 BitStream클래스의 isWriting 속성을 사용하시면 됩니다.

[코드] 5. SerializeNetworkView 예제 – 2

function OnSerializeNetworkView (stream : BitStream, info : NetworkMessageInfo) {

             var horizontalInput : float = 0.0;

             if (stream.isWriting) {

                           // Sending

                           horizontalInput = Input.GetAxis ("Horizontal");

                           stream.Serialize (horizontalInput);

             } else {

                           // Receiving

                           stream.Serialize (horizontalInput);

                           // ... do something meaningful with the received variable

             }

}

 

OnSerializeNetworkView sendRate수치에 따라 호출되고 동작하는 것이 정해집니다. sendRate 수치는 앞에서 설명한 네트워크 매니저에서 설정할 수 있습니다. 기본적으로 초당 15회로 세팅되어 있습니다.

만약 원격 프로시져 호출을 스크립트에서 사용하고자 한다면 게임오브젝트에 네트워크뷰 컴포넌트를 붙여야 합니다. 그리고 호출하고자 하는 함수에 자바스크립트라면 @RPC C#스크립트라면 [RPC]를 함수 구현 전에 붙여야 합니다. 그리고 나선 어떤 스크립트가 붙여져 있던 게임오브젝트라면 networkView.RPC()를 호출해서 원격 프로시져 콜을 수행할 수 있습니다.

[코드] 6. 자바스크립트 - RPC 예제

var playerBullet : GameObject;

function Update () {

             if (Input.GetButtonDown ("Fire1")) {

                           networkView.RPC ("PlayerFire", RPCMode.All);

             }

}

@RPC

function PlayerFire () {

             Instantiate (playerBullet, playerBullet.transform.position, playerBullet.transform.rotation);

}

 

또 다른 예제를 살펴 보도록 하겠습니다.

[코드] 7. RPC예제 2 – 자바스크립트

var cubePrefab : Transform;
function OnGUI () {
if (GUILayout.Button("SpawnBox")) {
    var viewID : NetworkViewID= Network.AllocateViewID();
    networkView.RPC("SpawnBox", RPCMode.AllBuffered, viewID, transform.position);
   }
}

@RPC
function SpawnBox (viewID : NetworkViewID, location : Vector3) {
   // Instantate the prefab locally
   var clone : Transform;
   clone = Instantiate(cubePrefab, location, Quaternion.identity) as Transform;
   var nView : NetworkView;
   nView = clone.GetComponent(NetworkView);
   nView.viewID = viewID;
}

 

 

[코드] 8. RPC 예제 2 - C# 스크립트

using UnityEngine;
using System.Collections;

public class example : MonoBehaviour {
public Transform cubePrefab;
void OnGUI() {
   if (GUILayout.Button("SpawnBox")) {
      NetworkViewID viewID = Network.AllocateViewID();
      networkView.RPC("SpawnBox",RPCMode.AllBuffered, viewID, transform.position);
   }
}
[RPC]
void SpawnBox(NetworkViewID viewID, Vector3 location) {
  Transform clone;
  clone = Instantiate(cubePrefab, location, Quaternion.identity) as Transform as Transform;
  NetworkView nView;
  nView =clone.GetComponent<NetworkView>();
  nView.viewID = viewID;
 }
}

 

 

RPC 상세 설명

원격 프로시져 호출(Remote Procedure Call) 은 원격 머신에 함수를 호출할 수 있게 해줍니다. 마치 일반 함수를 호출하듯이 쉽게 사용할 수 있지만 몇 가지 중요한 차이점은 꼭 알아 두셔야 합니다.

1.     RPC 호출은 많은 파라메터들을 가질 수 있습니다. 모든 파라메터들은 네트워크를 통해 전송이 됩니다. 많은 파라메터들을 보낼수록 전송 대역폭은 커집니다. 그러므로 할 수 있는한 최대한 최소의 파라메터만으로 통신하는 것이 좋습니다.

2.     RPC 호출을 할 때 어떤 대상에게 보내는 지를 정하는 것도 중요합니다. 몇가지 RPC 호출 모드로 모든 경우의 호출을 할 수 있는데 RPC함수를 사용해서 쉽게 특정 클라이언트만 , 서버에게만, 서버/클라이언트 모두에게 호출 전송이 가능합니다.

RPC호출은 보통 온라인 게임에서 특정 이벤트를 발생시킬 때 사용하거나, 경쟁하는 두 팀 사이에 특정 정보를 주고 받기 위해 사용합니다. 다만, 자동적으로 이루어지는 것은 아니고 개발자가 직접 RPC 함수를 생성하고 사용해야 합니다. 앞에 예를 든 것 중에 서버에 여러 클라이언트들이 접속해 있고 하나의 클라이언트가 아이템을 얻었다면 모든 클라이언트 화면에 아이템이 사라져야 하고 서버에선 아이템을 얻은 클라이언트에게 해당 아이템 정보를 인벤토리에 넣어주어야 합니다. 이런 경우, 아이템을 얻은 클라이언트가 서버에게 아이템을 얻었다는 RPC콜을 하고 서버는 RPC콜을 한 클라이언트에겐 아이템을 인벤토리에 넣으라는 RPC콜을 하고 다른 클라이언트들에겐 화면 안의 아이템을 다른 클라이언트가 먹었으니 화면에서 삭제해라라는 RPC콜을 합니다.

또 다른 경우로, 서버의 브로드캐스팅(BroadCasting)을 줄여서 클라이언트가 호스트(Host)처럼 동작하기 위해서도 사용합니다. 예를 들면,게임안의 마을 같은 곳에서 어떤 클라이언트가 춤을 추는 애니메이션을 한다던가 인사를 하는 제스쳐를 취한다던가 하는 게임에는 영향을 끼치진 않지만 그 클라이언트를 보고 있는 클라이언트들은 같은 상황을 볼 수 있게 할려면 춤을 추거나 인사를 하고자 하는 클라이언트가 주변 클라이언트에게 내 캐릭터가 이제부터 어떤 행동을 하는 애니메이션을 할 것이다라는 RPC콜을 하면 주변 클라이언트는 해당하는 화면 안의 클라이언트의 캐릭터가 애니메이션이 되는 것을 볼 수 있게 됩니다. 이렇게 서버가 꼭 알아야 할 필요가 없는 경우엔 클라이언트가 직접 주변 클라이언트에게 RPC호출을 하는 경우에도 사용할 수 있겠습니다.

RPC 사용법

이제 두 단계로 어떻게 RPC호출을 셋팅하는지를 살펴보도록 하겠습니다. 일단 RPC호출을 할 함수를 선언하고 함수 앞에 Prefix RPC 속성(attribute)을 주면 됩니다.

아래의 소스코드는 RPC 속성과 함께 함수를 정의하는 예제 입니다.

[코드] 9. RPC의 속성과 함께 함수를 정의하는 예제

// All RPC calls need the @RPC attribute!

@RPC

function PrintText (text : String)

{

    Debug.Log(text);

}

 

RPC 속성을 정의해준 것 만으로 앞으로 PrintText함수를 RPC함수로써 호출 할 수 있게 됩니다. 모든 RPC 네트워크 통신은 네트워크뷰가 붙어있는 같은 스크립트가 붙어있는 게임오브젝트들을 통해 이루어지게 됩니다. 다시말하면, RPC 통신을 하기 위해선 네트워크뷰 컴포넌트를 꼭 게임오브젝트에 추가시켜줘야 하는 것입니다.

RPC 함수의 인자로는 다음과 같은 타입을 사용할 수 있습니다. 꼭 유의하세요:

*       int

*       float

*       string

*       NetworkPlayer

*       NetworkViewID

*       Vector3

*       Quaternion

RPC함수를 호출하는 구문은 아래와 같이 사용하면 됩니다..

networkView.RPC ("PrintText", RPCMode.All, "Hello world");

이 코드는 네트워크뷰가 붙어있는 모든 게임오브젝트중에 PrintText()라는 함수를 가지고 있는 스크립트의 함수를 호출하게 됩니다.

RPC 함수의 첫번째 파라미터는 호출될 함수의 이름을 뜻합니다. 두번째 파라미터는 어떤 대상들이 호출하게 될지 결정하는 것이고, 세번째 파라미터는 호출할 함수에 넘겨줄 인자입니다. 이와 같이 RPCMode.All,즉 연결된 모든 대상들을 상대로 RPC 호출을 할 경우 연결된 대상들에겐 즉각적으로 호출을 요청하지만, 아직 접속중인 클라이언트의 경우 Buffer에서 기다렸다고 접속이 완료되는 대로 호출합니다.함수에서 요구하는 모든 파라메터들은 네트워크를 통해서 RPC함수에 전달이 됩니다. 위와 같은 경우 “Hello World” 라는 파라메터가 텍스트 파라메터로써 PrintText함수에 전달이 됩니다.

invoked RPC 호출을 할 때 외부 파라메터 NetworkMessageInfo라는 구조체의 변수를 외부 파라메터로써 호출이 가능합니다. 딱히 필요 없는 정보라면 전송을 하지 않아도 됩니다.

NetworkMessageInfo 파라메터를 사용한 예제 코드는 아래를 참고하세요

[코드] 10. NetworkMessageInfo 파라메터를 사용한 예제

@RPC

function PrintText (text : String, info : NetworkMessageInfo)

{

    Debug.Log(text + " from " + info.sender);

}

이미 언급했다시피, 스크립트 안에서 RPC 함수 호출 동작 순서는 네트워크뷰가 반드시 게임오브젝트에 붙어 있어야 하고 해당 스크립트를 포함하고 있어야 합니다.

RPC Buffer

RPC 호출은 버퍼(Buffer)에 저장될 수 있습니다. 버퍼화된 RPC호출은 저장되고 새로 연결된 클라이언트의 설정 순서에 맞추어서 차례차례 실행이 됩니다이것은 이미 존재하는 게임에 새로운 유저의 로그인 기능을 쉽게 만들 수 있습니다. 또한 로그인하는 모든 유저에게 특정 레벨을 바로 로딩하도록 할 수도 있습니다. RPC호출은 연결된 모든 클라이언트에게 전송이 되면서 버퍼에 세팅했다가 새로 들어오는 클라이언트에게 똑같은 과정을 거치도록 할 수 있는 것입니다. 또한 새로 들어오는 클라이언트에게 기존의 다른 클라이언트들을 자동적으로 보내어지게 되어 있어서 , 새로 들어온 클라이언트에게 기존의 연결 전체를 보내야 하는 과정이 필요 없습니다.

유니티는 또한 RPC버퍼로 부터의 호출을 제거할 수 있는 기능을 제공합니다. 예를들어 레벨을 로딩한다고 할때, 만약에 게임이 여러 레벨들로 이루어져 진행이 된다고 하면, 새로 연결된 플레이어를 현재 진행중인 레벨을 로딩하도록 할 수 있습니다. 위와 같은 경우엔, 기존에 이미 로드된 레벨 로드 함수들을 RPC 버퍼에서 제거해서 현재 레벨만 로드하도록 구현하면 됩니다.

 

NetworkView RPC의 실습예제

 

그러면 이번 챕터에서는 앞에서 알아본 네트워크뷰와 RPC호출을 사용해서 실제 사용될 수 있는 유니티 네트워크 코드를 살펴보겠습니다.

아래 예제를 실행토록 할려면 먼저 main, Scene stage1 Scene을 만들어야 합니다. Main Scene에서는 먼저 UnityNetwork.js(C# 버전으론 UnityNetworkCS.cs)스크립트 파일을 만들어서 아래의 소스코드를 입력합니다. 그리고 MainCamera에 만든 스크립트 파일(자바스크립트와 C#스크립트 둘 중 하나만)을 붙여 놓습니다. 그리고 캐릭터로 쓸 Prefab을 하나 만들어서 DummyKnight.js(C#버전으론 DummyKnightCS.cs) Prefab에 붙여 놓고 만든 Prefab Main SceneMainCamera UnityNetwork 스크립트 파일의 Inspector 창에서 Knight Player Prefab 이라는 변수에 앞에서 만든 Prefab을 드래그앤드롭으로 붙여 놓아야 합니다.

 

[그림] 4. Main Scene의 세팅

[그림] 5. DummyKnight (임시 캐릭터 프리팝) 세팅

 

 

 

[그림] 7.실행화면

[그림] 8.Console Window 출력 메시지

3.  상용 유니티 네트워크 엔진

 

1.   상용 네트워크 엔진?!

유니티에서 네트워크를 제공하긴 하지만 역시 대규모 게임 서버로썬 부족한 부분이 있습니다. 향후 유니티엔진의 추가되는 기능들의 개발 스케쥴을 보아도 많은 지원을 하진 않는다는 아쉬운 점이 있습니다. 그리고 대형 네트워크 시스템엔 어쩔수 없는 게임마다의 독특한 네트워크 레이어를 로우 레이어(Low Layer) 부터 커스터마이징해서 개발하고자 하는 게임에 맞춤형 시스템을 만들수 밖에 없는데 유니티 엔진에서는 네트워크 레이어정도의 커스터마이징이 불가능합니다.

그래서 유니티 엔진을 선택한 많은 온라인 게임회사들이 네트워크 시스템과 게임 서버를 자체 개발 또는 이미 만들어져서 라이선스를 유료로 판매하는 좋은 상용 네트워크 엔진들을 사용합니다.

 

이 단원에서는 상용 네트워크 엔진엔 어떤 것들이 있는지 알아보기 위해 현존하는 네트워크 엔진중 가장 각광을 받고 있는 두가지 네트워크 엔진인 스마트폭스(SmartFox)와 포톤(Photon)을 소개하고, 그중 포톤이라는 서버 엔진을 직접 사용해본 후기등을 이야기해 보려 합니다.

 

2.   스마트폭스서버(SmartFoxServer)

[그림] 9. 스마트폭스서버

 

스마트폭스서버는 다양한 플랫폼의 서버로써 유명한 네트워크엔진입니다. 최근에 유니티3D .Net 등을 지원하면서 유니티의 네트워크 엔진으로써 주목을 받고 있습니다.

 

특징

스마트폭스서버는 Adobe Flash and Flex 2용 네트워크 엔진으로써 디자인되었고, 또한 자바(Java)와 쇼크웨이브(Shockwave)와 닷넷(.Net)까지 지원합니다.

 

스마트폭스서버는 큰규모의 MMO 게임서버부터 간단한 채팅과 턴베이스(turn-based) 게임까지 커버가 가능하도록 해줍니다. 쉬운 사용법과 더불어 게임에 필요한 고급적인 기능들이 제공되어 네트워크 엔진으로써 각광을 받고 있습니다.

 

[그림] 10. 스마트폭스의 라이선스 형태

스마트폭스서버의 The BASIC edition은 멀티플레이어 응용어플리케이션 개발이 가능하지만 서버측면에서의 코딩은 불가능합니다. 간단한 채팅 게임이나 버디 메신저, 아바타 채팅등을 개발목적으로 만들시에 좋습니다. 서버측면에서의 코딩은 불가능 하다는 것은 제공되는 기능만 사용하고 추가적인 서버 기능은 만들어 넣을 수 없는 것을 의미합니다.

 

-       스마트폭스서버의 The PRO edition 은 대부분의 리얼타임 네트워크 엔진에 사용되며 큰규모의 게임의 개발 목적시에 사용됩니다..

 

-       200유로에 31만원(2011.08기준)정도라고 볼 때 네트워크 엔진을 사용하는 개발사입장에선 충분히 고려한 후에 최종 사용결정을 내리는 것이 중요합니다.

 

-       서버사이드의 기능확장은 액션스크립트(Actionscript),자바스크립트(Javascript),파이썬(Python)과 자바(Java)라는 언어를 사용합니다.

 

-       데이터베이스(DB) 엔진으로 접근이 쉬운 편입니다.( MySQL, Access, MS SQL) . 그리고, 관계형DB시스템 서비스를 서버측면에서 높은 성능으로 지원하고 있습니다.

 

-       웹서버의 기능을 사용해 파일 업로드와 서버의 컨텐츠들을 쉽게 제공할 수 있습니다.

 

-       The BlueBox 라는 모듈이 있는데, 이것은 방화벽과 프록시들에 상관없이 모든 클라이언트들과 연결을 하도록 도와줍니다.

 

-       서버의 프레임워크(framework)는 복잡한 상호 작용을 만드는 하이레벨(high-level) API와 확장 기능을 제공하고 고급 서버 기능에 대한 모든 액세스 권한을 제공합니다

 

-       제이썬(JSON) Raw 문자열(Raw string) 프로토콜을 사용함으로써 빠른 데이터 전송과 실시간 동작에 따른 패킷 전송량을 줄일 수 있습니다.

 

-       친구 목록 매니저는 친구 등록등에 필요한 기능 및 모든 주요 인스턴트 메신저와 같은 기능을 제공합니다

 

스마트폭스의 유니티엔진에서의 기본 사용법 소개

 

먼저, 스마트폭스서버의 홈페이지(http://www.smartfoxserver.com/)중 다운로드 페이지에서 해당하는 플랫폼의 스마트폭스서버의 설치파일을 다운 받습니다. (http://www.smartfoxserver.com/2X/download.php) 그리고

API 페이지(http://smartfoxserver.com/labs/API/)에서 유니티용 다운로드 .Net API를 다운로드 합니다.

다운 받을 파일을 설치한 후에  .Net API 압축파일을 풀은 폴더를 살펴보면 예제폴더(SFS_CSharp_1.2.6\SFS_CSharp_1.2.6\Examples)가 있습니다 그 중 01_SimpleConnect 를 살펴보도록 하겠습니다.

 

설치가 끝났다면 스마트폭스 설치폴더(C:\Program Files\SmartFoxServerPRO_1.6.6)에 가셔서 start 라는 윈도우용 배치파일을 실행시키면 아래와 같은 화면을 보실 수 있습니다.

[그림] 11. 스마트폭스 서버의 콘솔창

스마트폭스서버가 실행이 되었습니다~!

 

자 이제 예제 폴더 SFS_CSharp_1.2.6\SFS_CSharp_1.2.6\Examples\01_SimpleConnect 를 유니티에서 열어보면 프로젝트 폴더 구성은 아래와 같습니다.

[그림] 12. SimpleConnect 예제의 프로젝트 창

 

살펴보시면 Plugins라는 폴더 안에 SmartFoxClient라는 DLL 파일이 들어 있는 것을 확인하실 수 있습니다. 유니티에서 외부 라이브러리(DLL)을 사용할 경우엔 Plugins 폴더 안에 넣어야 합니다.

이렇게 DLL 파일이 Plugins 폴더안에 들어가게 되면 유니티프로젝트에서 SmartFoxClient의 기능을 사용할 수가 있습니다.

 

실행을 해보면 아래와 같은 메시지를 확인하실 수 있으실 겁니다.

[그림] 13. 실행 화면

연결이 성공됐다는 메시지가 화면에 출력이 됩니다.

 

그럼 이제 마지막으로 연결 소스 코드를 한번 살펴 보도록 하겠습니다.

[코드] 15. ConnectionGUI.cs – C# 스크립트 버전

 

using UnityEngine;

using System;

using SmartFoxClientAPI;

 

public class ConnectionGUI : MonoBehaviour

{

             //----------------------------------------------------------

             // Setup variables

             //----------------------------------------------------------

             private string ip = "127.0.0.1";

             private int port = 9339;

             private string statusMessage = "";

 

             //----------------------------------------------------------

             // Called when program starts

             //----------------------------------------------------------

             void Start()

             {

                           SmartFoxClient smartFox = new SmartFoxClient();

                           SFSEvent.onConnection += HandleConnection;

                           smartFox.Connect(ip, port);

             }

               

             //----------------------------------------------------------

             // Draw GUI every frame

             //----------------------------------------------------------

             void OnGUI()

             {

                           GUI.Label(new Rect(10, 10, 500, 100), "Status: " + statusMessage);

             }

 

             //----------------------------------------------------------

             // Handle connection response from server

             //----------------------------------------------------------

             void HandleConnection(bool success, string error)

             {

                           if (success)

                           {

                                        statusMessage = "Connection succesfull!";

                           }

                           else

                           {

                                        statusMessage = "Can't connect!";

                           }

             }           

}

 

 

 

Start()함수를 살펴보시면 SmartFoxClient라는 클래스를 인스턴스화해서 바로 사용하는 것을 보실 수 있고 소스 상단측에 보시면 using SmartFoxClientAPI 라는 구문을 통해서 스마트폭스 클라이언트 DLL을 사용할 수 있게됩니다.

 

만약, 여러분이 스마트폭스서버를 이용하여 네트워크게임의 서버를 개발하실려고 한다면 스마트폭스서버에서 제공하는 50개가 넘는 예제들을 살펴보시고 홈페이지의 문서와 WhitePaper 페이지를 꼭 숙지하는 것이 좋다는 어드바이스를 해드리고 싶습니다. 제공되는 API가 정확히 어떤식으로 돌아가는지를 알아야 서버측에서 생기는 여러가지의 문제점들을 유용하게 대처할 수 있기 때문입니다.

 

3.   포톤(Photon) 네트워크 엔진

.

[그림] 14. 포톤 네트워크 엔진

포톤 네트워크 엔진은 MMO장르에 개발하기 적합한 유니티를 위해 최적화된 소켓 네트워크 엔진입니다. MMO에만 적합한 것은 아니지만 그만큼 대형 서버 네트워크에 어울리는 성능을 가진 엔진입니다. 포톤 역시 스마트폭스서버와 함께 온라인 게임 개발사들이 많이 사용하는 네트워크 엔진이면서 지금도 게임 개발에 쓰기 적합한 많은 기능들이 추가되고 있는 엔진입니다.

 

특징

[그림] 15. 포톤 구성도

위 그림은 포톤의 구성도입니다. C/C++로 네트워크 코어 시스템의 성능을 끌어 올린후 .Net 프레임워크를 얹어서 여러 언어로 만든 개발자의 로직용 소스코드를 강력하게 서버 사이드에서 지원하고 있습니다. 클라이언트 사이드에선 SDK(SoftwareDevelopmentKit)을 사용해서 서버 사이드와 Binary Protocol을 사용해서 통신을 하게 됩니다.

 

서버 게임 로직은 C# 언어를 사용합니다. 이는 유니티에서 C#스크립트를 사용할 경우 코드를 공유해서 쓸 수 있다는 장점이 있습니다.

 

-       간단하고 유연한 RPC호출을 사용할 수 있습니다.

-       Fiber를 통한 메시지 전송은 많은 쓰레드 관련 문제를 해결했습니다.

-       Lite는 간단한 룸지향(Room Based) 게임을 위해 제공됩니다. 이는 고스톱이나 맞고와 같은 게임등을 제작할 시에 유용합니다.

-       클라우드 시스템을 간단히 개발, 추가할 수 있습니다.

-       가벼운 Binary 프로토콜은 전송대역폭 부하와 패킷의 크기를 최적화 하는 데 좋습니다.

-       플랫폼과 상관없이 통신할 수 있도록 하는 기능이 있습니다.

 

개발 시스템 요구사항

 

서버 운영을 위한 추천 사양

Windows Server 2008 - 64 bit

Microsoft .NET Framework 4.0

 

개발에 필요한 추천 사양

Windows XP, Windows Vista or Windows 7

Microsoft .NET SDK 3.5 SP1

Microsoft Visual Studio 2008

 

기본 사용 포트 리스트들

UDP: 5055

TCP: 4530

TCP: 843 (유니티 웹플레이어와 플래쉬 크로스도메인을 위해 사용)

TCP: 943 (실버라이트 크로스도메인을 위해 사용)

 

포톤의 유니티엔진에서의 기본 사용법 소개

 

먼저 포톤의 홈페이지(http://www.exitgames.com/)에서 포톤 서버를 다운받은 후(가입해서 로그인해야 합니다.) 홈페이지 하단부에 유니티 관련 SDK를 받을 수 있습니다. 포톤을 사용할 때  주의해야할 점중 하나가 라이센스(.license)파일입니다. 라이센스 파일에 따라 동시에 접속가능한 유저수와 기능이 제한되기 때문에 free license 파일을 같이 받으셔야 합니다.

 

포톤 서버의 압축을 푼 폴더를 살펴보면 Deploy폴더가 있습니다. 그 중 현재 사용하고 계신 플랫폼에 따라 Window OS 32비트 운영체제를 쓰는지 64비트 운영체제를 쓰는지, XP OS를 사용하고 계신지에 따라 폴더를 선택합니다.

 

폴더 안을 살펴보면 PhotonControl.exe라는 파일이 있습니다. 이파일을 실행시키기 전에 다운받은 라이센스파일을 PhotonControl.exe파일이 있는 폴더 안에 같이 복사해 넣으셔서 덮어씌우셔야 합니다.

이제 PhotonControl.exe를 실행시킵니다.

실행이 되었다면 아래와 같이 작업표시줄에 나타나게 됩니다.

 

[그림] 16. PhotonControl.exe 실행

박스 모양()의 포톤의 로고를 마우스 오른쪽 클릭을 하시면 아래와 같은 메뉴를 보실수있습니다.

[그림] 17. 포톤 아이콘을 마우스 오른쪽 클릭한 메뉴 화면

 

이중 Photon Start as application을 선택하시면

[그림] 18. 툴바 메뉴중 Start as application 을 선택

 

약간의 시간이 흐른 후 포톤이 실행이 된 것을 아래와 같이 확인하실 수 있습니다.

[그림] 19. Photon Application이 정상 동작함

박스모양()의 포톤의 로고에 파란 불이 들어온 것을 확인하셨으면 포톤 서버 실행에 성공하신 겁니다.

 

자 이제 유니티 SDK를 다운받아 압축을 푼 폴더을 살펴보면 샘플 프로젝트가 들어있는 걸 확인하실 수 있습니다. 유니티로 샘플 프로젝트를 열어보겠습니다.

먼저 Project 창을 살펴보시면 아래와 같은 파일들이 프로젝트 Asset폴더안에 들어 있는 것을 확인하실 수 있는데 Photon이라는 폴더 안에 library 폴더로 PhotonUnity3D 외부 라이브러리 파일이 들어 있는 것을 확인할 수 있고 같은 파일이름의 XML파일이 들어 있는 것을 보실 수있는데 이  XML파일안에  라이브러리에 포함된 포톤 클래스의 함수들과 함수 동작에 관련한 자세한 설명을 보실 수 있습니다.

 

[그림] 20. Demo-LiteLobby-ChatRoom 샘플의 프로젝트 창

 

그럼 이제demo-litelobby-chatroom 샘플 데모 프로젝트를 실행 시켜 보겠습니다.

유니티가 실행이 되어서 Project창의 ChatScene 이라는 Scene파일을 더블 클릭하면 아래와 같은 화면을 확인하실 수 있습니다.

[그림] 21. Demo-LiteLobby-ChatRoom 샘플의 Game

 

이제 유니티의 실행 버튼을 누르면 포톤 로비&채팅 데모 프로젝트가 정상동작하시는 것을 확인 하실 수 있습니다.

[그림] 22. Demo-LiteLobby-ChatRoom 샘플의 실행화면

참고로, 포톤의 경우 서버의 설정을 PhotonServer.XML파일을 통해 IP주소라던가 서버에서 실행 할 게임목록등을 정의 할 수 있습니다. 외부파일에서 정의하므로 설정 변경으로 인한 서버의 재컴파일을 안해도 되는 장점이 있습니다.

 

포톤서버에 대한 상세 설명

 

포톤의 Operation Events

 

Operations

Operation RPC(Remote Procedure Call)호출의 포톤만의 형식입니다. 이것은 서버에 구현된 함수를 클라이언트에서 호출하기 위해 사용되며, 클라이언트 측에선 특정 파라메터(위치값등이나 자신의 아이디) Operation 함수의 인자로써 서버에 구현된 함수를 호출하게 되면 전달된 함수의 인자를 갖고 서버에서 처리된 결과값을 해당 클라이언트에 돌려주게 되며 그때 OperationResult()함수가 호출됩니다.

 

Events

Operation과 달리 event는 항상 일어나는 일은 아니고 특정 이벤트나 트리거 등에 의하여 생성되어 클라이언트가 받게되는 일조의 메세지입니다. 이벤트는 서버나 다른 클라이언트에서 전송되어 질수 있습니다.

예를들어 다른 유저가 내가 접속해 있는 방에 접속해 왔을때 나한테 Event 메세지 날라와서 EventAction()함수가 호출 됩니다.

 

포톤의 작업순서

포톤 서버에 접속하고 Room에 참가하는 간단한 클라이언트를 구현한다고 해보겠습니다.

먼저 ,포톤서버의 lite 서버를 사용해서 포톤 lite 서버에서 제공하는 Room Join이나 Exit 기능을 사용하고 클라이언트가 Room join요청시 Room이 하나 생성되고 클라이언트는 특정 ID를 부여 받게 됩니다.

위와 같은 것을 포톤서버를 이용해 구현한다고 한다면

 

-       LiteLobbyPeer 인스턴스를 생성합니다.

 

-       정기적으로 Service()함수를 불러주어서 Event들을 받아오고 Command들을 보내 줍니다. (1초에 10번 정도면 적당합니다.)

 

-       Connect()함수를 호출해서 서버에 연결합니다.

 

-       IPhotonPeerListener.PeerStatusCallBack의 호출이 있을때 까지 기다립니다.

잠시후에, Status.Connect()와 같은 status가 반환되어 옵니다.

 

-       OPJoin()함수를 호출해 게임 접속을 요청하면 잠시후에, LiteOpCode.Join라는 OpCode와 함께 OperationResult()함수가 호출됩니다.

 

-       참고로, 이벤트는 evcode라는 타입으로 IPhotonPeerListener.EventAction() 함수에서 호출됩니다.

 

-      게임에서 나갈려고 한다면, LitePeer.OpLeave()를 호출하면 게임에서 나가게 되고 잠시후에 OperationResult()함수에서 LiteOpCode.Leave 메세지가 반환되어 돌아온다면 Room을 나오는 것에 성공한 것입니다.

 

자세한 구현은 아래 코드를 참고 하시면 됩니다.

 

[코드] 16. PhotonClient.cs

using System;

using System.Collections;

using System.Text;

using ExitGames.Client.Photon;

using UnityEngine;

public class PhotonClient : MonoBehaviour, IPhotonPeerListener

{

    protected LiteLobbyPeer Peer;

    public string ServerAddress = "localhost:5055";

    protected string ServerApplication = "LiteLobby";

public int SendIntervalMs = 100;

    private int NextSendTickCount = Environment.TickCount;

    public PeerStateValue LitePeerState { get { return this.Peer.PeerState; } }

    public ClientState State = ClientState.Disconnected;

    public int ActorNumber;

private StringBuilder DebugBuffer = new StringBuilder();

public string DebugOutput { get { return DebugBuffer.ToString(); } }

  

    public bool DebugOutputToConsole = true;

    public string OfflineReason = String.Empty;

    public enum ClientState : byte

    {

        Disconnected, Connected, InRoom

    }

    public virtual void Start()

    {

        this.Peer = new LiteLobbyPeer(this);

        this.Connect();

    }

    public virtual void Update()

    {

        if (Environment.TickCount > this.NextSendTickCount)

        {

            this.Peer.Service();

            this.NextSendTickCount = Environment.TickCount + this.SendIntervalMs;

        }

    }

    public virtual void OnApplicationQuit()

    {

        this.Peer.Disconnect();

    }

    internal virtual void Connect()

    {

        this.OfflineReason = String.Empty;

        // PhotonPeer.Connect() is described in the client reference doc: Photon-DotNet-Client-Documentation_v6-1-0.pdf

        this.Peer.Connect(this.ServerAddress, this.ServerApplication);

    }

    public void DebugReturn(DebugLevel level, string message)

    {

        this.DebugReturn(message);

    }

 

    public void DebugReturn(string message)

    {

        this.DebugBuffer.AppendLine(message);

        if (this.DebugOutputToConsole)

        {

            Debug.Log(message);

        }

    }

    public virtual void OperationResult(byte opCode, int returnCode, Hashtable returnValues, short invocID)

    {

        this.DebugReturn(String.Format("OperationResult: {0}={1}", opCode, returnCode));

        switch (opCode)

        {

            case (byte)LiteOpCode.Join:

                this.State = ClientState.InRoom;

                this.ActorNumber = (int)returnValues[(byte)LiteOpKey.ActorNr];

                break;

            case (byte)LiteOpCode.Leave:

                this.State = ClientState.Connected;

                break;

        }

    }

    public virtual void PeerStatusCallback(StatusCode statusCode)

    {

        this.DebugReturn(String.Format("PeerStatusCallback: {0}", statusCode));

        switch (statusCode)

        {

            case StatusCode.Connect:

                this.State = ClientState.Connected;

                break;

            case StatusCode.Disconnect:

                this.State = ClientState.Disconnected;

                this.ActorNumber = 0;

                break;

            case StatusCode.ExceptionOnConnect:

                this.OfflineReason = "Connection failed.\nIs the server online? Firewall open?";

                break;

            case StatusCode.SecurityExceptionOnConnect:

                this.OfflineReason = "Security Exception on connect.\nMost likely, the policy request failed.\nIs Photon and the Policy App running?";

                break;

            case StatusCode.Exception:

                this.OfflineReason = "Communication terminated by Exception.\nProbably the server shutdown locally.\nOr the network connection terminated.";

                break;

            case StatusCode.TimeoutDisconnect:

                this.OfflineReason = "Disconnect due to timeout.\nProbably the server shutdown locally.\nOr the network connection terminated.";

                break;

            case StatusCode.DisconnectByServer:

                this.OfflineReason = "Timeout Disconnect by server.\nThe server did not get responses in time.";

                break;

            case StatusCode.DisconnectByServerLogic:

                this.OfflineReason = "Disconnect by server.\nThe servers logic (application) disconnected this client for some reason.";

                break;

            case StatusCode.DisconnectByServerUserLimit:

                this.OfflineReason = "Server reached it's user limit.\nThe server is currently not accepting connections.\nThe license does not allow it.";

                break;

            default:

                this.DebugReturn("StatusCode not handled: " + statusCode);

                break;

        }

    }

    public virtual void EventAction(byte eventCode, Hashtable photonEvent)

    {

        this.DebugReturn(String.Format("EventAction: {0}", eventCode));

    }

}

 

포톤은 클라이언트와 서버간의 패킷을 3가지 Level로 나눠놨습니다. 그래서 각 레벨마다 중요도와 우선순위를 두어서 작동됩니다. 3가지 Level을 살펴보면 아래와 같습니다.

 

Low Level : 서비스, 연결, 끊김등과 같은 네트워크상태에 따른 서버와의 상태에 대한 Level입니다. 이 레벨은 UDP/TCP패킷을 모두 사용해서 명령을 전송합니다. 이것은 연결유지와 RPC 호출 등이 레벨에 속합니다.

 

Logic Level : Operation과 그 결과값, Event와 그 결과값등이 이 레벨에 속합니다. 포톤의 로직 처리에 필요한 레벨이며 Operation은 결과값(리턴값)이 있는 패킷이며 Event는 클라이언트의 특정 상태 업데이트등에 쓰이는 패킷 입니다.

 

Application Level : 특정 응용프로그램에 필요한 것과 특징적인 패킷들의 레벨입니다. 주로 Room이나 Actor들에 대한 패킷등을 이 레벨로 분류합니다.

 

반드시 Low Level이 항상 중요한 것만은 아닙니다만, Low Level 패킷 , Status Command등을 사용하면 내부적으로 연결을 유지시켜주고 패킷 손실이 일어났을 경우 재전송등이 이루어지게 되므로 중요한 패킷은 Low Level에서 이루어지는 것이 좋습니다.

 

모든 Operation 패킷(RPC호출) Prefix로써 Op가 붙습니다.

다른 서버에만 있는 함수를 호출할 경우 다른 파라메터와 다른 값을 돌려 받을수 있습니다. Operation 패킷은 클라이언트 라이브러리엔 포함되어 있진 않지만 OpCustom()함수를 호출해서 구현할 수 있습니다.

 

, IPhotonPeerListener 인터페이스들은 콜백들을 위하여 반드시 따로 구현을 해야합니다.

IPhotonPeerListener 인터페이스의 종류는 아래와 같습니다.

-       PeerStatusCallback() Peer 상태가 변할때(연결,끊김,에러,상태코드 변경) 사용되는 콜백 함수입니다.

-       OperationResult() Operation을 위한 콜백 함수입니다(Join,Leave,등등)

-       EventAction()은 이벤트를 위한 콜백 함수입니다.

-       DebugReturn()함수는 디버그 출력을 위한 콜백 함수입니다.

 

4.   포톤의 실제 서버 개발 후기

[그림]23. P 프로젝트의 Screen Shot

 

위의 스크린샷은 필자가 2010년 여름에 만든 게임 프로젝트로써 아이폰용 MMORPG게임프로젝트 입니다. 유니티엔진과 포톤서버를 사용해서 온라인게임을 제작했고 그 게임을 멀티 플레이 테스트하는 화면입니다.

 

비록 프로토타입 화면이지만 클라이언트와 서버를 모두 담당해서 개발했기 때문에 개발하면서 느낀점이나 노하우등을 소개해보려 합니다.

 

포톤으로의 서버 개발

C#으로 만든 서버?

포톤으로 서버를 개발하게 된다면 C#으로 프로그래밍을 하게 됩니다. 아마 기존에 서버프로그래밍을 전문적으로 담당하셨던 분이라면 C# 서버에 대해서 부정적인 생각이 있을 수 있습니다. C/C++보단 느리고 메모리 관련 문제나 패킷 최적화등을 어떻게 해결할 것인지, 수많은 데이터와 엄청나게 빠른 로직 처리를 해야 하는 게임 서버로썬 프로그래밍 언어에서 오는 네이티브 코드(C/C++)보다 느리다라는 제약까지 업고 개발하는 것은 어떻게 보면 서버프로그래밍의 기본 철칙을 어기는 것이라고 할 수 있기 때문입니다.

하지만 실제 개발을 해보니 C# 게임서버의 성능(Performance)은 서버시스템의 하드웨어의 많은 발전으로 인해 C/C++로 서버를 만들었을 때와 크게 다르지 않은 성능을 내는 것을 확인할 수 있었습니다. (포톤의 경우엔 소켓 통신 Core System C/C++로 구축이 되어 있습니다.)

그리고 성능이 약간 느리더라도 단점을 커버할 수 있는 많은 장점이 있었습니다.

장점 중 하나는 닷넷(.Net)이 제공하는 수많은 편리한 라이브러리를 가져다 쓸 수 있습니다. 쉽고 간단하면서도 빠른 DB 관리 라이브러리들과 LINQ등을 사용할 수 있습니다.

LINQ(Language Integrated Query )는 통합언어 쿼리로 .NET Framework 3.5에 포함되어 있습니다. LINQ to Object, LINQ to SQL, LINQ to XML로 크게 나눌 수 있는데 이는 데이터베이스, XML 등등 개체화하는 데이터 소스에 대해서 전반적으로 쉽게 질의 할 수 있게 제공합니다.

LINQ는 집계 연산을 수행하는 함수를 기본적으로 제공합니다. 기본함수에는 Count, Sum, Min/Max, Average, Aggregate 등등이 있습니다.

아무튼 이 LINQ를 이용해 게임 서버의 필수불가결인 수많은 게임데이터들의 Asset 파일인 XML, EXCEL 등의 파일에 쿼리문을 날려서 원하는 데이터만 추출, 저장, 가공이 쉽게 가능해 집니다. 이는 복잡한 게임 서버일수록 사용하기에 따라 엄청난 효용성을 가지고 있습니다.물론 Asset파일뿐만 아니라 DB SQL도 마찬가지입니다.

또 장점중 하나는 Window OS에서 제공하는 기능들을 쉽게 사용할 수 있다는 것입니다.

포톤의 예를 들면 아래와 같이 Window OS에서 제공하는 성능 모니터를 사용해서 서버 시스템의 성능에 대한 것을 자세히 쉽게 확인할 수 있습니다.

[그림] 24. 포톤의 성능모니터 어플을 실행

 

포톤 서버에서 성능 모니터를 실행시킨후

 

[그림] 25. 윈도우의 성능 모니터 실행 화면

 

위와 같이 현재 서버시스템의 성능을 쉽게 확인 할 수 있습니다.

 

이는 기존의 서버 프로그래밍에서 서버시스템의 운영툴이나 서버 시스템 관리툴을 별도로 제작하지 않아도 되는 이점이 있어, 서버 개발시에 많은 개발시간 단축이나 프로파일링을 통해 성능향상을 시킬 수도 있습니다.

이뿐만 아니라 .Net에서 제공하는 SMTP (simple mail transfer protocol)를 사용해서 서버 시스템의 문제가 있거나 특정 서버 시스템에 과부하가 걸렸을 경우 운영자의 메일이나 운영팀 메일로 현재 상황을 전송하여 원격으로 떨어져 있는 서버 운영팀들이 서버의 상태등을 쉽게 받아 볼수 있는 시스템을 쉽게 구축할 수 있습니다.

 

위와 같은 내용은 사실 C/C++서버에서도 불가능 한 것은 아니고 기존에 많은 사람들이 개발해 놓은 공개되어있는 좋은 라이브러리들을 가져다 써서 개발이 가능한 부분입니다, 하지만 그런 라이브러리들을 가져다 쓸 경우 기능 하나하나를 테스트 해야 하고 검증 단계를 거쳐야 하므로 쉽게 공짜로 된다고는 말할 수 없습니다. 그래서 .Net Framework의 기능들을 사용할 경우 이미 1억이 넘는 수많은 개발자들의 검증/수정 단계를 거친 라이브러리들이므로 최소한 커다란 문제는 없을 거라는 확신을 갖고 게임 서버 개발에 사용할 수 있습니다.

 

유니티와 포톤?

앞에 말씀 드렸다시피 포톤의 경우 C#으로 코딩을 하게 됩니다. 만약 유니티에서 C#스크립트를 사용하셨다면 클라이언트와 서버의 코드 공유가 가능하게 됩니다. 저 같은 경우에도 유니티에선 C#스크립트를 사용했고 서버의 로직의 경우 같은 언어(C#)를 사용함으로 인해 클라이언트와 서버에서의 똑같은 코드 공유가 가능했습니다. 뿐만 아니라 유니티의 네트워크뷰(NetworkView)와 포톤의 서버를 같이 사용했습니다. 같은 던전안에 있는 플레이어들끼리의 위치정보는 클라이언트들끼리의 P2P로써 네트워크뷰를 사용했고 만약 P2P연결이 불가능할시엔 포톤으로 만든 게임 서버를 통해 위치 정보를 공유했습니다. 아이템 구매나 캐릭터의 성장과 같은 중요한 데이터는 포톤서버를 사용했고 , 이벤트 연출이나 애니메이션 변경등은 유니티의 네트워크뷰를 사용했습니다. DB 읽고 쓰기는 포톤서버를 통해서, 이벤트 트리거나 RPC등은 유니티 네트워크뷰를 통해서 활용했습니다.

이렇게 개발을 해보니 생각보다 많은 게임서버의 개발시간을 단축할 수 있었습니다. 맨 처음 보여드렸던 스크린샷 이미지의 프로토타입의 클라이언트+서버 개발에 들어간 시간이 총 6주 입니다. 수정/변경 시간을 뺀 순수 개발시간은 3~4주 정도밖에 걸리지 않았다면 얼마나 많은 개발시간을 단축 할 수 있었는지 가늠하실 수 있을 거라 여깁니다.

 

결론

서두에 말씀 드렸다시피 향후 게임 개발에 있어서 네트워크와 멀티플레이는 필수불가결이 될 것입니다. 이는 수많은 콘솔,패키지 개발사들이 네트워크 시스템 개발에 관심을 갖게 된 배경이기도 합니다.

비록 이곳에선 리뷰와 사용법 소개 등에 그쳤지만 인터넷에 존재하는 수많은 정보와 소스코드들을 참고하신다면 좋은 네트워크 게임을 만드실 수 있을 거라 생각됩니다.

집필을 시작할때만 해도 유니티에서의 네트워크를 소개하고자 큰 포부를 갖고 시작했으나 책의 페이지 구성상 깊은 내용을 다룰 수 없는 점이 있었습니다. 그리고 지금 시점에도 빠르게 변화하고 있는 네트워크 기술들이다 보니, 세세한 설명을 할 수 없었습니다, 그래도 유니티에서 네트워크 프로그래밍을 처음 시작하는 사람들에게 미약하나마 도움이 되었으면 좋겠습니다.

 

주세영

babochuse@nate.com

 

참고 문서나 링크

http://unity3d.com/support/documentation/ScriptReference/index.html

http://www.smartfoxserver.com/

http://www.exitgames.com/

http://www.sqler.com/390183?WT.mc_id=soc-c-kr-loc-w-dw

http://100.naver.com/100.nhn?docid=719227

 






실전 Unity3D Engine Programing 과정 7일차 (2013.07.30 (화))



- 어제 했던 작업 환경에서 추가한다.


오늘은 몬스터를 잡아 보자.

몬스터

충돌

기사에게 달려 오는 것


몬스터와 기사의 거리를 찾고 노말라이제이션 해서 방향 백터로 변경하고 기사에게 달려온다.

기사는 칼로 몬스터를 치는 것이다.


카메라의 파란색이 포워드 백터이다.

포워드 방향의 수직의 직교하는 백터를 구해서 좌우 앞뒤로 이동하게 했다.




캐릭터 이동

- 무브

  이동하면서 충돌까지 채크한다.

- 심플무브

   충돌 없이 이동만 한다.



카메라를 중심으로 좌우 이동한다.


캐릭터를 애니메이션 시킨다.

- 애니메이션의 변환시 블랜딩 처리해서 자연스럽게 한다.

- 애니메이션 클립파일을 변경하기에 코드 변경없이 처리 할 수 있다.


캐릭터를 돌게한다.

- 트랜스폼의 포워드 백터로 움직임까지 변경한다.



## Gravity처리

- 땅에 떨어져 있으면 떨어 뜨린다.

- 터랜의 콜라이더가 없으면 무한정 떨어질 것이다.



using UnityEngine;

using System.Collections;


public class Character_Control : MonoBehaviour {

public float MoveSpeed = 5.0f;

public float RotateSpeed = 500.0f;

public float VerticalSpeed = 0.0f;

private float gravity = 9.8f;

private CharacterController charactercontroller;

private Vector3 MoveDirection = Vector3.zero;

private CollisionFlags collisionflags;

public AnimationClip idleAnim;

public AnimationClip walkAnim;

public AnimationClip attackAnim;

public enum CharacterState

{

IDLE = 0,

WALK = 1,

ATTACK = 2,

SKILL = 3,

SIZE

}

private CharacterState state = CharacterState.IDLE;

// Use this for initialization

void Start () {

charactercontroller = GetComponent<CharacterController>();

animation.wrapMode = WrapMode.Loop;

animation.Stop();

}

// Update is called once per frame

void Update () {

//아래내용은순서대로한것이다.

Move ();

CheckState();

AnimationControl();

BodyDirection();

ApplyGravity();

}

bool isGrounded()

{

//기본제공함수도있지만 성능이좋지않다.

return (collisionflags & CollisionFlags.CollidedBelow) != 0;  //앤드연산.

}

// 중력에의해서떨어지게한다.

void ApplyGravity() 

{

if(isGrounded() == true)

{

VerticalSpeed = 0.0f;

} else 

{

//on air 떨어뜨리겠다는것이다.

VerticalSpeed -= gravity * Time.deltaTime;

}

}

void BodyDirection()

{

//벨로시티는속도이면서방향이다.

Vector3 horizontalVelocity = charactercontroller.velocity;

horizontalVelocity.y = 0.0f;

if(horizontalVelocity.magnitude > 0.0f)

{

Vector3 trans = horizontalVelocity.normalized;

Vector3 wantedVector = Vector3.Lerp(transform.forward, trans, 0.5f);

if(wantedVector != Vector3.zero)

{

transform.forward = wantedVector;

}

}

}

void AnimationControl()

{

switch(state)

{

case CharacterState.IDLE:

animation.CrossFade(idleAnim.name);

break;

case CharacterState.WALK:

animation.CrossFade(walkAnim.name);

break;

case CharacterState.ATTACK:

break;

}

}

void CheckState()

{

if(charactercontroller.velocity.sqrMagnitude > 0.1f)

{

//move

state = CharacterState.WALK;

}else

{

//stand

state = CharacterState.IDLE;

}

}

void Move()

{

Transform cameraTransform = Camera.mainCamera.transform;

Vector3 forward = cameraTransform.

TransformDirection(Vector3.forward);

forward = forward.normalized; //normal vector 1 vector

Vector3 right = new Vector3(forward.z ,0.0f,-forward.x);

float v = Input.GetAxisRaw("Vertical");

float h = Input.GetAxisRaw("Horizontal");

Vector3 targetVector = v *forward + h * right;

targetVector = targetVector.normalized; //normal vector

MoveDirection = Vector3.RotateTowards(MoveDirection,

targetVector,RotateSpeed*Mathf.Deg2Rad*Time.deltaTime,500.0f);

MoveDirection = MoveDirection.normalized;

//중력값적용.

Vector3 grav = new Vector3(0.0f, VerticalSpeed, 0.0f);

Vector3 movementAmt = MoveDirection * MoveSpeed * Time.deltaTime + grav;

collisionflags = charactercontroller.Move(movementAmt);

}

void OnGUI()

{

GUI.color = Color.red;

GUI.Label(new Rect(10,10,100,20),"state : "+state.ToString());

GUI.Label(new Rect(10,30,100,20),"flag :"

+collisionflags.ToString());

}

}


## 어택

- 마우스왼쪽 버튼으로 정의 한다.

- 공격애니메이션 다시 아이들

- 한번만 사용

- 공격 -> 아이들로 이동시 보간

- 애니메이션이 끝나면 아이들로 돌아가야 하낟.
  애니메이션은 노마라이제이션타임이 변화된다. 90%면 변경한다.

- 이동중에 공격을 하려면, 애니메이션이 복합 동작을 하기에 이상하다.

   이런 부분은 디자이너와 함께 상의해야 한다.



using UnityEngine;

using System.Collections;


public class Character_Control : MonoBehaviour {

public float MoveSpeed = 5.0f;

public float RotateSpeed = 500.0f;

public float VerticalSpeed = 0.0f;

private float gravity = 9.8f;

private CharacterController charactercontroller;

private Vector3 MoveDirection = Vector3.zero;

private CollisionFlags collisionflags;

public AnimationClip idleAnim;

public AnimationClip walkAnim;

public AnimationClip attackAnim;

public enum CharacterState

{

IDLE = 0,

WALK = 1,

ATTACK = 2,

SKILL = 3,

SIZE

}

private CharacterState state = CharacterState.IDLE;

// Use this for initialization

void Start () {

charactercontroller = GetComponent<CharacterController>();

animation.wrapMode = WrapMode.Loop;

animation.Stop();

animation[attackAnim.name].wrapMode = WrapMode.Once; //재생모드는1회만하게한다.

animation[attackAnim.name].layer = 1;  //  복합동작시 이레이어설정으로동작의비중이높다.

}

// Update is called once per frame

void Update () {

//아래내용은순서대로한것이다.

Move ();

CheckState();

AnimationControl();

BodyDirection();

ApplyGravity();

}

bool isGrounded()

{

//기본제공함수도있지만 성능이좋지않다.

return (collisionflags & CollisionFlags.CollidedBelow) != 0;  //앤드연산.

}

// 중력에의해서떨어지게한다.

void ApplyGravity() 

{

if(isGrounded() == true)

{

VerticalSpeed = 0.0f;

} else 

{

//on air 떨어뜨리겠다는것이다.

VerticalSpeed -= gravity * Time.deltaTime;

}

}

void BodyDirection()

{

//벨로시티는속도이면서방향이다.

Vector3 horizontalVelocity = charactercontroller.velocity;

horizontalVelocity.y = 0.0f;

if(horizontalVelocity.magnitude > 0.0f)

{

Vector3 trans = horizontalVelocity.normalized;

Vector3 wantedVector = Vector3.Lerp(transform.forward, trans, 0.5f);

if(wantedVector != Vector3.zero)

{

transform.forward = wantedVector;

}

}

}

void AnimationControl()

{

switch(state)

{

case CharacterState.IDLE:

animation.CrossFade(idleAnim.name);

break;

case CharacterState.WALK:

animation.CrossFade(walkAnim.name);

break;

case CharacterState.ATTACK:

if(animation[attackAnim.name].normalizedTime > 0.9f)

{

// 어택애니메이션이거의완료된시점.

animation[attackAnim.name].normalizedTime = 0.0f;

state = CharacterState.IDLE;

} else 

{

animation.CrossFade(attackAnim.name);

}

break;

}

}

void CheckState()

{

if(state == CharacterState.ATTACK)

{

return;

}

if(charactercontroller.velocity.sqrMagnitude > 0.1f)

{

//move

state = CharacterState.WALK;

}else

{

//stand

state = CharacterState.IDLE;

}

if(Input.GetMouseButtonDown(0)) //마우스 0:왼쪽 1:오른쪽 2:휠. 

{

state = CharacterState.ATTACK;

}

}

void Move()

{

Transform cameraTransform = Camera.mainCamera.transform;

Vector3 forward = cameraTransform.

TransformDirection(Vector3.forward);

forward = forward.normalized; //normal vector 1 vector

Vector3 right = new Vector3(forward.z ,0.0f,-forward.x);

float v = Input.GetAxisRaw("Vertical");

float h = Input.GetAxisRaw("Horizontal");

Vector3 targetVector = v *forward + h * right;

targetVector = targetVector.normalized; //normal vector

MoveDirection = Vector3.RotateTowards(MoveDirection,

targetVector,RotateSpeed*Mathf.Deg2Rad*Time.deltaTime,500.0f);

MoveDirection = MoveDirection.normalized;

//중력값적용.

Vector3 grav = new Vector3(0.0f, VerticalSpeed, 0.0f);

Vector3 movementAmt = MoveDirection * MoveSpeed * Time.deltaTime + grav;

collisionflags = charactercontroller.Move(movementAmt);

}

void OnGUI()

{

GUI.color = Color.red;

GUI.Label(new Rect(10,10,100,20),"state : "+state.ToString());

GUI.Label(new Rect(10,30,100,20),"flag :"

+collisionflags.ToString());

}

}


## 기사의 콜라이더 적용

하이어아키에서 kinght를 선택한다. 

   - 인스팩토에서 Tag를 player Tag를 지정한다.

- menu > GameObject > Create other >  Share를 추가한다. 

   - 구하나가 생기는데 이것을 통해 충도 체크 한다.

   - 포지션은 0.0.0

   - Mesh Renderer는 제거한다. 

   - 신창에서 보면, 

- Share 선택하고 Tag에서 Add Tag하고 Element는 sword명으로 추가하고 이것으로 지정한다.

   - 콜라이더는 트리거 체크한다. 

- Bone_R_weapon 를 선택하고 여기에 share를 차일드로 붙인다.

- Sphere  이름은 AttacCol

   포지션 0..0.0

   스케일 1.1.1

   라디오스 0.05

- 신화면에서 AttackCol을 마우스로 잡아서 창끝에 붙여 주도록 한다.

- 자료로 제공한 이팩트 패키지를 추가한다.

- 추가가 완료되면 AttackCol를 선택하고 menu > Component > Effects > Trail Renderer를 선택한다. 

   - AttackCol 에 붙은 Trail Renderer 값을 수정한다.  Time: 0.25, Start Width 1, End Width 0.1

   - Meterials > Elements 0의 오른쪽 끝의 동그라면 버튼을 누르고 BPShork 이팩트를 선택한다.




이팩트가 마음에 안들면 스토어에서 Melee Weaphon을 찾아서 설치해 봐라.



칼끝에 AttackCol을 붙이기가 쉽지 않다.

이럴때는 포지션을 0.0.0  으로 하고 Attackcol의 파란색 방향타를 잡고 이동시킨다.

그래도 약간 틀릴 수가 있다. 로테이션값도 0.0.0으로 조절하고 다시 방향타를 잡고 이동하면서 맞추면 가능하다.






@@TIP

유니티 IDE를 두개 띄우는 방법

menu > edit > preference > Always Show Project Wizard 를 체크한다. / 대신 폴더를 구분해야 한다. 



## 몬스터를 만들자

- Kinght의 Tag는 Player로 설정한다.

- AttactCol의 리지디바디를 추가한다.

   - Use Gravity를 언체크한다.


- 아셋에 poison_beetle을 꺼내 다.

- poison_beetle 의 정보를 변경한다.

   - 포지션 3.0.-5

   - 스케일 10,10,10

- 비틀에게 콜라이더를 붙이자.

  menu > component > Phisics > Box Collider

  -값을 Size : 0.1, 0.1, 0.1

    Center 0, 0.1, 0

- 스크립트 만든다. Beetle_Control



using UnityEngine;

using System.Collections;


public class Beetle_Control : MonoBehaviour {

public Vector3 targetPos = Vector3.zero;

public float MoveSpeed = 5.0f;

public GameObject HitEffect;

public GameObject DeadEffect;

//아래코드는공통코드라서한곳에모아서사용한다.

public enum BeetleState

{

IDLE = 0,

WALK = 1,

ATTACK = 2,

HIT = 3,

SIZE

}

private BeetleState state = BeetleState.IDLE;


// Use this for initialization

void Start () {

animation.wrapMode = WrapMode.Loop;  //반복.

//다짜고짜실행무브.

animation.Play("move");

}

// Update is called once per frame

void Update () {

SearchTarget();

//거리를구하고방향을구하고이동시킨다.

Vector3 currentPos = transform.position;

Vector3 diffPos = targetPos - currentPos;

transform.Translate(diffPos * Time.deltaTime * MoveSpeed, Space.World); //전체월드에서구한다.

}

void SearchTarget()

{

//player의포지션을얻어와서쫒아다니도록한다.

GameObject target = GameObject.FindWithTag("Player");

targetPos = target.transform.position;

}

}

- Beetle에 스크립트를 붙인다.

- DynamicElements_Effects 패키지를 임포트한다.

- Beetle를 선택하고

   아셋에서 SimpleHitEffect을 찾아서 Beetle의 스크립트 Hit Effect에 붙인다.

   아셋에서 FireExplosion을 찾아서  Beetle의 스크립트 Hit Effect에 붙인다.


- 다시 스크립트로 가자. 칼에 부딧히면 이팩트를 발생시키고 목숨이 달면 터치는 이팩트를 발생시킨다.

using UnityEngine;

using System.Collections;


public class Beetle_Control : MonoBehaviour {

public Vector3 targetPos = Vector3.zero;

public float MoveSpeed = 5.0f;

public GameObject HitEffect;

public GameObject DeadEffect;

//아래코드는공통코드라서한곳에모아서사용한다.

public enum BeetleState

{

IDLE = 0,

WALK = 1,

ATTACK = 2,

HIT = 3,

DEATH = 4,

SIZE

}

private BeetleState state = BeetleState.IDLE;


// Use this for initialization

void Start () {

animation.wrapMode = WrapMode.Loop;  //반복.

//다짜고짜실행무브.

animation.Play("move");

}

// Update is called once per frame

void Update () {

SearchTarget();

//거리를구하고방향을구하고이동시킨다.

Vector3 currentPos = transform.position;

Vector3 diffPos = targetPos - currentPos;

//길이가짧으면.

if(diffPos.magnitude < 2.0f)

{

return;

}

diffPos = diffPos.normalized;

transform.Translate(diffPos * Time.deltaTime * MoveSpeed, Space.World); //전체월드에서구한다.

//Player를바라보게한다.

transform.LookAt(targetPos);

}

void SearchTarget()

{

//player의포지션을얻어와서쫒아다니도록한다.

GameObject target = GameObject.FindWithTag("Player");

targetPos = target.transform.position;

}

private int BeetleLife = 10;

void OnTriggerEnter(Collider other)

{

Debug.Log(">> OnTriggerEnter()");

if(other.gameObject.tag == "sword")

{

Debug.Log("++ OnTriggerEnter() Hit BeetleLife"+ BeetleLife);

state = BeetleState.HIT;

//Instantiate(HitEffect, transform.position, transform.rotation);

Instantiate(HitEffect, other.transform.position, transform.rotation);

BeetleLife--;

if(BeetleLife == 0) 

{

Debug.Log("++ OnTriggerEnter() Death" );

state = BeetleState.DEATH;

Instantiate(DeadEffect, other.transform.position, transform.rotation);


Destroy(gameObject);

}

}

}

}



@@TIP

public 으로 선언하게되면...

1. 선언.

2. 인스팩트값

3. start() 코드에 선언한다.

* 따라서 스테이트값은 private로 하는게 좋다.



## 카메라 처리를 한다.

- 3인칭 카메라.. 댐핑카메라, 와우카메라 같은 것...

- 방법

  - player에 뒤로 우리는 원하는 거리만큼 빼고 회전하는 만큼 회전하고 원하는 만큼 높여 준다.

  - 이렇게 하면 케릭터를 바라본다.

-  스크립트 Camera_Control을 만든다. (3인칭)

using UnityEngine;

using System.Collections;


public class Camera_Control : MonoBehaviour {

//Third view point var

public float distance = 10.0f;

public float height = 5.0f;

public float heightDamping = 2.0f;

public float distanceDamping = 3.0f;

public GameObject target; //player

public enum CameraViewPoint {FIRST =0, SECOND =1, THIRD =2, SIZE};

public CameraViewPoint current = CameraViewPoint.THIRD;


// Use this for initialization

void Start () {

}

// Update is called once per frame

void Update () {

}

}



- 한박자 늦은 업데이트.. 캐릭터 움직이고 나서 카메라가 움직이게 한다. 

  업데이트가 모두 끝난 후에 호출되는 함수 

void LastUpdate()

{

switch(current)

{

case CameraViewPoint.THIRD:

ThirdView();

break;

case CameraViewPoint.SECOND:

break;

case CameraViewPoint.FIRST:

break;

}

}

void ThirdView()

{

}

}


이제 타켓을 찾고 플레이어의 방향을 회전한다.


public class Camera_Control : MonoBehaviour {

//Third view point var

public float distance = 10.0f;

public float height = 5.0f;

public float heightDamping = 2.0f;

public float rotationDamping = 3.0f;

public GameObject target; //player

public enum CameraViewPoint {FIRST =0, SECOND =1, THIRD =2, SIZE};

public CameraViewPoint current = CameraViewPoint.THIRD;

void LastUpdate()

{

switch(current)

{

case CameraViewPoint.THIRD:

ThirdView();

break;

case CameraViewPoint.SECOND:

break;

case CameraViewPoint.FIRST:

break;

}

}

void ThirdView()

{

if(target == null)

{

//타켓이없으면찾는다.

target = GameObject.FindWithTag("Player");

} else

{

float wantedRoationAngle = target.transform.eulerAngles.y;

float wantedHeight = target.transform.position.y + height;

//현재내각도와높이는연결해준다. 카메라의 값이다. 보관한다.

float currentRotationAngle = transform.eulerAngles.y;

float currentHeight = transform.position.y;

//LerpAngle를통해서보간한다.

currentRotationAngle = Mathf.LerpAngle(currentRotationAngle, wantedRoationAngle, rotationDamping * Time.deltaTime);

currentHeight = Mathf.Lerp(currentHeight, wantedHeight, heightDamping * Time.deltaTime);

//각도.

Quaternion currentRotation = Quaternion.Euler(0, currentRotationAngle, 0);

//player position

transform.position = target.transform.position;

//move back

transform.position -= currentRotation * Vector3.forward * distance;

transform.position = new Vector3(transform.position.x, currentHeight, transform.position.z);

transform.LookAt(target.transform);

}

}

}


플래이어



- 엠프티오브젝트 만들고 Spawner를 만든다

- 아셋에 Prefab폴더 만들고 Prefabs 만든다. 그리고 Beelte를 여기에 담는다.

- 스크립트 Spawner_Control를 만든다.

using UnityEngine;

using System.Collections;


public class Spawner_Control : MonoBehaviour {

public float SpawnTime = 1.0f;

public float LastSpawnTime;

public GameObject monster;


// Use this for initialization

void Start () {

LastSpawnTime = Time.time;

}

// Update is called once per frame

void Update () {

if(Time.time > LastSpawnTime + SpawnTime)

{

LastSpawnTime = Time.time;

Vector3 pos = new Vector3(transform.position.x + Random.Range(-5.0f, 5.0f), transform.position.y, transform.position.z + Random.Range(-5.0f, 5.0f));

Instantiate(monster, pos, transform.rotation);

}

}

}


Spawner오프젝트에 스크립트를 붙이고 Monster에 Prefab을 붙인다.


벌래가 계속 나오는 것을 볼 수 있다.



실전 Unity3D Engine Programing 과정 6일차 (2013.07.29(월))


Animation.pptx



금일

- 물리

- 세이더.

- 등등..



아틀라스 : 여러개의 이미지를 하나의 이미지로 모은다. 2048x2048, 안전하게 1024x1024가 좋다
드로우컬은 아틀라스의 갯수로 파악이 가능하다.

스크린 사이즈는 절대값을 넣으면 안된다.
화면의 꼭지점의 기준으로 앵커 형태로 개발해야 한다.

핫트윈, 알트윈을 돌려 보았다.

EGGUI - NGUI - TOOLGUI


## 애니메이션에 대해서
- 사용방법, 캐릭터 적용시 사용방법..

The Animation System supports
animation blending,
블랜딩: 또는 믹싱. 서서이 맞추어준다.
mixing,
믹싱: 해당 트랜스폼에 특정 애니메이션을 넣어주는 것이다. 하나만 만들면 다른 곳에도 붙인다.
additive animations,
애디팅 : .. 사용치 않는다.
walk cycle time synchronization,
애니메이션마다 재생 시간이 다르다. 이것을 노말라이즈하여서 동기화 해서 플레이 할 수 있다.
animation layers,
레이어 : 웨이트와 레이어의 비중을 많이 넣어 주면 애니메이션 썩을 때 비중으로 사용한다.
control over all aspects of the animation playback (time, speed, blend-weights),
mesh skinning with 1, 2 or 4 bones per vertex as well as supporting physically based rag-dolls and procedural animation. 
  손목.. 등 다 따라 올라 온다. 




3D애니메이션은 각 타임라인별로 포지션, 로테이션, 스케일까지 하나하나 숫자 값이 바뀌는 것이다.
숫자의 나열을 시간별로 노출 시킨다.
숫자만 뿌리면 느릴 이유가 없지만, 시작과 끝만 나열하고 나머지는 보간 처리(앞뒤것을)하기 때문이다.
CPU를 많이 잡아 먹는다. 움직이는 연산이다. 
실전에서는 많이 사용하지 않는다.



Clip < = 많이 사용한다. 
Stores keyframe based animations.
length Animation length in seconds (Read Only)
frameRate Frame rate at which keyframes are sampled (Read Only)
wrapMode Sets the default wrap mode used in the animation state.


Once
When time reaches the end of the animation clip, the clip will automatically stop playing.
한번재생
Loop
When time reaches the end of the animation clip, time will continue at the beginning.
반복재생
When time reaches the end of the animation clip, time will ping pong back between beginning and end.
왕복
Reads the default repeat mode set higher up.
Plays back the animation. When it reaches the end, it will keep playing the last frame and never stop playing
끝까지 간다.




State <== wrap 과 쌍벽

In most cases the Animation interface is sufficient and easier to use.

Variables
enabled Enables / disables the animation.
weight The weight of animation
wrapMode Wrapping mode of the animation.
time The current time of the animation
normalizedTime The normalized time of the animation.
speed The playback speed of the animation. 1 is normal playback speed.
normalizedSpeed The normalized playback speed.
length The length of the animation clip in seconds.
layer The layer of the animation. When calculating the final blend weights,   animations in higher layers will get their weights
clip The clip that is being played by this animation state.
name The name of the animation
blendMode Which blend mode should be used?





두개가 썩인다.
Void Update()
{

If(Input.GetAxis(“Vertical”) > 0.2f)

{

  animation.CrossFade(“walk”);

}else

{

  animation.CrossFade(“idle”);

}

}




필히 셋팅해야 한다. 
기본으로 Loop을 사용한다. 나머지는 하나하나 꺼내서 세팅한다. 
Void Start () {

  animation.wrapMode = WrapMode.Loop;

  animation["shoot"].wrapMode = WrapMode.Once;

  animation["shoot"].layer = 1;

  animation.Stop();  //무조건 한번 호출

}



실슴..
void Start () {

    Transform mixTransform;

    mixTransform = transform.Find("root/upper_body/left_shoulder"); animation["wave_hand"].AddMixingTransform(mixTransform);

}



애니메이션 이벤트 <== 특정함수에 특정 이벤트 호출

function PrintFloat (theValue : float) {

  Debug.Log ("PrintFloat is called with a value of " + theValue);

}


최적화

Use one Skinned Mesh Renderer
Your character should use only a single skinned mesh renderer.
한개의 스킨을 사용해야 한다. 차이들을 포함해서
Don't Use Many Materials
You also want to keep the number of materials on that mesh as low as possible.
여러개 사용하지 않는게 좋다.
Reduce Amount of Bones
Medium Desktop games use bone hierarchies with 15-60 bones. The fewer bones you use the faster; with 30 bones you can achieve very good quality on Desktop platforms and fairly good quality on Mobile Platforms..
30개의 본을 사용해라 37이상이면 성능 저하 심하게 발생 최대 35개 좋다.
발 4개 캐릭터는 매우 힘들다... 
Polygon Count
How many polygons you should use depends on the quality you require and the platform you are targeting. Anything between 300-1500 triangles on Mobile Platforms and 500-6000 triangles on Desktop Platforms is reasonable.
옛날 이야기이다. 6000개 이상 넘지 말아라.. 1500 ~ 6000개 이내
Separate Out IK and FK
Separate out inverse kinematics (IK) and forward kinematics (FK). When animations are imported, the IK nodes are baked into FK, thus Unity doesn't need the IK nodes at all.
FK는 지원하지 않는다. 계단 올라가는 뼈대... 지원하지 않는다.
Use Reusable Rigs
Create a rig which you can reuse. This allows you to share animations between different characters.
본 세팅만 같으면 남의 애니메이션을 재 사용할 수 있다. 
Name Bones Correctly

Name the bones correctly (left hip, left ankle, left foot etc.). Especially with characters, naming your bones correctly is very important.
BIP001 등 .. 이름을 바꾸지 않는다. 바이패드 이름 수정하지 마라..




퀄리티..

- Quality Settings.. 간단히 셋팅되어 있다.

- Blend Weights가 중요한 것이다.




## 애니메이션 실습을 해 보자 

1 New Project  > AnimationTest

2. 신 : test

3. 폴더: Scripts

4. AnimationExample 스크립트 만든다.

   - 함수 추가

     void EventTest(int i) 

{

Debug.Log("Event Test Click~ "+i.ToString());

}

5. 큐브 추가 

   포지션은 0.0.0  . 검개 나온다.

6. 빛 추가

   

7. Menu > Windows > Andimation 창을 연다.

- Cube를 선택하고 한다.






8. 애니메이션 창에서 Cube를 선택하고 NewAnimation를 만든다.

9. 레코드 버튼을 누르고 값을 변환해 보고 실행해 본다.


10. 아까 만든 AnimationExample 스크립틀 추가 한다. 




11. 애니메이션 창을 새로 열고 10번째 선택후 이벤트를 추가한다. (키 이벤트 삽입 버튼)

아래 이미지중 3번째 언더바가 키 이벤트 삽입 버튼 4번째 언더바가 해당 이벤트



12. 10번 프레임에서 이벤트를 클릭하면 아까지 만든 메소드명이 뜨고 그곳에 5값을 넣는다.

상기 이미지에서 4번째 언더바를 클릭하면 아래와 같은 창이 나온다.




13. 플레이 해 보면 콘솔에 다음 내용이 출력된다.

Event Test Click~ 5

UnityEngine.Debug:Log(Object)

AnimationExample:EventTest(Int32) (at Assets/Scripts/AnimationExample.cs:18)


이렇게 트랜스폼 중심으로 애니메이션과 스크립트를 만들 수 있다.


## 인터넷 유니티 사이트의 애니메이션 설명을 보자

http://docs.unity3d.com/Documentation/ScriptReference/index.html





## 캐릭터에 애니메이션 

1. New Scen > test 2

2. 오늘자 KnightandBeetle 리소스 패키지를 추가한다. 

- 기사

- 비틀이 있다.

3. 추가한 것에 Knight를 히어알키에 추가한다.

4. 스케일 값을 키우다. 10.10.10, 로테이트는 0.180.0 포지션은 0.0.-5

5. 라이트 추가

6. knight를 선택하고 menu > windodw > animation를 선택한다.

7. 각 동작별로 변경해 보며 플레이 해봐라. 애니메이션 클립 내용을 볼 수 있다.

- 32 프레임이다. 유니티는 .. 맥스랑 다르고, 언리얼은 60프레임 따라서 이창에서 확인해 보는게 좋다.


## 에니메이션과 손을 믹싱과 애드해 보자.

1. AnimationTest 스크립트 추가

using UnityEngine;

using System.Collections;


public class AnimationTest : MonoBehaviour {

public AnimationClip idleAnim;

public AnimationClip attackAnim;

public Transform mixingTransform;

// Use this for initialization

void Start () {


//기본적인것2개추가한다.

animation.wrapMode = WrapMode.Loop;

animation.Stop();

//mixing  해쉬맵에키값을넣으면스테이트값이나오고이것을믹싱한다.

animation[attackAnim.name].AddMixingTransform(mixingTransform);

//= animation["attack_01"] = AnimationState 

animation.CrossFade(attackAnim.name);

}

}


2.  knight에 스크립트를 추가한다.
3. knight의 애니메이션 항목을 펼치고 idle를 선택하면 좌측에 리소스가 노란색으로 노출된다. 
4. 해당 리소스를 선택해서 스크립트 해당 변수에 드래그드롭으로 붙여 준다.



5. kinght을 열어 보면 Bip001 Pelvis가 있고 이것을 knight 스크립트 믹싱 트랜스폼에 붙인다.




6. play해 본다.

7. 다른 것도 미싱트랜스폼에 넣어서 플래이 해봐라.



## Additive를 해보자

1. 스크립트를 수정하자. 이전에 코딩은 주석하고 추가한다.

using UnityEngine;

using System.Collections;


public class AnimationTest : MonoBehaviour {

public AnimationClip idleAnim;

public AnimationClip attackAnim;

public Transform mixingTransform;

// Use this for initialization

void Start () {


//기본적인것2개추가한다.

animation.wrapMode = WrapMode.Loop;

animation.Stop();

//mixing  해쉬맵에키값을넣으면스테이트값이나오고이것을믹싱한다.

//animation[attackAnim.name].AddMixingTransform(mixingTransform);

////= animation["attack_01"] = AnimationState 

//animation.CrossFade(attackAnim.name);

//additive

animation[attackAnim.name].blendMode = AnimationBlendMode.Additive;

animation[attackAnim.name].layer = 10;

animation[attackAnim.name].weight = 1.0f;

animation[attackAnim.name].enabled = true;

animation[idleAnim.name].wrapMode = WrapMode.Loop;

animation.Play(idleAnim.name);

}

}


2. knight의 Idle anim을 move 리소스를 붙여 본다.

3. play해 보면.. 뛰면서 칼을 휘두른다.

layer와 weight을 바꿔 보면 다르게 동작이 될 것이다.

save secen , save project



## RPG를 만들어 보자.

사전지식

- 지형 만들고

- 캐릭터 세우고

- 캐릭터 이동하면서 애니메이션 된다.



1. New Project > RPG

2. 신저장 > main
3. Direciton light 추가 한다.
4. menu > Terrain > create Terrain
5.menu > Terrain > set Resolution의 값을 반띵한다.
6. 포지션은 -500,0,-500
7. 앗셋에서 임포트패키지에서 터레인을 임포트한다.
8. 하이어키에서 터레인 선택하고 인스패트에서 붓모양의 아이콘을 클릭한다.
9. 애드 텍스쳐를 누른다. 그리고 셀랙트를 누른다. GoodDuct를 추가한다. 그리고  add한다.
10. 애드 텍스쳐를 누른다. 그리고 셀랙트를 누른다. Glass를 추가한다. 그리고  add한다.
11. 해당 클라스를 선택하고 신창에서 글을 써봐라. 그러면 잔디밭이 추가된다.
12. 테란을 선택하고 인스패트에서 첫번째 아이콘으로 언덕을 만든다. shit 아이콘 누르면 낮아 진다.

 


13. 터레인에서 5번째 아이콘으로 나무를 심어 보자.


Add 버튼으로 추가한후 땅을 클릭해 보면 나무가 추가된다. 



윈드로 설정할 수 있는데 


다른 아이콘은 잔디도 심을 수 있다.



##

1. KnightandBeetle 패키지 추가

2. Knight 를 추가 하낟. 포시션 0, 0, -5, 로테이션 0, 180, 0, 스케일 10,10,10

3. Kinght 선택 menu > component > phisice > charactor controler



값은  center 0, 0.1, 0  각도 0.05, 높이 0.2

4. 스크립트 추가 Charictor_control

캐릭터와 카메라의 관계에서 카메라가 바라보는 위치에 따라서 캐릭터의 방향이 달라진다.

카메라중심으로 캐릭터를 움직여야 한다.

카메라에서부터 시작해야 한다.

카메라가 어디에 있냐. 카메라의 앞방향의 캐릭터의 앞뒤 방향.. 좌우가 .. 

이것을 정확히 90를 구분하면 좌우가 된다.

백터는 방향을 갖는 두 힘이다. 이렇게 축이 된다.

카메라 포워드 (트랜스.포워드) 를 이용해서 캐릭터의 앞뒤, 직교하는 좌우를 구한다. 

두백터를 더하면 캐릭터의 이동이 나온다.

시간에 따라 방향만 구하면 된다.

이것을 백터의 노멀라이즈라고 한다.

길이가 있는 것을 방향을 구해 준다.




void Move() 

{

//카메라를구한다.

Transform cameraTransform = Camera.mainCamera.transform;

Vector3 forward = cameraTransform.TransformDirection(Vector3.forward);

forward = forward.normalized; //normal vector 1 vector

Vector3 right = new Vector3(forward.z, 0.0f, -forward.x);

//좌우백터.

//움직인다.

}


여기서

Vector3 right = new Vector3(forward.z, 0.0f, -forward.x); // 이것의 의미는 


A.B = 0 일려면, 

AxBx + AyBy = 0;



완성된 스크립트

public float RotateSpeed = 500.0f;

public float VerticalSpeed = 0.0f;

public float gravity = 9.8f;

private CharacterController charactercontroller;

private Vector3 MoveDirection = Vector3.zero;

private CollisionFlags collisionflags;

// Use this for initialization

void Start () {

charactercontroller = GetComponent<CharacterController>();

}

// Update is called once per frame

void Update () {

Move();

}

void Move() 

{

//카메라를구한다.

Transform cameraTransform = Camera.mainCamera.transform;

//좌우백터. 축을구한다.

Vector3 forward = cameraTransform.TransformDirection(Vector3.forward);

forward = forward.normalized; //normal vector 1 vector

//방향을바꿔져서직교이다.

Vector3 right = new Vector3(forward.z, 0.0f, -forward.x);

//움직인다.

float v = Input.GetAxisRaw("Vertical");

float h = Input.GetAxisRaw("Horizontal");

Vector3 targetVector = v * forward + h * right;

targetVector = targetVector.normalized; // normal vector

//벡터를 방향으로옮기기 위해서 방향을바꿔준다.

MoveDirection = Vector3.RotateTowards(MoveDirection, targetVector, RotateSpeed * Mathf.Deg2Rad * Time.deltaTime, 500.0f);

MoveDirection = MoveDirection.normalized;

Vector3 movementAmt = MoveDirection * MoveSpeed * Time.deltaTime;

collisionflags = charactercontroller.Move(movementAmt);

}

}




이동시키는 코드를 스크립트로 짠것이다.

5. knight에 방금 만든 스크립트를 추가한다.



캐릭터를 캐릭터콘트롤을 쓰는게 좋다. 그렇지만 리니지바디보다는 무겁다. 

캐릭터일 때만 사용해라..

collisionflags를 통해서 이 값이 어디에 있는지 알 수 있다.

캐릭터콘터롤러는 기본적으로 그라비티가 없기에 추가해 주어야 한다.



## 애니메이션 시켜 보자


1. 스크립트에 아래 내용을 추가한다.

//애니메이션클립을가져오기위해서퍼블릭으로했다.

public AnimationClip idleAnim;

public AnimationClip walkAnim;

public AnimationClip attackAnim;

public enum CharacterState

{

IDLE = 0,

WALK = 1,

ATTACK = 2,

SKILL = 3,

SIZE

}

상태를 바꿔가면서 애니메이션을 바꿔준다.

스크립트를 빼는 기준은 중복된 코드는 스크립트로 뺀다.

// Update is called once per frame

void Update () {

Move();

CheckState();

AnimationControl();

}

void AnimationControl() 

{

//상태별로애니메이션을변경한다.

switch(state)

{

case CharacterState.IDLE:

animation.CrossFade(idleAnim.name);

break;

case CharacterState.WALK:

animation.CrossFade(walkAnim.name);

break;

case CharacterState.ATTACK:

break;

}

}

void CheckState()

{

//캐릭터콘트롤러는움직이면속도를알려준다

//길이 루크길이.

if(charactercontroller.velocity.sqrMagnitude > 0.1f)

{

//move

state = CharacterState.WALK; 

} else 

{

// stand

state = CharacterState.IDLE;

}

}


총..

using UnityEngine;

using System.Collections;


public class Charictor_control : MonoBehaviour {

public float MoveSpeed = 5.0f;

public float RotateSpeed = 500.0f;

public float VerticalSpeed = 0.0f;

public float gravity = 9.8f;

private CharacterController charactercontroller;

private Vector3 MoveDirection = Vector3.zero;

private CollisionFlags collisionflags;

//애니메이션클립을가져오기위해서퍼블릭으로했다.

public AnimationClip idleAnim;

public AnimationClip walkAnim;

public AnimationClip attackAnim;

public enum CharacterState

{

IDLE = 0,

WALK = 1,

ATTACK = 2,

SKILL = 3,

SIZE

}

private CharacterState state = CharacterState.IDLE;

// Use this for initialization

void Start () {

charactercontroller = GetComponent<CharacterController>();

animation.wrapMode = WrapMode.Loop;

animation.Stop();

}

// Update is called once per frame

void Update () {

Move();

CheckState();

AnimationControl();

}

void AnimationControl() 

{

//상태별로애니메이션을변경한다.

switch(state)

{

case CharacterState.IDLE:

animation.CrossFade(idleAnim.name);

break;

case CharacterState.WALK:

animation.CrossFade(walkAnim.name);

break;

case CharacterState.ATTACK:

break;

}

}

void CheckState()

{

//캐릭터콘트롤러는움직이면속도를알려준다

//길이 루크길이.

if(charactercontroller.velocity.sqrMagnitude > 0.1f)

{

//move

state = CharacterState.WALK; 

} else 

{

// stand

state = CharacterState.IDLE;

}

}

void Move() 

{

//카메라를구한다.

Transform cameraTransform = Camera.mainCamera.transform;

//좌우백터. 축을구한다.

Vector3 forward = cameraTransform.TransformDirection(Vector3.forward);

forward = forward.normalized; //normal vector 1 vector

//방향을바꿔져서직교이다.

Vector3 right = new Vector3(forward.z, 0.0f, -forward.x);

//움직인다.

float v = Input.GetAxisRaw("Vertical");

float h = Input.GetAxisRaw("Horizontal");

Vector3 targetVector = v * forward + h * right;

targetVector = targetVector.normalized; // normal vector

//벡터를 방향으로옮기기 위해서 방향을바꿔준다.

MoveDirection = Vector3.RotateTowards(MoveDirection, targetVector, RotateSpeed * Mathf.Deg2Rad * Time.deltaTime, 500.0f);

MoveDirection = MoveDirection.normalized;

Vector3 movementAmt = MoveDirection * MoveSpeed * Time.deltaTime;

collisionflags = charactercontroller.Move(movementAmt);

}

void OnGUI()

{

GUI.color = Color.red;

GUI.Label(new Rect(10,10,100,20),"state : "+state.ToString());

GUI.Label(new Rect(10,30,100,20),"flag : "+collisionflags.ToString());

}

}



그리고 knight의 각각의 스크립트의 animation에 물리항목을 붙이도록 한다.

완성된 화면이다.


오른쪽에 state는 방향이고 flag는 땅의 상태를 나타낸다.


## 캐릭터의 움직임의 변환 처리
바디디랙션
1. 스크립트에서 
// Update is called once per frame
void Update () {
//아래내용은순서대로한것이다.
Move ();
CheckState();
AnimationControl();
BodyDirection();
}
void BodyDirection()
{
//벨로시티는속도이면서방향이다.
Vector3 horizontalVelocity = charactercontroller.velocity;
horizontalVelocity.y = 0.0f;
if(horizontalVelocity.magnitude > 0.0f)
{
Vector3 trans = horizontalVelocity.normalized;
Vector3 wantedVector = Vector3.Lerp(transform.forward, trans, 0.5f);
if(wantedVector != Vector3.zero)
{
transform.forward = wantedVector;
}
}
}

이렇게 추가하면 방향으로 속도로 애니메이션을 보간한 것이다.
캐릭터가 확돌다가 천천히 돌것이다. move디렉션으로 바디디렉션으로 처리한 것이다. 

120줄에 이동하면서 애니메이션 되는 것은 유니티만 가능한 것이다.

오늘 복습
클립을 가져와서 블랜딩한다.

캐릭터를 띄우기 위해서 카메라

카메라 값을 가져와서 캐릭터를 돌렸다.



내일 할일...
와우카메라. 스프링 카메라..
몬스터 띄우서 몬스터 잡아 보자.



실전 Unity3D Engine Programing 과정 5일차 (2013.07.26 (금)) 


자료 모음: C:\Users\Mac\Documents

한글사이트: www.unity3dkorea.com 



##리뷰..

Component Centric 엔진


## 오늘 할일

- NGUI 

- 애니메이션


## NGUI 적용하기

- 어제 하던 것에서 NGUI 을 임포트한다.

- menu > Assets > Refresh 한다. 그러면 메뉴에 NGUI가 추가된다.


[설정]

GUI 는 그리팩 유저 인터페이스이다.

화면 해상도가 달라지면 대응하는 방법 안내

- 화면의 사각형의 모서리 엥커를 파악해 두고 거기서 이미지를 배치한다.

  총 9개의 앵커 (좌상단, 좌중단, 좌하단, 중상단, 중중단, 중하단, 우상단, 우중단, 우하단)

  총 9개의 앵커가 존재하며, 이 엥커를 기준으로 이미지를 배치한다.

- One Unit = 2/화면세로폭

- Root / 스크린의 높이

  - 자동으로 늘어나는 것을 언체크해 둔다. 해상도는 동일한 UI를 갖게된다.

  - 이 Root(불변 사이즈)를 기준으로 One Unit을 정의한다.

- Camera..  카메라 밑에 앵커를 둔다.

   - 앵커 밑에 판넬을 붙이다.

   - 판넬은 이미지, 라벨, 드로콜정보, 매트리얼 사용량 정보글 관리한다. 



[이제 작업해 보자]

어제 작업한 것중에 Camera의 Flare Layer, GUILayer, Audio Listener를 언체크한다. (오디오 리스너는 유일하게 하나이어야 한다.)



목표

- 상단에 점수, 10000M

- 하단에 목숨, 폭탄

 

이렇게 구성해 보자.


menu > NGUI > Create a New UI


창이 뜬다. Layer 선택하고 Add Layer하고  GUI 추가한다. 




다시 Layer를 선택하면 만든 GUI가 나온다. GUI를 선택한다.

그리고 create .. 선택하면 


하이어아키에 UI Root(2D)가 생긴다.

- Camera, 

   - Anchor

     - Panel

       -clipping을 설명. (화면에 보이는 것만 나온다.

이렇게 나온다. 이 각항목을  클릭해서 오른쪽 상세 내용을 보라..





Anchor를 복제해서 4개를 만든다.

그리고 앵커 이름을 각각 바꿔주다. 그리고 내부에 side를 좌우상하로 배치시킨다.

Anchor_TopLeft

Anchor_TopRight


판넬을 선택해서 menu > NGUI > create widget를 선택해서 팝업 화면이 나온다.



Atlas와 Font를 추가하고 Template는 Label로 한다. Add To한다.



아틀라스 만들자. (스프라이트 이미지를 하나의 파일로 재배치해서관리한다.)

- 텍스쳐 폴더에 모든 텍스쳐를 선택하고

- 상단에 menu > NGUI > Altras Make를 한다.

- 팝업에 Replace에 이름을 지정한다. 그러면 create가 나온다. 이 크리에이트 버튼을 누르면 

  아셋에 3개의 파일이 생성되는 것을 볼 수 있다.

  - 이미지를 선택하면 모두 아틀라스를 만들 수 있다. (최대 80개까지 포함시킬 수 있다.)




포트로 아틀라스로 만들 수 있다. BM폰트....


각 포지션으로 만든다.


버튼은  Achor_bottomRight에 추가하는데..

- 엠프티오브젝트를 만들고 이 것에 판넬을 추가한다.

- 이름 Button으로 한다.

- 이 버튼의 상세 내용중 Layer를 GUI로 변경한다.


그리고 이버튼에 menu > NGUI > Attatch Collider 를 추가한다.

그리고 Component > NGUI > interation > 버튼메시지, 버튼사운드, 버튼스켈일을 추가한다.



그리고 버튼을 눌러보면 아래와 같이 붙어 있다.



## 버튼을 누르면 메시지를 출력해 본다.

메인스크립트에 아래 내용을 추가한다.

using UnityEngine;

using System.Collections;


public class MainScript : MonoBehaviour {

public GameObject SkyGamera;

public float CameraRotateSpeed = 0.1f;


// Use this for initialization

void Start () {

}

// Update is called once per frame

void Update () {

//카메라를Y축으로돌리자. 축을 중심으로회전한다.

SkyGamera.transform.RotateAround(Vector3.up, Time.deltaTime * CameraRotateSpeed);

}

// 버튼누르면호출된다.

void Bomb() 

{

Debug.Log("Bomb Button Pressed");

}

}


메인오브젝트를 버튼에 넣어 주소
펑션은 상기 Bomb로 지정해 준다.
아래 그림을 참고 한다.



실행해서 버튼을 눌러보라 그러면 consol에서 메시지가 출력된다.



##이어서 하자.

보톰 라벨을 life로 하자.


모노라이브 > menu > search > go to file > uiLabel

맨밑에 아래 내용 추가후 저장 > 컨버터

void SetText(string s)

{

this.mText = s;

this.hasChanged = true;

}


메인스크립트에 아래 내용 추가

using UnityEngine;

using System.Collections;


public class MainScript : MonoBehaviour {

public GameObject SkyGamera;

public float CameraRotateSpeed = 0.1f;

public GameObject Distance;

public GameObject Life;

public GameObject Point;

private int playerLife  =3;

private int playerPoint = 0;

private float AccelSpeed = 100.0f;

 


// Use this for initialization

void Start () {

}

// Update is called once per frame

void Update () {

//카메라를Y축으로돌리자. 축을 중심으로회전한다.

SkyGamera.transform.RotateAround(Vector3.up, Time.deltaTime * CameraRotateSpeed);

Life.SendMessage("SetText", "X "+playerLife.ToString());

Point.SendMessage("SetText", "Point: "+playerPoint.ToString());

//N0(zero)를 넣어주면소숫점은절삭된다.

Distance.SendMessage("SetText", (Time.time*AccelSpeed).ToString("N0")+" m");

}

//총알이적군이랑충돌되면호출한다.

void Hit(int p)

{

this.playerPoint += p;

}

// 버튼누르면호출된다.

void Bomb() 

{

Debug.Log("Bomb Button Pressed");

}

}



에너미

public class EnemySprite :  SpriteBase {
public float Speed  = 10.0f;
public GameObject expolosion;
// Use this for initialization
void Start () {
gameObject.AddComponent<BoxCollider>();
}
// Update is called once per frame
void Update () {
float moveAmt = Speed * Time.deltaTime;
transform.Translate(Vector3.down * moveAmt);
//많이 떨어지면파괴한다.
if(transform.position.y < -4.0f)
{
InitPosition();
}
}
    void InitPosition()
{
transform.position = new Vector3(Random.Range(-3.0f, 3.0f), 5.0f, 0.0f);
}
//충돌되면 충돌된 것에 Tag를비교한다 Tag가가장빠르다.
void OnTriggerEnter(Collider other) 
{
if(other.tag == "bullet")
{
Instantiate(expolosion, transform.position, transform.rotation);
InitPosition();
//히어아키에서main을찾아온다.
GameObject main = GameObject.Find("Main");
main.SendMessage("Hit", 50);
}
}
}

메인에서 추가된 변수에 파넬에 각각 라벨을 붙여 준다.


assets store로 가서 로그인 하고 itween으로 찾는다.




hotween을 찾아 다운로드 설치한다.



붙인 것 샘플을 돌려 보자




## 클리어신을 만들어 보자

1. new scene , 이름은 win

2. menu > NGUI > create new ui > layer는 GUI로 변경한다.




   크리에이트 한다.

3. 위제만든다.

    아틀라스는 판타지 아틀라스붙인다.

4. 패널에 라벨3개, 스프라이트 하나 만든다.    

5. 라벨을 적당히 채운다.

6. 엠프티 오브젝트 만들고

-버튼

- 레이어는 GUI

- 그리고 판넬로 이동

- 위치 제조정

7. 버튼에 라벨과 스크립트 넣는다.

8. 스크립트 만든다.

using UnityEngine;

using System.Collections;


public class ResultUI : MonoBehaviour {

public Transform TitleLeft;

public Transform TitleRight;

public Transform Button;

IEnumerable Start() 

{

Debug.Log("Start Time "+Time.time);

//yield는양보로 기다렸다가 해당시간후다음라인이 동작한다.

yield return new WaitForSeconds(1.0f);

Debug.Log("Next Time "+Time.time);

}

}


아래 것은 양보를 한다. 인공지능에 사용할 수 있고, 그러나 초는 정확하지 않다.

yield 




UIRootdp 스크립트를 추가한다.

그리고 각각 연결한다.



다시 스크립트에 

using Holoville.HOTween; 추가한다.


스타트함수에 다시 코딩


라벨스테이션 포지션 -1050, 40, 0

라벨클리어 푖션 1050, 40, 0

으로 한다. 


그래서 1000을 빼주고 더해주면 화면들로 들어 온다.

실행해 본다.



상세에서

버튼도 스케일도 100, 100, 1로 변경해 본다.

버튼은 언체크 한다. 









using Holoville.HOTween;
using UnityEngine;
using System.Collections;

public class ResultUI : MonoBehaviour {
public Transform TitleLeft;
public Transform TitleRight;
public Transform Button;
IEnumerable Start() 
{
Debug.Log("Start Time "+Time.time);
yield return new WaitForSeconds(1.0f);
Debug.Log("Next Time "+Time.time);
HOTween.Init(true, true, true);
//stage
HOTween.To(TitleLeft, 0.5f, "localPosition", new Vector3(1000, 0, 0),true);
//clear
TweenParms tweenParams = new TweenParms().Prop("localPosition", new Vector3(-1000, 0, 0), true).Ease(EaseType.Linear).Loops(-1,LoopType.Yoyo);
HOTween.To(TitleRight, 0.5f, tweenParams);
yield return new WaitForSeconds(1.0f);
//button enable
Button.gameObject.SetActive(true);
// on Child widget
for(int i=0; i<Button.transform.childCount;i++)
{
Button.transform.GetChild(i).gameObject.SetActive(true);
}
//button animation
Sequence sequence = new Sequence(new SequenceParms().Loops(1, LoopType.Yoyo));
sequence.Append(HOTween.To(Button, 0.5f, new TweenParms().Prop("localScale", new Vector3(-110, -110, 0),true)));
sequence.Append(HOTween.To(Button, 0.5f, new TweenParms().Prop("localScale", new Vector3(20, 20, 0), true)));
sequence.Append(HOTween.To(Button, 0.5f, new TweenParms().Prop("localScale", new Vector3(-9, -9, 0), true)));
sequence.Append(HOTween.To(Button, 0.5f, new TweenParms().Prop("localEulerAngles", new Vector3(720,0,0),true)));
sequence.Play();
}
}





http://www.unity3dacademy.com/index.php?mid=EduInfo


실전 Unity3D Engine Programing 과정 4일차 (2013.07.25 (목)) 

남들이 알아 주지 않더라도 나의 일을 묵묵히 하다 보면
결국 남 좋은 일만 하게 된다.



## [폴리곤 줄이는 방법을 배워보자.]  <== 국내에 유일한 팁으로 스크립트를 모두 사용해라


메뉴 > 윈도우 >  Assets Store 계정을 만들어라.

다음 클라우드에서 오늘자 리소스를 받아라.

skybox_objects.unitypackage

shooting2D_resources

skybox_objects

NGUI <== 상용 플러그인다. 출처를 밝히지 말라. 550$ 상당의 가치이다.


뉴프로젝트한다. : 2DShooting

Assets창에서 패키지를 로딩한다: shooting2D_resources

Consol창에서 Error pause를 켜 놓아야 한다. <== 일반적으로 켜 놓고 사용해라. 

콘솔창이 없으면 Menu > Window > Consol을 선택해서 아무곳에 붙이면 된다.


아셋 > 텍스쳐를 봐라.

- 비행기

- 해골

- 미사일

- plane 

있고 이것들의 size가 나온다. 유니티는 스스로 2승수로 바꾼다. 

이것을 변경하는 것을 갔다 사용한다.

인터넷 > unity3d.com에 접속

http://wiki.unity3d.com/index.php/Main_Page 

http://wiki.unity3d.com/index.php/TextureImportSettings

Adds under the menu Custom→Texture a way to change for multiple selected textures the import settings in one step. Idea was to have the same choices for multiple texture files as you would have if you open the import settings of a single texture. Currently the most often used import settings are editable: Texture Format (same amount and order as in Unity), enable/disable MipMap and changing the maximum texture size.

Usage

You must place the script in a folder named Editor in your project's Assets folder for it to work properly.

Select some textures in the project window and select from the Custom→Texture menu the modification you want to apply to the selected textures.

Screenshot

Textureimportsettings.jpg

내용 보면 두개의 스크립트가 있는데 첫번째것이 4버전이고 두번째것이 3버전이다. 




ChangeTextureImportSettings 이 파일명으로 스크립트를 만들어 사용한다.

내용은 아래 것으로 채운다. (상기 인터넷 사이트 것)

using UnityEngine;
using UnityEditor;
 
// /////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Batch Texture import settings modifier.
//
// Modifies all selected textures in the project window and applies the requested modification on the 
// textures. Idea was to have the same choices for multiple files as you would have if you open the 
// import settings of a single texture. Put this into Assets/Editor and once compiled by Unity you find
// the new functionality in Custom -> Texture. Enjoy! :-)
// 
// Based on the great work of benblo in this thread: 
// http://forum.unity3d.com/viewtopic.php?t=16079&start=0&postdays=0&postorder=asc&highlight=textureimporter
// 
// Developed by Martin Schultz, Decane in August 2009
// e-mail: ms@decane.net
//
// /////////////////////////////////////////////////////////////////////////////////////////////////////////
public class ChangeTextureImportSettings : ScriptableObject {
 
	[MenuItem ("Custom/Texture/Change Texture Format/Auto")]
    static void ChangeTextureFormat_Auto() { 
		SelectedChangeTextureFormatSettings(TextureImporterFormat.Automatic);
	}
 
	[MenuItem ("Custom/Texture/Change Texture Format/RGB Compressed DXT1")]
    static void ChangeTextureFormat_RGB_DXT1() { 
		SelectedChangeTextureFormatSettings(TextureImporterFormat.DXT1);
	}
 
	[MenuItem ("Custom/Texture/Change Texture Format/RGB Compressed DXT5")]
    static void ChangeTextureFormat_RGB_DXT5() { 
		SelectedChangeTextureFormatSettings(TextureImporterFormat.DXT5);
	}
 
	[MenuItem ("Custom/Texture/Change Texture Format/RGB 16 bit")]
    static void ChangeTextureFormat_RGB_16bit() { 
		SelectedChangeTextureFormatSettings(TextureImporterFormat.RGB16);
	}
 
	[MenuItem ("Custom/Texture/Change Texture Format/RGB 24 bit")]
    static void ChangeTextureFormat_RGB_24bit() { 
		SelectedChangeTextureFormatSettings(TextureImporterFormat.RGB24);
	}
 
	[MenuItem ("Custom/Texture/Change Texture Format/Alpha 8 bit")]
    static void ChangeTextureFormat_Alpha_8bit() { 
		SelectedChangeTextureFormatSettings(TextureImporterFormat.Alpha8);
	}
 
	[MenuItem ("Custom/Texture/Change Texture Format/RGBA 16 bit")]
    static void ChangeTextureFormat_RGBA_16bit() { 
		SelectedChangeTextureFormatSettings(TextureImporterFormat.ARGB16);
	}
 
	[MenuItem ("Custom/Texture/Change Texture Format/RGBA 32 bit")]
    static void ChangeTextureFormat_RGBA_32bit() { 
		SelectedChangeTextureFormatSettings(TextureImporterFormat.ARGB32);
	}
 
	// ----------------------------------------------------------------------------
 
	[MenuItem ("Custom/Texture/Change Texture Size/Change Max Texture Size/32")]
    static void ChangeTextureSize_32() { 
		SelectedChangeMaxTextureSize(32);
	}
 
	[MenuItem ("Custom/Texture/Change Texture Size/Change Max Texture Size/64")]
    static void ChangeTextureSize_64() { 
		SelectedChangeMaxTextureSize(64);
	}
 
	[MenuItem ("Custom/Texture/Change Texture Size/Change Max Texture Size/128")]
    static void ChangeTextureSize_128() { 
		SelectedChangeMaxTextureSize(128);
	}
 
	[MenuItem ("Custom/Texture/Change Texture Size/Change Max Texture Size/256")]
    static void ChangeTextureSize_256() { 
		SelectedChangeMaxTextureSize(256);
	}
 
	[MenuItem ("Custom/Texture/Change Texture Size/Change Max Texture Size/512")]
    static void ChangeTextureSize_512() { 
		SelectedChangeMaxTextureSize(512);
	}
 
	[MenuItem ("Custom/Texture/Change Texture Size/Change Max Texture Size/1024")]
    static void ChangeTextureSize_1024() { 
		SelectedChangeMaxTextureSize(1024);
	}
 
	[MenuItem ("Custom/Texture/Change Texture Size/Change Max Texture Size/2048")]
    static void ChangeTextureSize_2048() { 
		SelectedChangeMaxTextureSize(2048);
	}
 
	// ----------------------------------------------------------------------------
 
	[MenuItem ("Custom/Texture/Change MipMap/Enable MipMap")]
    static void ChangeMipMap_On() { 
		SelectedChangeMimMap(true);
	}
 
	[MenuItem ("Custom/Texture/Change MipMap/Disable MipMap")]
    static void ChangeMipMap_Off() { 
		SelectedChangeMimMap(false);
	}
 
	// ----------------------------------------------------------------------------
 
	static void SelectedChangeMimMap(bool enabled) { 
 
		Object[] textures = GetSelectedTextures(); 
		Selection.objects = new Object[0];
		foreach (Texture2D texture in textures)  {
			string path = AssetDatabase.GetAssetPath(texture); 
			TextureImporter textureImporter = AssetImporter.GetAtPath(path) as TextureImporter; 
			textureImporter.mipmapEnabled = enabled;	
			AssetDatabase.ImportAsset(path); 
		}
	}
 
	static void SelectedChangeMaxTextureSize(int size) { 
 
		Object[] textures = GetSelectedTextures(); 
		Selection.objects = new Object[0];
		foreach (Texture2D texture in textures)  {
			string path = AssetDatabase.GetAssetPath(texture); 
			TextureImporter textureImporter = AssetImporter.GetAtPath(path) as TextureImporter; 
			textureImporter.maxTextureSize = size;	
			AssetDatabase.ImportAsset(path); 
		}
	}
 
	static void SelectedChangeTextureFormatSettings(TextureImporterFormat newFormat) { 
 
		Object[] textures = GetSelectedTextures(); 
		Selection.objects = new Object[0];
		foreach (Texture2D texture in textures)  {
			string path = AssetDatabase.GetAssetPath(texture); 
			//Debug.Log("path: " + path);
			TextureImporter textureImporter = AssetImporter.GetAtPath(path) as TextureImporter; 
			textureImporter.textureFormat = newFormat;	
			AssetDatabase.ImportAsset(path); 
		}
	}
 
	static Object[] GetSelectedTextures() 
	{ 
		return Selection.GetFiltered(typeof(Texture2D), SelectionMode.DeepAssets); 
	}
}



 menu > assets > refresh 하면 상당 menu에 custom이란 항목이 나오면 성공



텍스쳐를 선택해서 사이즈를 변경한다.

menu > custom > texture > 



성공되면 해당 텍스쳐선택히 우측 하단에... NOPT라고 나오면 된다.



현재까지 한 것은  Game 신으로 저장한다.



[폴리곤을 바꿔 보자]

카메라는 이차원 카메라로 바꾼다.

포지션 0, 0, 0

프로적션은 오도고날 (2차원)

사이즈 3.5

클립핑 -2, 2

라이트 추가



스크립트추가 SpriteBase

using UnityEngine;

using System.Collections;


public class SpriteBase : MonoBehaviour {

 

public Material mat;

void Awake()

{

gameObject.AddComponent("MeshFilter");

gameObject.AddComponent("MeshRenderer");

Mesh mesh = GetComponent<MeshFilter>().mesh;

mesh.Clear(); //remove

// 새로만든자 - xy 좌표에서 사각형좌표를찍었다

mesh.vertices = new Vector3[]

{new Vector3(0,0,0), new Vector3(0,1,0),new Vector3(1,1,0), new Vector3(1,0,0)};

// 밝아지는반경

mesh.uv = new Vector2[] {new Vector2(0,0), new Vector2(0,1), new Vector2(1,1), new Vector2(1,0)}; 

//사각형을두개의삼각형으로붙인다

mesh.triangles = new int[]{0,1,2,0,2,3};


//노말을생성한다 빗의반사색상

mesh.RecalculateNormals();


if(mat != null)

{

gameObject.renderer.material = mat;

}

}

}


위에것으로 하면 컴파일 안된다.
아래처럼 주석에 .을 붙여야 하다.
using UnityEngine;
using System.Collections;

public class SpriteBase : MonoBehaviour {
 
public Material mat;
void Awake()
{
gameObject.AddComponent("MeshFilter");
gameObject.AddComponent("MeshRenderer");
Mesh mesh = GetComponent<MeshFilter>().mesh;
mesh.Clear(); //remove
// 새로만든자 - xy 좌표에서 사각형좌표를찍었다.
mesh.vertices = new Vector3[]
{new Vector3(0,0,0), new Vector3(0,1,0),new Vector3(1,1,0), new Vector3(1,0,0)};
// 밝아지는반경.
mesh.uv = new Vector2[] {new Vector2(0,0), new Vector2(0,1), new Vector2(1,1), new Vector2(1,0)}; 
//사각형을두개의삼각형으로붙인다.
mesh.triangles = new int[]{0,1,2,0,2,3};
//노말을생성한다 빗의반사색상.
mesh.RecalculateNormals();
if(mat != null)
{
// 대소문자구분 소문자game은자체.
gameObject.renderer.material = mat;
}
}
}






빛과 텍스쳐가 각이 맞다면 색상이 달라진다. 이것을 노말이라고 한다. 

//노말을생성한다 빗의반사색상

mesh.RecalculateNormals();



아셋에서 Material 폴더를 추가한다.

<== 폴더를 구분하는 것은 매우 중요하다. 아셋에 들어갈게 많아서 구분하는게 좋다.

메트리얼 추가한다.

메트리어에 비행기 텍스쳐을 잡소 오른쪽에 붙이다.





엠프티오브젝트를 추가하고 포지션은 0,0,0으로 한다.

아까만든 SpriteBase 스크립트를 엠프티오브젝트에 추가한다.

그리고 메터리어을 스트립트의 mat에 붙인다.


그리고 실행해 보면 4각형의 비행기가 나온다.

실제 비행기 이미지 보다 작게 나온다.



<== 이 스크립트를 사용하면 폴리곤을 수를 업청 줄일 수 있다. 이것이 노하우다 이 스크립트를 사용해라


tris수가 2로 매우 작게 나온다 . 

이렇게 하지 않았을 경우는 200 까지 나온 것이다.



이렇듯 실제 사이즈가 다르면 안되니 이것을 맞추도록 하자.

사이즈 수정하자..

스크립트 아래 내용 추가하자.

using UnityEngine;

using System.Collections;


public class SpriteBase : MonoBehaviour {

 

public Material mat;

void Awake()

{

gameObject.AddComponent("MeshFilter");

gameObject.AddComponent("MeshRenderer");

Mesh mesh = GetComponent<MeshFilter>().mesh;

mesh.Clear(); //remove

// 새로만든자 - xy 좌표에서 사각형좌표를찍었다.

mesh.vertices = new Vector3[]

{new Vector3(0,0,0), new Vector3(0,1,0),new Vector3(1,1,0), new Vector3(1,0,0)};

// 밝아지는반경.

mesh.uv = new Vector2[] {new Vector2(0,0), new Vector2(0,1), new Vector2(1,1), new Vector2(1,0)}; 

//사각형을두개의삼각형으로붙인다.

mesh.triangles = new int[]{0,1,2,0,2,3};

//노말을생성한다 빗의반사색상.

mesh.RecalculateNormals();

if(mat != null)

{

// 대소문자구분 소문자game은자체.

gameObject.renderer.material = mat;

}


//사이즈변경하는것호출한다.

MakePerfectSize();

}

void MakePerfectSize() 

{

Texture tex = mat.mainTexture;

float one_unit = 2.0f / Screen.height;

float x = one_unit * tex.width;

float y = one_unit * tex.height;

Vector3 newScale = new Vector3(x, y, one_unit);

transform.localScale = newScale;

}

}


<== 여기까지가 이제 2D의 시작이다.

이제부터 텍스쳐 다르게 넣으면 다르게 나온다.

계속 이 스크립트는 복제해서 사용하자.



## [드라곤 플라이트 게임 만든자]

새롭게 시작하다. 기존 게임오브젝트는 지운다.

PlayerSprite 스크립트 새로 추가한다.

상속은 1단계만 된다. SpriteBase을 상속한다.


using UnityEngine;

using System.Collections;


public class PlayerSprite : SpriteBase {

public float Speed = 10.0f;

private float LastShootTime;

public float ShootDelay = 0.2f;

// Use this for initialization

void Start () {

LastShootTime = Time.time;

}

// Update is called once per frame

void Update () {

float moveAmt = Input.GetAxis("Horizontal") * Speed  * Time.deltaTime;

transform.Translate(Vector3.right * moveAmt);

}

}



엠프티오브젝트추가
이름 player
포지션 0, -3, 0
스크립트 추가
메트리얼 추가



비행기의 기준점이 없어 불편하다. 디버그 포인트를 추가하자.

모눈종이 모양의 선을 그려보다.


PlayerBase에 추가하자.

using UnityEngine;

using System.Collections;


public class SpriteBase : MonoBehaviour {

 

public Material mat;

void Awake()

{

gameObject.AddComponent("MeshFilter");

gameObject.AddComponent("MeshRenderer");

Mesh mesh = GetComponent<MeshFilter>().mesh;

mesh.Clear(); //remove

// 새로만든자 - xy 좌표에서 사각형좌표를찍었다.

mesh.vertices = new Vector3[]

{new Vector3(0,0,0), new Vector3(0,1,0),new Vector3(1,1,0), new Vector3(1,0,0)};

// 밝아지는반경.

mesh.uv = new Vector2[] {new Vector2(0,0), new Vector2(0,1), new Vector2(1,1), new Vector2(1,0)}; 

//사각형을두개의삼각형으로붙인다.

mesh.triangles = new int[]{0,1,2,0,2,3};

//노말을생성한다 빗의반사색상.

mesh.RecalculateNormals();

if(mat != null)

{

// 대소문자구분 소문자game은자체.

gameObject.renderer.material = mat;

}


//사이즈변경하는것호출한다.

MakePerfectSize();

}

void MakePerfectSize() 

{

Texture tex = mat.mainTexture;

float one_unit = 2.0f / Screen.height;

float x = one_unit * tex.width;

float y = one_unit * tex.height;

Vector3 newScale = new Vector3(x, y, one_unit);

transform.localScale = newScale;

}

void OnDrawGizmosSelected()

{

Gizmos.color = Color.white;

Gizmos.DrawWireSphere(transform.position, 1.0f);

}

void OnDrawGizmos()

{

Gizmos.color = Color.red;

Gizmos.DrawWireCube(transform.position, Vector3.one);

}

}


신을 보라..


이렇게 위치가 빨갔게 나오고 스프라이트가 선택되면 힌색이 나온다.

스프라이트로만 처리하면 신화면에 나오지 않으니 디버깅용으로 사용할 수 있다.

실제 화면에서는 나오지 않으니 디버깅용으로 매우 좋다.


## [총탄을 만들자]

메트리얼 만들자.  그리고 미사일 텍스쳐를 붙인다.


그리고 BulletSprite 스프라이트 만든다.

리니즈바디 추가 그라비티 를 언체크하는 것을 스크립트로 제어한다.


using UnityEngine;

using System.Collections;


public class BulletSprite : SpriteBase {

public float Speed = 15.0f;

// Use this for initialization

void Start () {

//리니즈바디 추가 그라비티 를 언체크하는 것을 스크립트로 제어한다.

gameObject.AddComponent<Rigidbody>();

gameObject.rigidbody.useGravity = false;

gameObject.AddComponent<BoxCollider>();

gameObject.collider.isTrigger = true;

}

// Update is called once per frame

void Update () {

float moveAmt = Speed * Time.deltaTime;

transform.Translate(Vector3.up *moveAmt);

if(transform.position.y > 10.0f)

{

Destroy(gameObject);

}

}

}


아셋에 Prefab 폴더 만들고  BulletPrefab을  만든다.


엠프티오브젝트 만들고

 블랫스프라이트 추가

 블랫메트리얼 추가

 Tag  bullet으로 선언




그리고 BulletPrefab에 만든 엠프티오브젝트를 추가한다. 지운다.

(정상적으로 추가되면 아래 BulletPrefab 처럼 아이콘이 유색으로 변한다. 추가되지 않았을 때에는 회색)






## [ 플레이어의 총알이 자동발사하는 것을 만들자.]


1. 플레이 경과 시간파악

2. 지금 시간이 마지막 총을 쏜 시간  지연되어으면 ......

//Time.time은play 버튼이누른이후계속재시는시간이다 42일max까지이다.

if(Time.time > LastShootTime + ShootDelay)

{

LastShootTime = Time.time;

Instantiate(bullet, transform.position, transform.rotation);

}

<= 이것 많이 사용한다. 


Player  오브젝트에 보면 bullet이 추가되어 있다. 여기에 BulletPrefab을 추가 한다. 



Player오브젝트를 열어보면 리니지바디 중력은 언체크되어 있고 박스콜라이더는 트리거체크되어 화면에 녹색네모가 보이게 된다.

이렇게 코드로 추가 가능하다. 이렇게 해야 충돌 체크가 가능해진다.


[팁] 스크립트로 오브젝트를 add하면 메모리 누스를 줄일 수 있다.

[팁] 부모 함수를 자식이 호출 할 수 있다. base.. 그러나 호출 순서는 장담할 수 없다고 합니다. 스크립트라서... 



##[적군만들자]

EnemySprite 스프라이트 만들자

using UnityEngine;

using System.Collections;


public class EnemySprite : MonoBehaviour {

public float Speed  = 10.0f;

public GameObject expolosion;

// Use this for initialization

void Start () {

gameObject.AddComponent<BoxCollider>();

}

// Update is called once per frame

void Update () {

float moveAmt = Speed * Time.deltaTime;

transform.Translate(Vector3.down * moveAmt);

//많이 떨어지면파괴한다.

if(transform.position.y < -4.0f)

{

InitPosition();

}

}

    void InitPosition()

{

transform.position = new Vector3(Random.Range(-3.0f, 3.0f), 5,0f, 0.0f);

}

}


신 저장


에너미 오브젝트를 만들고

스크립트추가

이팩트를 추가


이팩트는 추가한 다이나믹을 추가한다.




##[배경을 흐르는 것을 만들자]

스카이박스 패키지를 추가한다.  <== 출처는 비밀

sky_box_ring을선택해서 히어아키창에 둔다.  (아셋 > 오브젝트 > 스카이 )

이것을 Y값을 1000으로 둔다. 노출되지 않게

배경을 줄이자...



카메라를 추가한다.

포지션 0, 1670, 0

필더뷰 : 35

클립 0.3, 5000

Depth : -2 (낮은 값이 먼저 찍는다.)


엠프트오브젝트 Main

스크립트 MainScript 

추가..

using UnityEngine;

using System.Collections;


public class MainScript : MonoBehaviour {

public GameObject SkyGamera;

public float CameraRotateSpeed = 0.1f;


// Use this for initialization

void Start () {

}

// Update is called once per frame

void Update () {

//카메라를Y축으로돌리자. 축을 중심으로회전한다.

SkyGamera.transform.RotateAround(Vector3.up, Time.deltaTime * CameraRotateSpeed);

}

}



메인카메라에서 Clear Flags를 Depth only로 한다. 
= 뒷배경 그리도 덥어 쒸운다는 것.



아까만든 Main 오브제트에 스크립트 추가한다.

스크립트에 카메라는 하이어라키의 카메라를 붙인다.



카메라의 Rotation 0, 0, 2780 으로 변경한다.

그리고 Field of View 25로 한다





배경 이미지를 바꿔보자.



가로 형태는 카메라 축을 변경하면 충분히 처리한다.


내일은 GUI를 붙인다..





실전 Unity3D Engine Programing 과정 3일차 (2013.07.24 (수)) 


어제 만든 것을 3D로 변경 한다.


asset에 새로운 리소스 임포트한다.





1.신만든다. 이름은 NewGame으로 한다.

2. 메인 카메라 값을 변경한다.




3. 라이트 추가한다.

4. Assets _Object에 있는 SpaceCraftBox 하이어라키에 추가한다

- 에니메이션은 리무브 컴포넌트한다.

- PlayControl 스크립트를 추가한다.

- PlayControl 스크립트 드래그해서 오른쪽에 추가한다.



PlayControl스크립트 수정

void Update () {

float moveAmt = Input.GetAxis ("Horizontal") * Speed * Time.deltaTime;

float moveAmt2 = Input.GetAxis("Vertical") * Speed * Time.deltaTime;

Vector3 moveVector = Vector3.right * moveAmt + Vector3.up * moveAmt2;

transform.Translate(moveVector);

if(Input.GetKeyDown(KeyCode.Space)){

audio.Play();

Instantiate(bullet, transform.position, transform.rotation);

}

}


5. Assets _Object에 있는 missileFBX를 하이어라키에 추가한다.

- 에니메이션 제거

-   scale 은 5, 5, 2변경한다.

- 충돌을 위해서 리디지바디를 추가한다.

   그라비티  언체크

- 충돌시 캡슐 콜리더를 추가한다. Capsul Collider

  -  센터는 0 0 0

  - 트리거 체크

   - 각도.. 0.06 높이 0.42 축 z-Axis

-  BulletScript를 추가한다.

- 스크립트를 다음과 같이 수정

using UnityEngine;

using System.Collections;


public class BulletControl : MonoBehaviour {


public float Speed = 20f;

// Update is called once per frame

void Update () {

float moveAmt = Speed * Time.deltaTime;

transform.Translate(Vector3.forward * moveAmt);

if(transform.position.z > 35.0f){

Destroy(gameObject);

}

}

- 파티클을 추가하자.

   Standard Assets, Particles , small flames 하이어키에 추가하자.

   - Particle animator의 force값 수정 0.0.-5

   - Ellipsoid Particle Emitter Local velocity, Rnd Velocity를 모두 0으로 준다.

                                        사이즈를 모두 줄인다 1/2


미사일를 비행체 자식으로 바꿔준다.

하이얼리키에서 옮긴다.



그리고 불을 미사일 꽁무니에 옮겨준다. 신화면에서





Prefabs에서 MeshBulletfabs를 만들고 미사일FBX을 끌어다 놓는다. 그리고 미사일 FBX는 제거한다.




[적군을 만들자]

새로 asset에 있는 오브젝트에서 sunRingFBX  하이어라키에 추가한다.

- 에니메이션 리무브

- 메슈콜라이더삭제

- 메뉴 콤포넌트 피직스 shhere collider추가 (크기가 비스하게 들어간다.)

- EnemyControl 스크립트 드래그 드랍으로 추가

   - 

- 오디오 소스 추가. way on awake 언체크, 히트오디오 추가



- EnemyControl 수정

void Update () {

float moveAmt = Speed * Time.deltaTime;

transform.Translate(Vector3.forward * moveAmt);

if(transform.position.z < -7.0f)

{

InitPosition();

}

}

void InitPosition()

{

transform.position = new Vector3(

Random.Range(-10.0f,10.0f),

   Random.Range(-10.0f, 10.0f),

35.0f);

}



- sunRingFBX에서 포지션 0, 0, 35, Rotation 0, 180, 0 수정

sunRingFBX 추가로 2개 복재한다.  x 값을 수정해서 나란히 한다.


- prefabs의 MeshgBulletPrefab Tag를 bullet으로 수정한다.




[배경을 넣자]

assets -> import package -> skybox를 추가한다.


 내용을 놓쳤다...  ㅜ ㅜ





#스크립트에 대해서 배우자. Scripting Overview 파일을 연다.

- www.unity3d.com > community > Document / Go to document  > 우측의 script탭을 누른다. ( Wiki 열어보면 스크립트가 많이 있다. )

  (유니티 IDE에 메뉴에 있는 script help는 옛날 것이다.)

  상기에 스크립트 최신 정보를 볼 수 있다.

  독일 쪽에 스크립트 최신 정보를 찾아 볼 수있고, 구글에서 검색해 본다.

  에러 발생시 서치해 본다.

  - 서치에서 monobehaviour를 친다.  



  


MonoBehaviour

Description

MonoBehaviour is the base class every script derives from.

Using Javascript every script automatically derives from MonoBehaviour. When using C# or Boo you have to explicitly derive from MonoBehaviour.

Note: The checkbox for disabling a MonoBehavior (on the editor) will only prevent Start(), Awake(), Update(), FixedUpdate(), and OnGUI() from executing. If none of these functions are present, the checkbox is not displayed.

See Also: The chapter on scripting in the manual.
Variables

useGUILayout

Disabling this lets you skip the GUI layout phase.
Functions

CancelInvoke

Cancels all Invoke calls on this MonoBehaviour.

Invoke

Invokes the method methodName in time seconds.

InvokeRepeating

Invokes the method methodName in time seconds.

IsInvoking

Is any invoke on methodName pending?

StartCoroutine

Starts a coroutine.

StopAllCoroutines

Stops all coroutines running on this behaviour.

StopCoroutine

Stops all coroutines named methodName running on this behaviour.
Static Functions
printLogs message to the Unity Console. This function is identical to Debug.Log.
Messages

Awake

Awake is called when the script instance is being loaded.

FixedUpdate

This function is called every fixed framerate frame, if the MonoBehaviour is enabled.

LateUpdate

LateUpdate is called every frame, if the Behaviour is enabled.

OnAnimatorIK

Callback for setting up animation IK (inverse kinematics).

OnAnimatorMove

Callback for processing animation movements for modifying root motion.

OnApplicationFocus

Sent to all game objects when the player gets or loses focus.

OnApplicationPause

Sent to all game objects when the player pauses.

OnApplicationQuit

Sent to all game objects before the application is quit.

OnAudioFilterRead

If OnAudioFilterRead is implemented, Unity will insert a custom filter into the audio DSP chain.

OnBecameInvisible

OnBecameInvisible is called when the renderer is no longer visible by any camera.

OnBecameVisible

OnBecameVisible is called when the renderer became visible by any camera.

OnCollisionEnter

OnCollisionEnter is called when this collider/rigidbody has begun touching another rigidbody/collider.

OnCollisionExit

OnCollisionExit is called when this collider/rigidbody has stopped touching another rigidbody/collider.

OnCollisionStay

OnCollisionStay is called once per frame for every collider/rigidbody that is touching rigidbody/collider.

OnConnectedToServer

Called on the client when you have successfully connected to a server.

OnControllerColliderHit

OnControllerColliderHit is called when the controller hits a collider while performing a Move.

OnDestroy

This function is called when the MonoBehaviour will be destroyed.

OnDisable

This function is called when the behaviour becomes disabled () or inactive.

OnDisconnectedFromServer

Called on the client when the connection was lost or you disconnected from the server.

OnDrawGizmos

Implement this OnDrawGizmos if you want to draw gizmos that are also pickable and always drawn.

OnDrawGizmosSelected

Implement this OnDrawGizmosSelected if you want to draw gizmos only if the object is selected.

OnEnable

This function is called when the object becomes enabled and active.

OnFailedToConnect

Called on the client when a connection attempt fails for some reason.

OnFailedToConnectToMasterServer

Called on clients or servers when there is a problem connecting to the MasterServer.

OnGUI

OnGUI is called for rendering and handling GUI events.

OnJointBreak

Called when a joint attached to the same game object broke.

OnLevelWasLoaded

This function is called after a new level was loaded.

OnMasterServerEvent

Called on clients or servers when reporting events from the MasterServer.

OnMouseDown

OnMouseDown is called when the user has pressed the mouse button while over the GUIElement or Collider.

OnMouseDrag

OnMouseDrag is called when the user has clicked on a GUIElement or Collider and is still holding down the mouse.

OnMouseEnter

OnMouseEnter is called when the mouse entered the GUIElement or Collider.

OnMouseExit

OnMouseExit is called when the mouse is not any longer over the GUIElement or Collider.

OnMouseOver

OnMouseOver is called every frame while the mouse is over the GUIElement or Collider.

OnMouseUp

OnMouseUp is called when the user has released the mouse button.

OnMouseUpAsButton

OnMouseUpAsButton is only called when the mouse is released over the same GUIElement or Collider as it was pressed.

OnNetworkInstantiate

Called on objects which have been network instantiated with Network.Instantiate.

OnParticleCollision

OnParticleCollision is called when a particle hits a collider.

OnPlayerConnected

Called on the server whenever a new player has successfully connected.

OnPlayerDisconnected

Called on the server whenever a player disconnected from the server.

OnPostRender

OnPostRender is called after a camera finished rendering the scene.

OnPreCull

OnPreCull is called before a camera culls the scene.

OnPreRender

OnPreRender is called before a camera starts rendering the scene.

OnRenderImage

OnRenderImage is called after all rendering is complete to render image.

OnRenderObject

OnRenderObject is called after camera has rendered the scene.

OnSerializeNetworkView

Used to customize synchronization of variables in a script watched by a network view.

OnServerInitialized

Called on the server whenever a Network.InitializeServer was invoked and has completed.

OnTriggerEnter

OnTriggerEnter is called when the Collider other enters the trigger.

OnTriggerExit

OnTriggerExit is called when the Collider other has stopped touching the trigger.

OnTriggerStay

OnTriggerStay is called once per frame for every Collider other that is touching the trigger.

OnValidate

This function is called when the script is loaded or a value is changed in the inspector (Called in the editor only).

OnWillRenderObject

OnWillRenderObject is called once for each camera if the object is visible.

Reset

Reset to default values.

Start

Start is called just before any of the Update methods is called the first time.

Update

Update is called every frame, if the MonoBehaviour is enabled.
Inherited members
Variables

enabled

Enabled Behaviours are Updated, disabled Behaviours are not.

animation

The Animation attached to this GameObject (null if there is none attached).

audio

The AudioSource attached to this GameObject (null if there is none attached).

camera

The Camera attached to this GameObject (null if there is none attached).

collider

The Collider attached to this GameObject (null if there is none attached).

constantForce

The ConstantForce attached to this GameObject (null if there is none attached).

gameObject

The game object this component is attached to. A component is always attached to a game object.

guiText

The GUIText attached to this GameObject (null if there is none attached).

guiTexture

The GUITexture attached to this GameObject (Read Only). (null if there is none attached).

hingeJoint

The HingeJoint attached to this GameObject (null if there is none attached).

light

The Light attached to this GameObject (null if there is none attached).

networkView

The NetworkView attached to this GameObject (Read Only). (null if there is none attached).

particleEmitter

The ParticleEmitter attached to this GameObject (null if there is none attached).

particleSystem

The ParticleSystem attached to this GameObject (null if there is none attached).

renderer

The Renderer attached to this GameObject (null if there is none attached).

rigidbody

The Rigidbody attached to this GameObject (null if there is none attached).

tag

The tag of this game object.

transform

The Transform attached to this GameObject (null if there is none attached).

hideFlags

Should the object be hidden, saved with the scene or modifiable by the user?

name

The name of the object.
Functions

BroadcastMessage

Calls the method named methodName on every MonoBehaviour in this game object or any of its children.

CompareTag

Is this game object tagged with tag?

GetComponent

Returns the component of Type type if the game object has one attached, null if it doesn't.

GetComponentInChildren

Returns the component of Type type in the GameObject or any of its children using depth first search.

GetComponents

Returns all components of Type type in the GameObject.

GetComponentsInChildren

Returns all components of Type type in the GameObject or any of its children.

SendMessage

Calls the method named methodName on every MonoBehaviour in this game object.

SendMessageUpwards

Calls the method named methodName on every MonoBehaviour in this game object and on every ancestor of the behaviour.

GetInstanceID

Returns the instance id of the object.

ToString

Returns the name of the game object.
Static Functions

Destroy

Removes a gameobject, component or asset.

DestroyImmediate

Destroys the object obj immediately. You are strongly recommended to use Destroy instead.

DontDestroyOnLoad

Makes the object target not be destroyed automatically when loading a new scene.

FindObjectOfType

Returns the first active loaded object of Type type.

FindObjectsOfType

Returns a list of all active loaded objects of Type type.

Instantiate

Clones the object original and returns the clone.
Operators

bool

Does the object exist?

operator !=

Compares if two objects refer to a different object.

operator ==

Compares if two objects refer to the same.


이들 내용중에 on으로 시작하는 것은 꼭 달달 외워야 한다.

transform와 Translate를 찾아보라..


Scripting Overview 파일은 아래 내용을 붙였다..


많이 물어보는 내용으로 설명해 본다.


##. GameObject를 찾는 방법

Find  (찾는 것 최상에 것이 찾아진다.) 빠름2

FindWithTag    빠름 1

FindGameObjectWithTag (전부 다찾는것) 빠름3

FindObjectOfType 빠름 4  <== 쓰지 마라 느리다.. 

FindObjhectsOfType 빠름 5 <== 절대 코드에 적용하지 마라 매우 느리다.


GameObject로  찾아보라..



빠르고 느린지를 확인하는 방법...

Play해보고 아래 것을 열어 보면  overview을 알 수 있다. 

메뉴 > 윈도우 > 프로파일러

CPU, Render등이 나온다. 


이제 문서를 보자.

## Update...


Unity 한 Frame의 의미는?

GameObject의 upate가 동작하

~~~~~~~~~~~~~ <= update시작

스크립트의 내용

 - preupdate update lateUdate

 - 이것을 모든 scene에서 돈다.

update 호출 

<== 이것은 눈에 보이지 않고 느린 주범이다. 코더의 수정 부분 코딩은 잘해야 한다.


그리고 preRender Render PostRender가 있다.

  Render는 카메라에 붙어 있는 모든 Render를 호출한다. pre와 postRender로 한다. 

<== 이미지 개선이 가능한 부분 디자이너의 개선 부분

~~~~~~~~~~~~<- 여까지가 한프레임이...(폴리콘.. 카운트 )


따라서 코더는 스크립트의 단위 테스트로 문제점 시간 등을 확인해야 한다.


** 스크립트는 80line을 넘지말다 (최대 120line) / 스크립트는 하나의 함수라고 생각해야 한다.

** 스크립트가 많아지면 언떻게 해야 파라메터 차이로 스크립트 늘어나는 것을 줄일 수 있다.

** 간결하게 쓰고 이중 포문 쓰지 마라, 

** 펑션콜을  줄인다...


update는 빠른 컴퓨터는 빠르고, 느리다.

- deltaTime

fixedUpdate 는 정해진 시간에 업데트된다. 

- TimeManger를 가봐라..

- 이것은 물리에만 사용해야 하낟.

- fixedDeltaTime


##숏컷



##get하는 방법

-함수를 불러오는 방법

  playercontrol 스크립트 안에 있는 함수를 찾는다.

  GameObject.Find("player").GetComponet<playercontrol>().함수명();


   스크립트내에서 찾을 때 소문자. 소문자는 자기것을 의미함

   gameObjct.GetComponent<playControl>().함수명();


   tranform.FindChild("player").gameObject.

- 중복된 스크립트는 어떻게 가져 오나?  더비부모를 만들고 거기에 넣어서 더미부모를 가져 오게 한다. 이때 응답은 배열이다. 또는 Tag검색하라.


## 스크립트의 동작순서

1) Awake <== 딱 한번 호출 FInd 함수 되도록이면 쓰지마라. 참조해서 사용해라

2) Start <== 딱 한번 호출 FInd 함수 되도록이면 쓰지마라. 참조해서 사용해라

    생성하고 꺼놓으다가 다시 호출해도 호출되지 않는다. Inspector에서 스크립트 체크박스를 언체크...하면 끄는 것이다. 

3) update() == OnGUI()

    업데이트도 껐다 키면 호출 안된다. 

4) lateUpdate


## 인스턴스

as는 타입케스팅이다.

인스턴스를 통해서 원하는 컨포넌트를 불러올 수 있다.



## Corote..

직접예제...


## 상속

초기화 Awake, 

클래스와 파일이름은 동일하게


## 

namespace는 사용마라..


## 생성자는 못쓴다. 모노비해뷰어에 상속받은 놈은



## Use STatic Typing

자바스크립트보다 C# 스크립트가 훨 빠르다..

Var 타입때문에 매우 드리다.

이것은 모든 타입을 모두 담을 수 있다. 따라서 동적으로 타입케스팅된다. 



## 스크립트를 빠르게 쓸 수 있는 방법

ArrayList.add(모든타입)...

따라서 배열크기가 일정하지 않아서 검색시 느리다.

타입이 하나라면,

Vector[] 형태로 배열로 사용하는 것이 훨씬 빠르다. 17.5배 빠르다.

이렇게 하면 메모리 각 크기가 동일하기에 빠른 것이다.



## 컴포넌트 캐싱

미리 담아 둔다는 의미..

최신 숏컷으로 대신하기에 별로 사용하지 않는다...

소문자 transform으로 자신의 것을 지정할 수 있다.



## 이팩트 사용시 화면이 멈칫하는 것 막는 방법

1) 생성후 저멀리 위치 옮겨 놓을 것을 위치를 옮기를 형태로 한다. 

2) 스크립트에서 원하는 해당 콤포넌트를 하나씩 AddComponet하는 방법이 있다.

    - 한꺼번에 넣어 놓으면 느리다. 코드로 단계별로 호출하는 방식이 빠르다.

3) 화면에 보이지 않으면 업데이트 하지 마라..



## 카메라 들어올때만 업데이트하라

- 스크립트를 언체크한다. (끈다)



## 컴파일 순서

Assets 내용에 있는것을 본다.

- Startd Assets을 1차 

- Editor 폴더

- Plugin 폴더 (외부 것.. C++, DLL)

* 상기 3가지는 다른 것에서 불러 올수 없다. (컴파일시)


[Scripting Overview 파일]

번역/수정/추가 주세영

 

Scripting Overview  

유니티 내부의 스크립팅은 Behaviours(동작들)라고 불리는 스크립트 개체를 게임 오브젝트에 붙임 으로써 시작된다고 할 수 있습니다. 스크립트 개체의 내부 함수들은 특정 이벤트 상에서 호출이 되는데 가장 많이 사용되는 함수는 아래와 같습니다.

Update

이함수는 하나의 프레임이 렌더링되기 전에 호출된다. 물리코드를 제외한 대부분의 게임 동작 코드가 실행되는 곳입니다.

FixedUpdate

물리 동작의 매 스텝마다 호출됩니다. 물리학 기반의 게임 동작들을 실행 할 수 있는 곳입니다.

Code outside any function

오브젝트들이 로드될 때 함수 외부의 코드들이 실행 됩니다. 이것은 스크립트의 상태를 초기화 하는데 사용될 수 있습니다.

 

일반적인 동작들

대부분의 게임 오브젝트에 대한 조작은 게임오브젝트의 Transform 이나 Rigidbody를 통해 이루어 집니다. 이것은 Monobehaviour를 상속받은 스크립트 안에서 접근 할 수 있는데, 사용법은 대충 아래와 같습니다.

using UnityEngine;
using System.Collections;

public class example :
MonoBehaviour {
    void Update() {
        transform.Rotate(0, 5, 0);
    }
}

만약 오브젝트를 움직이고 싶다면

C#

using UnityEngine;
using System.Collections;

public class example :
MonoBehaviour {
    void Update() {
        transform.Translate(0, 0, 2);
    }
}

 

Overview: Keeping Track of Time  

Time 클래스는 deltaTime이라 불리는 매우 중요한 클래스 변수를 갖고 있다. 이 변수는 Update또는 FixedUpdate 함수가 마지막으로 호출된 이후 경과한 시간의 양을 저장한다. (Update 함수 내부와 FixedUpdate 함수 내부랑은 다르다)

그래서 앞의 예제를 일정한 속도로 회전하도록 만들기 위해 다음 처럼 수정한다.

using UnityEngine;
using System.Collections;

public class example :
MonoBehaviour {
    void Update() {
        transform.Rotate(0, 5 *
Time.deltaTime, 0);
    }
}

오브젝트 이동:

C#

using UnityEngine;
using System.Collections;

public class example :
MonoBehaviour {
    void Update() {
        transform.Translate(0, 0, 2 *
Time.deltaTime);
    }
}

만약 당신이 프레임마다 값을 더하거나 뺀다면 Time.delTaTime 곱해야 한다.

다른 예로 만일 당신이 시간에 따라 빛의 범위를 증가시키고 싶다면 아래와 같이 변경하면 되는데 아래 구문은 초당 2유닛 만큼씩 반경을 변경시킬 것이다.

C#

using UnityEngine;
using System.Collections;

public class example :
MonoBehaviour {
    void Update() {
        light.range += 2.0F *
Time.deltaTime;
    }
}

Forces를 통해 Rigidbody를 조작할때는, 엔진이 이미 그것을 처리해 주므로 일반적으로 Time.deltaTime을 곱할 필요는 없다.

Overview: Accessing Other Components  

 

컴포넌트들은 게임 오브젝트에 Attack 된다. 하나의 Renderer 컴포넌트를 게임 오브젝트에 붙이는 것은 그것을 스크린 상에 그려지도록 만드는 것이고, Camera를 붙이는 것은 그것을 카메라 오브젝트로 변환하는 것이다. 모든 스크립트들은 컴포넌트들이므로 그것들은 게임 오브젝트들에 붙일수 있다(Monobehaviour를 상속받았다면)

대부분의 일반 컴포넌트들은 간단한 멤버변수들로 액세스 할 수 있다.

Component

Accessible as

Transform

transform

Rigidbody

rigidbody

Renderer

renderer

Camera

camera (only on camera objects)

Light

light (only on light objects)

Animation

animation

Collider

collider

... etc.

 

만일 게임오브젝트가 당신이 얻고자 하는 컴포넌트를 갖고 있지 않다면, 변수들은 null 셋팅된다.

게임 오브젝트에 attach 어떤 컴포넌트나 스크립트는 GetComponent 통해서 access 있다.

using UnityEngine;
using System.Collections;

public class example :
MonoBehaviour {
    void Awake() {
        transform.Translate(0, 1, 0);
        GetComponent<
Transform>().Translate(0, 1, 0);
    }
}

 

transform Transform 사이의  /소문자 차이에 주의해야한다. 전자는 변수이고 후자는 하나의 클래스 똔느 스크립트의 이름이다. 이 대/소문자는 클래스/스크립트이름과 변수이름을 구별하게 해준다.

우리가 배운 것을 적용해 이제 우리는 GetComponent를 사용해 동일한 게임오브젝트에 붙은 다른 스크립트 또는 builtin 컴포넌트들을 찾을 수 있다. 다음의 예제를 작동시키려면 DoSomething함수를 가진 OtherScript 라는 스크립트가 필요하다. OtherScript는 게임오브젝트에 attach되어야 한다.

 

C#

using UnityEngine;
using System.Collections;

public class example :
MonoBehaviour {
    void Update() {
        OtherScript otherScript = GetComponent<OtherScript>();
        otherScript.DoSomething();
    }
}

 

Overview: Accessing Other Game Objects 

 

대부분의 게임코드는 단 하나의 오브젝트만 다루지 않는다. 유니티 스크립팅 인터페이스는 다른 게임 오브젝트들과 그 내부의 컴포넌트들을 찾아서 접근하기 위한 다양한 방법들을 제공한다.

다음의 코드에서 우리는 Scene내에 게임 오브젝트들에 붙여진 OtherScript 라는 이름의 스크립트가 있다고 가정한다.

.

C#

using UnityEngine;
using System.Collections;

public class example :
MonoBehaviour {
    void Update() {
        OtherScript otherScript = GetComponent<OtherScript>();
        otherScript.DoSomething();
    }
}

1.     Inspector를 통한 참조

Inspector를 통해서 변수에 할당된 컴포넌트에 접근할 수 있다.

:

C#

using UnityEngine;
using System.Collections;

public class example :
MonoBehaviour {
    public
Transform target;
    void Update() {
        target.Translate(0, 1, 0);
    }
}

 

또한 다른 오브젝트에 대한 참조들을 inspector에 노출 시킬 수 있는데 Inspector의 타겟 슬롯위로 다른 스크립트를 가지고 있는 게임 오브젝트를 드래그 할 수 있다.

 

C#

using UnityEngine;
using System.Collections;

public class example :
MonoBehaviour {
    public OtherScript target;
    void Update() {
        target.foo = 2;
        target.DoSomething("Hello");
    }
}

2.     오브젝트 계층을 통한 위치 찾기

오브젝트의 Transform 컴포넌트를 통해 기존 오브젝트의 Child Parent 오브젝트를 찾을 수 있다.

 

C#

using UnityEngine;
using System.Collections;

public class example :
MonoBehaviour {
    void Awake() {
        transform.Find("Hand").Translate(0, 1, 0);
    }
}

일단 Hierarchy에서 transform을 찾으면, 타 스크립트들에 접근해서 GetComponent함수를 사용할 수 있다.

 

C#

using UnityEngine;
using System.Collections;

public class example :
MonoBehaviour {
    void Awake() {
        transform.Find("Hand").GetComponent<OtherScript>().foo = 2;
        transform.Find("Hand").GetComponent<OtherScript>().DoSomething("Hello");
        transform.Find("Hand").rigidbody.AddForce(0, 10, 0);
    }
}

모든 Child 들을 순환하면서 접근할 있다.

:

C#

using UnityEngine;
using System.Collections;

public class example :
MonoBehaviour {
    void Awake() {
        foreach (
Transform child in transform) {
            child.Translate(0, 10, 0);
        }
    }
}

3.     이름과 Tag를 통해 접근하기

GameObject.FindWithTag GameObject.FindGameObjectsWithTag 사용해서 어떤 tag들로 게임 오브젝트들을 찾을 있다GameObject.Find  이용해서 이름으로 찾을수 있다.

C#

using UnityEngine;
using System.Collections;

public class example :
MonoBehaviour {
    void Start() {
       
GameObject go = GameObject.Find("SomeGuy");
        go.transform.Translate(0, 1, 0);
       
GameObject player = GameObject.FindWithTag("Player");
        player.transform.Translate(0, 1, 0);
    }
}

찾은 게임 오브젝트를 통해 GetComponent들을 사용할 있다.

C#

using UnityEngine;
using System.Collections;

public class example :
MonoBehaviour {
    void Start() {
       
GameObject go = GameObject.Find("SomeGuy");
        go.GetComponent<OtherScript>().DoSomething();
       
GameObject player = GameObject.FindWithTag("Player");
        player.GetComponent<OtherScript>().DoSomething();
    }
}

메인 카메라 같은 특별한 오브젝트들은  Camera.main. 같은 단축어를 있다.

4.     파라미터를 통해서 위치 찾기

어떤 이벤트 메시지들은 이벤트에 대한 자세한 정보를 담는다. 예를 들어 Trigger 이벤트들은 핸들러 함수에 충돌 오브젝트의 Collider 컴포넌트를 넘겨 준다. OnTriggerStay 함수는 Collider 대한 참조를 제공해 준다. Collider로부터 그것에 Attach rigidbody 얻을 있다.

C#

using UnityEngine;
using System.Collections;

public class example :
MonoBehaviour {
    void OnTriggerStay(
Collider other) {
        if (other.rigidbody)
            other.rigidbody.AddForce(0, 2, 0);
       
    }
}

또는 Collider 동일한 게임오브젝트에 붙여진 다른 컴포넌트들을 얻을 있다.

C#

using UnityEngine;
using System.Collections;

public class example :
MonoBehaviour {
    void OnTriggerStay(
Collider other) {
        if (other.GetComponent<OtherScript>())
            other.GetComponent<OtherScript>().DoSomething();
       
    }
}

샘플에서 Other 변수 뒤에 접미사를 붙임으로써 충돌 오브젝트 안의 다른 컴포넌트를 액세스 있다.

 

5. 특정 타입의 스크립트들

 Object.FindObjectsOfType 사용해 특정 타입의 어떤 오브젝트나 스크립트들을 찾거나 Object.FindObjectOfType. 사용해 특정 타입의 첫번째 오브젝트를 찾을 있다.

C#

using UnityEngine;
using System.Collections;

public class example :
MonoBehaviour {
    void Start() {
        OtherScript other = FindObjectOfType(typeof(OtherScript));
        other.DoSomething();
    }
}

Overview: Vectors

유니티는 모든 3차원 벡터를 표현하기 위해 Vector3 클래스를 사용한다. 3차원 벡터의 성분들은 x,y,z 멤버 변수들을 통해 접근 있다.

 

Vector3 aPosition;

aPosition.x = 1;

aPosition.y = 1;

aPosition.z = 1;

 

벡터의 모든 성분들을 한번에 초기화하기 위해 Vector3 생성자를 사용할 있다..

C#

using UnityEngine;
using System.Collections;

public class example :
MonoBehaviour {
    public
Vector3 aPosition = new Vector3(1, 1, 1);
}

Vector3  또한 공통적으로 많이 사용하는 값들을 정의해놓은 공통 변수들을 통해 셋팅할 있다.

C#

using UnityEngine;
using System.Collections;

public class example :
MonoBehaviour {
    public
Vector3 direction = Vector3.up;
}

벡터 상에서의 연산들은 아래와 같은 방식으로 접근 된다.

C#

using UnityEngine;
using System.Collections;

public class example :
MonoBehaviour {
    void Awake() {
        SomeVector.Normalize();
    }
}

또한 여러 개의 벡터들에 대한 연산들은 Vector3 클래스 함수들을 사용한다.

C#

using UnityEngine;
using System.Collections;

public class example :
MonoBehaviour {
    public
Vector3 oneVector = new Vector3(0, 0, 0);
    public
Vector3 otherVector = new Vector3(1, 1, 1);
    public float theDistance =
Vector3.Distance(oneVector, otherVector);
}

또한 벡터들을 다루기 위해 일반 수학 연산자들을 사용할 있다.

vectors: combined = vector1 + vector2;

사용 가능한 연산들과 속성들의 전체 리스트는 Vector3 클래스 참조 문서를 참고하기 바란다.

 

Overview: Member Variables & Global Variables

 

함수 외부에서 정의된 변수는 하나의 멤버 변수로 정의된다. 멤버 변수들은 유니티 내의 Inspector를 통해 접근 될 수 있다. 또한 멤버 변수에 저장되는 어떤 값은 프로젝트와 함께 자동으로 저장된다.

 

C#

using UnityEngine;
using System.Collections;

public class example :
MonoBehaviour {
    public float memberVariable = 0.0F;
}

 

위의 변수는 inspector 안에서 Member Variable 이라는 하나의 수치 속성으로서 보여지게 된다.

만일 변수 타입을 컴포넌트 타입(Transform, RigidBody, Collider,스크립트이름)으로 셋팅한다면, Inspector안에서 게임 오브젝트들을 위로 드래깅함으로써 그것들을 셋팅할 있다.

C#

using UnityEngine;
using System.Collections;

public class example :
MonoBehaviour {
    public
Transform enemy;
    void Update() {
        if (
Vector3.Distance(enemy.position, transform.position) < 10)
            print("I sense the enemy is near!");
       
    }
}

또한 Private 멤버 변수들을 생성할 있다. Private 멤버 변수들은 스크립트 외부에서는 보이지 않아야 하는 State 저장하는 유용하다. Private 멤버 변수들은Disk 저장되지 않으며 Inspector에서 Edit 없다. 그것들은 Debug 모드일 Inspector 안에서 있다. 이것은 실시간 Updating Debugger 처럼 Private 변수들을 사용할 있게 해준다.

C#

using UnityEngine;
using System.Collections;

public class example :
MonoBehaviour {
    private
Collider lastCollider;
    void OnCollisionEnter(
Collision collisionInfo) {
        lastCollider = collisionInfo.collider;
    }
}

전역변수

Static 키워드를 사용해 전역 변수를 생성 있다.

아래 스크립트는 someGlobal 이라는 전역 변수를 생성한다.

C#

using UnityEngine;
using System.Collections;

public class example :
MonoBehaviour {
    public static int someGlobal = 5;
    void Awake() {
        print(someGlobal);
        someGlobal = 1;
    }
}

다른 스크립트에서 그것을 액세스 하기 위해서는 스크립트명.전역변수명을 사용할 필요가 있다.
TheScriptName.someGlobal = 10;

Overview: Instantiate  

Instantiate는 한 오브젝트를 복제(Duplicate)한다. 모든 Attach 된 스크립트들과 전체 Hierarchy를 포함한다. 그것은 예상되는 그대로의 Reference들을 유지한다. 복제된 Hierarchy의 외부 오브젝트들에 대한 참조들은 Untouched 상태로 되고 , 내부 오브젝트들에 대한 참조들은 복제된 오브젝트에 매핑될 것이다.

 

Instantiate는 믿기지 않을 만큼 빠르고 용도도 매우 많다. 유니티를 최대로 활용하기 위해서는 필수적이다.

 

아래의 간단한 예제 스크립트는 Collider를 가진 rigidbody attach 되었을때 자신을 Destroy하고 대신 explosion 오브젝트를 Spawn할 것이다.

 

C#

using UnityEngine;
using System.Collections;

public class example :
MonoBehaviour {
    public
Transform explosion;
    void OnCollisionEnter() {
        Destroy(gameObject);
       
Transform theClonedExplosion;
        theClonedExplosion = Instantiate(explosion, transform.position, transform.rotation) as
Transform;
    }
}

Instantiate 흔히 Prefab.들과 결합되어 사용된다.

Overview: Coroutines & Yield  

 

게임 코드를 작성할 , 하나의 이벤트 시퀀스를 스크립트해 필요를 자주 느끼게 된다. 그것은 다음 코드와 같을 것이다.

C#

using UnityEngine;
using System.Collections;

public class example :
MonoBehaviour {
    private int state = 0;
    void Update() {
        if (state == 0) {
            state = 1;
            return;
        }
        if (state == 1) {
            state = 2;
            return;
        }
    }
}

yield문을 사용하는 것이 좀더 편리할 때가 종종있다. Yield 문은 return 특수한 종류이고, next time 호출될 yield 다음 라인으로부터 함수가 계속됨을 보장해 준다.

C#

using UnityEngine;
using System.Collections;

public class example :
MonoBehaviour {
    IEnumerator Awake() {
        while (true) {
            yield return null;
            yield return null;
        }
    }
}

 

또한 어떤 이벤트가 발생할 때까지 Update 함수의 실행을 지연시키기 위해 특별한 값들을 Yield 문에 전달 있다.

C#

using UnityEngine;
using System.Collections;

public class example :
MonoBehaviour {
    IEnumerator Awake() {
        yield return new
WaitForSeconds(5.0F);
    }
}

 

아래 예제는 Do 실행하지만 호출한 즉시 계속해서 다음이 실행될 것이다.

C#

using UnityEngine;
using System.Collections;

public class example :
MonoBehaviour {
    IEnumerator Do() {
        print("Do now");
        yield return new
WaitForSeconds(2);
        print("Do 2 seconds later");
    }
    void Awake() {
        Do();
        print("This is printed immediately");
    }
}

예제는 Do 실행하고, 실행을 계속하기 전에 Do 끝날 때까지 기다릴것이다.

C#

using UnityEngine;
using System.Collections;

public class example :
MonoBehaviour {
    IEnumerator Do() {
        print("Do now");
        yield return new
WaitForSeconds(2);
        print("Do 2 seconds later");
    }
    IEnumerator Awake() {
        yield return StartCoroutine("Do");
        print("Also after 2 seconds");
        print("This is after the Do coroutine has finished execution");
    }
}

이벤트 핸들러들 역시 coroutine 있다.

 

Update FixedUpdate 함수 안에서는 Yield 사용할 없다는 것에 주의하라, 하지만 당신은 StartCoroutine 하나의 함수를 시작하기 위해 사용할 있다.

 

많은 정보는  YieldInstructionWaitForSecondsWaitForFixedUpdateCoroutine and MonoBehaviour.StartCoroutine  참조하기 바란다.

Overview: Writing Scripts in C# & Boo

 

Syntax 별개로 하고 C#이나 Boo 스크립트를 작성할 몇가지 차이점이 있다.

1. MonoBehaviour 부터 상속

모든 게임오브젝트 동작 스크립트들은 Monobehaviour로부터 상속을 받아야 한다. (직접또는 간접적으로) 이것은 자바스크립트 안에서는 자동적으로 일어나지만 C#이나 Boo 스크립트들에서는 명시적이어야 한다. 만일 유니티에서 Asset -> Create -> C Sharp/Boo Script  통해 스크립트를 생성한다면, 생성된 템플릿은 이미 필요한 정의를 포함하고 있을 것이다.

public class NewBehaviourScript : MonoBehaviour {...} // C#

class NewBehaviourScript (
MonoBehaviour):  ... # Boo

2. 초기화는  Awake 또는 Start 함수에서 하라

자바스크립트에서는 함수들 외부에 놓는 것들을, C#이나 BOO 에서는 Awake 함수나 Start함수에 놓는다.

Awake Start 차이점은 Awake 씬이 로드될 실행되고 , Start Update FixedUpdate 처음 호출되기 직전에 호출된다. 모든 Awake함수들은 Start 함수들이 호출되기 이전에 호출된다.

3. 클래스 이름은 파일이름과 일치해야 한다..

클래스 이름과 파일이름이 다르면 GameObject에서 제대로 스크립트 컴포넌트로써 인식을 못한다.

4. C# 에서 Coroutines 들은 다른 syntax 갖는다..

Coroutines  IEnumerator 타입을 리턴해야하고  yield  대신  yield return ... ;  사용해야한다.

using System.Collections;
using UnityEngine;
public class NewBehaviourScript :
MonoBehaviour {
   
// C# coroutine
    IEnumerator SomeCoroutine () {
       
// Wait for one frame
        yield return 0;
       
       
// Wait for two seconds
        yield return new WaitForSeconds (2);
    }
}

5. namespaces 사용하지 말라.

유니티는 아직 namespace안에 당신의 스크립트들을 놓는 것을 지원하지 않는다. 제한은 차후 버전에서는 제거 것이다.

6. 오직 멤버 변수들만 serialized  되고 Inspector 에서 보여진다.

Private protected 멤버 변수는 Debug Mode에서만 보일것이다.  Properties 들은 serialized 되지 않거나, 또는 inspector에서 보이지 않을 것이다.

7. 생성자 사용을 피하라.

어떤값도 생성자에서 초기화 하지 말라. 대신 Awake Start 사용해야 한다. 유니티는 Edit Mode에서 조차도 생성자를 자동으로 Invoke한다. 이것은 종종 스크립트를 컴파일한 즉시 발생하는데, 생성자는 스크립트의 Default Value들을 Retrieve하기 위해서 Invoked 필요가 있기 때문이다. 생성자는 예기치 않은 순간에 호출될 수도 있을 뿐만 아니라, prefabs Inactive 게임오브젝트들을 위해서 호출될 수도 있다.

예를 들어 생성자를 사용하는 singleton 패턴의 경우에 위함한 결과를 야기할 있고, random Null reference Exception 에러로 이어질 있다.

 

그러므로 만일 SingleTon 패턴을 구현하고 싶다면 생성자 대신 Awake 사용하라. 실제로 Monobehavior로부터 상속받은 클래스의 생성자 안에 어떤 코드를 넣을 일은 없다.

Overview: The most important classes  

Global functions accessable in javascript or the base class in C#

Moving / Rotating objects around

Animation System

Rigid bodies

FPS or Third person character controller

 

Overview: Performance Optimization  

1. Use Static Typing

자바스크립트를 사용할 때 가장 중요한 최적화는 동적 타입 대신에 정적 타입을 사용하는 것이다. 유니티는 별도의 작업없이 자바스크립트 구조체들을 정적 타입 코드로 변환해주는 자바스크립트 타입 참조 라는 테크닉을 사용한다.

C#

*JavaScript

*C#

*Boo

var foo = 5;

using UnityEngine;
using

System.Collections;

public class example :
MonoBehaviour {
public int foo =5;
}

import

UnityEngine
import

System.Collections

class example(
MonoBehaviour):

public foo as int = 5

Var foo = 5;

위의 예제에서 foo 자동으로 integer 값으로 추론이 된다. 그러므로 유니티는 비싼 동적으로 변수 찾기없이 많은 컴파일 타임 최적화를 적용할 있다. 이것이 유니티의 자바스크립트가 다른 자바스크립트 구현들 보다 20 빠른 이유이다.

유일한 문제는 때때로 타입의 추론이 모두 적용되지 않아서 유니티가 동적 타입으로 되돌아 가는 것이다. 동적 타이핑으로 돌아가면 자바스크립트 코드 작성은 더 간단하다. 하지만 또한 코드가 더 느리게 실행되게 된다.

Let's see some examples.

C#

*JavaScript

*C#

*Boo

function Start () {
var foo =

GetComponent(MyScript);
foo.DoSomething();
}

using UnityEngine;
using

System.Collections;

public class example :
MonoBehaviour {
void Start()

{
MyScript foo = GetComponent<MyScript>();
foo.DoSomething();
}
}

JavaScript

Function Start()

{

Var foo = GetComponent(MyScript);

foo.Dosomething();

}

import

UnityEngine
import

System.Collections

class example(
MonoBehaviour):

def Start():
foo as MyScript = GetComponent[of

MyScript]()
foo.DoSomething()

여기서 foo 동적으로 타입이 정해지므로 DoSomething 함수 호출은 필요 이상으로 오래 걸린다. 이유는 foo 타입은 var , unknown 이기 때문에 그것이 DoSomething 함수를 지원하는지를 확인해야 하고 만일 그렇다면 함수를 호출한다.

C#

*JavaScript

*C#

*Boo

function Start () {
var foo :

MyScript = GetComponent(MyScript);
foo.DoSomething();
}

using UnityEngine;
using

System.Collections;

public class example :
MonoBehaviour {
void Start()

{
MyScript foo =

GetComponent<MyScript>();
foo.DoSomething();
}
}

JavaScript

function StarT()

{

Var foo : MyScript = GetComponent(MyScript);

Foo.DoSomething();
}

import

UnityEngine
import

System.Collections

class example(
MonoBehaviour):

def Start():
foo as MyScript = GetComponent[of

MyScript]()
foo.DoSomething()

여기서 우리는 foo 강제로 특정 타입으로 만들었다, 이렇게 하면 좀더 나은 성능을 얻을 있는 것이다.

2. Use #pragma strict

문제는 당신이 동적타이핑을 사용할 때 보통 알아 차리지 못하고 있다는 것인데, 이것을 알려주는 것이 #pragma strict 이다 . 간단히 스크립트의 맨위에 #pragma strict 를 추가하면 유니티는 그 스트립트에서 동적 타이핑기능을 꺼버리고, 정적 타이핑만 사용하도록 강제할 것이다. 타입을 알지 못할 때마다, 유니티가 컴파일 에러를 리포트를 할 것이다. 그래서 이 경우에서는 컴파일시 foo에서 에러가 날 것이다.

C#

*JavaScript

*C#

*Boo

#pragma strict
function Start ()

{
var foo : MyScript = GetComponent(MyScript)

as MyScript;
foo.DoSomething();
}

using UnityEngine;
using

System.Collections;

public class example :
MonoBehaviour {
void Start()

{
MyScript foo = GetComponent<MyScript>() as

MyScript;
foo.DoSomething();
}
}

JavaScript

#pragma strict

{

var foo = GetComponent(MyScript);

foo.DoSomething();

}

import

UnityEngine
import

System.Collections

class example(
MonoBehaviour):

def Start():
foo as MyScript = GetComponent[of

MyScript]()
foo.DoSomething()

3. component 캐싱

또 다른 최적화는 컴포넌트의 캐싱이다. 이 최적화는 약간의 코딩이 요구되고 항상 가치가 있는 것은 아니다. 하지만 정말 자주 사용되는 스크립트에서 최적화의 마지막 방법으로 유용하다.

GetComponent 나 액세스 변수를 통해 하나의 컴포넌트를 액세스할 때 마다, 유니티는 게임 오브젝트로부터 올바른 컴포넌트를 찾아야 하는데 private 변수에 컴포넌트에 대한 참조를 캐싱함으로써 쉽게 저장해서 찾는 시간을 줄일 수 있다.

간단히 다음을:

C#

*JavaScript

*C#

*Boo

function Update () {
transform.Translate(0, 0, 5);
}

using UnityEngine;
using

System.Collections;

public class example :
MonoBehaviour {
void Update()

{
transform.Translate(0, 0, 5);
}
}

import

UnityEngine
import

System.Collections

class example(
MonoBehaviour):

def Update():
transform.Translate(0, 0, 5)

아래와 같이

C#

*JavaScript

*C#

*Boo

private var myTransform : Transform;
function

Awake () {
myTransform =

transform;
}

function Update () {
myTransform.Translate(0, 0, 5);
}

using UnityEngine;
using

System.Collections;

public class example :
MonoBehaviour {
private
Transform myTransform;
void Awake()

{
myTransform = transform;
}
void Update() {
myTransform.Translate(0, 0,

5);
}
}

import

UnityEngine
import

System.Collections

class example(
MonoBehaviour):

private myTransform as
Transform

def Awake():
myTransform = transform

def Update():
myTransform.Translate(0, 0, 5)

후자의 코드는 유니티가 매프레임 게임오브젝트에서 transform 컴포넌트를 찾을 필요가 없기 때문에 상당히 더 빠르게 실행될 것이다.transform이나 다른 단축어 속성 대신 GetComponent를 사용하는 스크립트된 컴포넌트들에 모두 똑같이 적용된다.

4. 고유의 배열들 사용

고유 배열들은 빠르다, 매우 빠르다~그러니 그것들을 쓰자.

비록 ArrayList Array 클래스들은 쉽게 성분들을 추가할 수 있어서 사용하기 쉽지만 거의 비슷한 속도를 갖고 있지 않다. 고유 배열들은 고정된 사이즈를 가지지만 대부분의 경우 사전에 최대 사이즈를 알고 있기에 나중에 쉽게 채울 수가 있다. 고유 배열의 가장 좋은 점은 어떤 외부 타입 정보나 오버헤드 없이 하나의 꽉차게 묶여진 버퍼에 구조체 데이터 타입들을 직접 가져온다는 것이다. 그러므로 모든 것이 메모리에서 선형적으로 존재하기 때문에 캐쉬상에서 반복순환 하기가 매우 쉽다.

C#

*JavaScript

*C#

*Boo

private var positions : Vector3[];
function

Awake () {
positions = new
Vector3[100];
for

(var i = 0; i < 100; i++)
positions[i] =
Vector3.zero;
}

using UnityEngine;
using

System.Collections;

public class example :
MonoBehaviour {
private
Vector3[] positions;
void Awake()

{
positions = new
Vector3[100];
int i =

0;
while (i < 100) {
positions[i] =
Vector3.zero;
i++;
}
}
}

import

UnityEngine
import

System.Collections

class example(
MonoBehaviour):

private positions as (
Vector3)

def Awake():
positions = array[of
Vector3](100)
i as int

= 0
while i < 100:
positions[i] =
Vector3.zero
i++

5. 불필요한 함수 호출은 피해라

모든 최적화에서 가장 간단하면서도 최선의 방법은 동작을 최소로 하는 것이다. 예를 들면 , 멀리 떨어져 있는 적의 경우 잠들게 하는 것은 성능 향상에 좋은 기법이다. 잠들게 한다는 것은 플레이어가 가까이 오기 전까지는 잠든 듯이 아무것도 하지 않는 것이다. 이런 상황을 느리게 조종하는 방법은 아래와 같을 것이다.

C#

*JavaScript

*C#

*Boo

var target : Transform;

function Update ()

{
// Early out if the player is too far

away.
if

(Vector3.Distance(transform.position,

target.position) > 100)
return;
// perform real work work...
}

using UnityEngine;
using

System.Collections;

public class example :
MonoBehaviour {
public
Transform target;
void Update() {
if (
Vector3.Distance(transform.position,

target.position) > 100)
return;

}
}

import

UnityEngine
import

System.Collections

class example(
MonoBehaviour):

public target as
Transform

def Update():
if
Vector3.Distance(transform.position,

target.position) > 100:
return

이것은 좋은 방법이 아니다. 왜냐하면 유니티는 매 프레임 Update 함수를 호출해야하기 때문이다. 좀더 나은 해결법은 플레이어가 가까이 올 때 까지 스크립트를 꺼두는 것(disable)시키는 것이다. 이것에는 3가지 방법이 있다.

1. OnBecameVisible OnBecameInvisible 을 사용하는 법. 이 콜백함수 들은 렌더링 시스템에 들어 있다. 어떤 카메라가 그 오브젝트를 볼수 있는 순간 , OnBecameVisible이 호출되고, 어떤 카메라도 그것을 더 이상 보지 않을 때 OnBecameInvisible 이 호출 될 것이다. 이것은 몇가지 경우에 유용하지만 종종 AI상에서는 유용하지 않다. 왜냐하면 카메라를 적들로부터 돌리자마자 꺼지기 때문이다.

C#

*JavaScript

*C#

*Boo

function OnBecameVisible () {
enabled =

true;
}

function OnBecameInvisible () {
enabled = false;
}

using UnityEngine;
using

System.Collections;

public class example :
MonoBehaviour {
void OnBecameVisible()

{
enabled = true;
}
void OnBecameInvisible() {
enabled = false;
}
}

import

UnityEngine
import

System.Collections

class example(
MonoBehaviour):

def OnBecameVisible():
enabled = true

def OnBecameInvisible():
enabled = false

2. triggers 사용 , 하나의 간단한 구형 트리거로 놀라운 일을 할수 있는데 , 원하는 영향 범위에서 벗어날 때 OnTriggerEnter/Exit 함수가 호출된다.

C#

*JavaScript

*C#

*Boo

function OnTriggerEnter (c : Collider) {
if

(c.CompareTag("Player"))
enabled =

true;
}

function OnTriggerExit (c :
Collider) {
if

(c.CompareTag("Player"))
enabled = false;
}


using UnityEngine;
using

System.Collections;

public class example :
MonoBehaviour {
void OnTriggerEnter(
Collider c) {
if (c.CompareTag("Player"))
enabled = true;

}
void OnTriggerExit(
Collider c) {
if (c.CompareTag("Player"))
enabled = false;

}
}

import

UnityEngine
import

System.Collections

class example(
MonoBehaviour):

def OnTriggerEnter(c as
Collider):
if

c.CompareTag('Player'):
enabled = true

def OnTriggerExit(c as
Collider):
if

c.CompareTag('Player'):
enabled = false

3. Coroutines을 사용. Update함수에서의 문제는 매프레임 호출된다는 것이다. 플레이어와의 거리 체크는 5초마다 수행할 수도 있을 것이다. 이것은 처리 시간을 많이 Save해준다.

Overview: Script compilation (Advanced)  

유니티는 모든 스크립트들을 .NET DLL 파일들로 컴파일 한다. DLL파일들은 런타임에 JIT(Just In Time) 컴파일 될 것이다.

이것은 믿기 어려울 만큼 빠르게 스크립트를 실행 할수있게 해준다. 그것은 전통적인 자바 스크립트보다 20배 더 빠르고 native C++코드보다 약 50% 더 느리다 . 스크립트를 저장할 때 유니티는 모든 스크립트를 컴파일하고 잠깐의 시간이 걸린다. 만일 유니티가 여전히 컴파일 중이라면 유니티의 메인 윈도우의 오른쪽 구석에서 프로그레스바가 도는 것을 볼수 있다.

스크립트 컴파일은 4단계로 수행된다.

1. "Standard Assets", "Pro Standard Assets" 또는 "Plugins" 안의 모든 스크립트들이 먼저 컴파일된다.

이 폴더들 안의 스크립트 들은 폴더 밖의 다른 스크립트들을 직접 액세스 할 수 없다.

클래스나 변수들을 참조할 수 없지만, GameObject.SendMessage를 통해서 통신은 할 수 있다.

2. "Standard Assets/Editor", "Pro Standard Assets/Editor" 또는 "Plugins/Editor" 안의 모든 스크립트들이 먼저 컴파일 된다.

만약에 UnityEditor 네임스페이스를 사용하고 싶다면 당신의 스크립트들을 이 폴더들에 넣어야 한다. 예를 들면 메뉴 아이템 추가나 커스텀 창들을 만들기 위해서는 스크립트들을 이 폴더들 안에 넣어야 한다. 이 스크립트들은 앞의 그룹의 스크립트들을 액세스 할 수 있다.

 

3. "Editor" 밖의 모든 스크립트들이 그 다음 컴파일 된다.

“Editor”폴더와 그 서브 폴더를 제외한 모든 스크립트들이 컴파일 된다.

이 단계의 스크립트들은 이전 단계의 스크립트들을 액세스 할 수 있다. 이것은  다른 언어로 짜여진 스크립트들을 얻을수 있도록 해준다. 예를 들면, C#스크립트 를 사용하는 자바스크립트를 만들길 원한다면 , C#스크립트는 Standard Assets 폴더에 놓고 자바스크립트는 Standard Assets 폴더 바깥에 놓는다면 그 자바스크립트는 C#스크립트를 직접 참조 할수 있다.

첫번째 그룹안에 놓인 스크립트들이 컴파일되면 세번째 그룹도 재컴파일 되어야 하므로 컴파일에 시간이 더 걸릴 것이다. 따라서 컴파일 시간을 단축시키고 싶다면 덜 바뀌는 스크립트들을 첫번째 그룹으로 옮기고 많이 바뀌는 것은 3번째 그룹으로  옮겨라

 

4. "Editor" 안의 스크립트들이 가장 마지막에 컴파일된다.

만약 UnityEditor 네임스페이스를 쓰고 싶다면 이 폴더에 넣어야 한다. 예를 들면 menu 아이템들을 추가하거나 custom wizards를 작성하기 위해서는 스크립트들을 이 폴더 안에 넣어야 한다.

이 스크립트들은 앞 그룹의 모든 스크립트들을 접근할 수 있다.

 

또한 WebPlayerTemplates이라고 불리는 폴더는 컴파일 되지 않는다..

조건부 컴파일은 유니티 버전에 의존한다.

유니티 2.6버전부터 C# 전처리기 정의가 추가되었다. 이 정의는 사용중인 유니티의 버전을 정의하고 특정 특징들을 조건적으로 액세스할 수 있게 해준다. 예를들면,


// Specific version define including

the minor revision
#if

UNITY_2_6_0
// Use Unity 2.6.0 specific

feature
#endif

// Specific version define not including the minor

revision
#if UNITY_2_6
// Use Unity 2.6.x specific feature
#endif

이 코드는 특정 버전에서만 가능한 game feature들을 활성화 시키는데 사용될 수 있다. 이 정의는 오직 2.6버전부터 제공된다는 것에 주의해라. 앞으로 나올 유니티 버전들은 스크립트 버전을 분별하기 위한 적절한 정의들이 제공될 것이다.

알림 : 플랫폼별 조건부 컴파일에 대해서 좀더 정보가 필요하다면 Platform Dependent Compilation.을 참조해라

Overview: Unity and Mono compatibility  

유니티를 위하여 당신의 스크립트 안에서 닷넷 라이브러리를 사용할 수있다. 당신 프로젝트 안에서 닷넷 호환성 레벨을 선택한 것에 따라 달라지고, 유니티를 풀클래스 라이브러리들보다 좀더 많거나 적게 제공하고 있다. 어떤 특정한 세팅들과 함수 호환성을 체크하고 싶다면 .Net Compatibility page.

 








3_MonoBehaviourAndUnityScripting20111215 파일을 연다.

Object <- Componet <- Behaviour  형태로 상속 받았다. 함수를 모두 사용할 수 있다.

이렇게 상속받아서 솟컷이 가능하다..



## 해당 이벤트 발생 처리


## 객체가 접근

## Component  접근

## Debugging  <== 실습하자..



## Send Message 방법을 알아보자.

GO.SendMessage("함수이름");

GO.SendMessage("함수이름, 인자");

이것은 프라이빗 함수라도 호출 가능하다. (인자도 특정한 것으로 정해져 있다.) 

펑션콜보다 센드메시지가 훨 빠를 때가 있다.


이것을 쓰면 메신저에 쌓아두고 나중에 여유가 되면 겟해서 실행한다.

- 시간에 관계없이 여유될 때 사용하게 하려면 이렇게 해서 처리해 두면 좋다.

- RPC로 다른 함수를 호출시 이해가 쉽다.

  이것은 패킷 필요없고 함수 호출로 동작이 가능하다. 

  이것의 시작점이 sendmessage이다. 통신도 편하게 사용할 수 있다.

  이것은 퍼블릭함수만 호출가능하다.





[delegate를 설명하자.]

- 활용하기에 유용하다. C# 스럽다.

새로 new Scene

스크립트 DelegateTest 스크립트 추가. 


using UnityEngine;

using System.Collections;

using System;

using System.Collections.Generic;

using System.Text;


delegate void dele1();

delegate int dele2(int a, int b);

public class DelegateScript : MonoBehaviour {

public class MathClass

{

public void Intor()

{

Debug.Log("Start the Intro~");

}

public int Sum(int a, int b)

{

return a+b;

}

}

// Use this for initialization

void Start () {

MathClass mclass = new MathClass();



dele1 intro = new dele1(mclass.Intor);

dele2 sum = new dele2(mclass.Sum);

intro();

int result = sum(-10, 90);

Debug.Log("result : " + result.ToString());

}


}


그리고 메인카메라에 스크립트를 추가한다.




using UnityEngine;

using System.Collections;

using System;

using System.Collections.Generic;

using System.Text;


//사용법


delegate void deleMath(int Value);



public class MathClass

{

public int number;

public void plus(int Value)

{

number = number + Value;

}

public void Minus(int Value)

{

number = number - Value;

}

public void Multiply(int Value)

{

number = number * Value;

}

}


public class DelegateTest : MonoBehaviour {

void Start() 

{

MathClass MClass = new MathClass();

deleMath Math = new deleMath(MClass.plus);

Math += new deleMath(MClass.Minus);

Math += new deleMath(MClass.Multiply);

//result

MClass.number = 10;

Math(10);

Debug.Log("result : "+MClass.number.ToString());

// delegate remove

Math -= new deleMath(MClass.Minus);

//result

MClass.number = 10;

Math(10);

Debug.Log("result : "+MClass.number.ToString());

}

}


/*

delegate void dele1();

delegate int dele2(int a, int b);

public class DelegateScript : MonoBehaviour {

public class MathClass

{

public void Intor()

{

Debug.Log("Start the Intro~");

}

public int Sum(int a, int b)

{

return a+b;

}

}

// Use this for initialization

void Start () {

MathClass mclass = new MathClass();

dele1 intro = new dele1(mclass.Intor);

dele2 sum = new dele2(mclass.Sum);

intro();

int result = sum(-10, 90);

Debug.Log("result : " + result.ToString());

}

// Update is called once per frame

void Update () {

}

}

*/



c#은 모두 콜백이.. 레퍼런스로 사용가능하다.
delegate는 한사람만 사용해야 한다. 또는 자기것만 사용한다.
결과는 run해서 consol에서 확인 가능하다.
result : 100
UnityEngine.Debug:Log(Object)
DelegateTest:Start() (at Assets/Scripts/DelegateTest.cs:41)
result : 200
UnityEngine.Debug:Log(Object)
DelegateTest:Start() (at Assets/Scripts/DelegateTest.cs:50)




내일은 슈팅게임을 다시 만든다..
2D슈팅게임 한다.
마무리하고 보강한다..

캐릭터한다.

오늘은 3D로 움직인다.라는 것.











 실전 Unity3D Engine Programing 과정 2일차 (2013.07.23 (화))


금일 만든 파일: 

shooting.unitypackage




툴에대해서

히어락키

카메라

인스팩터 : 설명, 폴더 (중요하지 않다.)

플레이/좌우 버튼.


창을 새로 끄낼경우.. 우측 상단에 Layers 버튼으로 추가할 수 있다.

Layers는  WIde로 하자..



슈팅게임 만들자..

프로젝젝트 하단 폴더가 비어 있습니다. 오른쪽 클릭, 옵션 메뉴 

크리에터 / 폴더

- Scripts 만든다.

- Materials

- Prefabs




포커싱의 의미

 히어아키에서 카메라를 선택해서 카메라가 중앙에 오도록 한다.

- 카메라가 중앙에 있으면 이상태에서 개발하면 좋다.

- 0 1 1.0

- 이상태 Menu-> GameObject -> Create Empty하면 카메라 것과 동일하게 생긴다.

- 이 GameObject 클릭해 보면 카메라와 동일하다. 포지션이


카메라 선택하고 포지션은 0.0.0으로 한다.





프로젝션은 Othographic를 선택한다.

신창  Alt + 마우스로 카메라를 옯길 수 있다. 휠키로 줌 처리



카메라 신창의 박스를 조절하자.

Size : 5

ClippingPlanes

Near -2, Far 2




Menu GameObject Create Other Cube를 선택한다. 




히얼아키에 Cube가 생기고

신탭 옆에 Game탭을 눌러보면 검은색이 생겨 있을 것이다.

검은색 박스가 안보이면  포지션을 0.0.0 으로 한다.

그리고 이름은 Cube -> Player로 변경한다.




-5   5  5  -5 크기의 박스에서 중앙에 플레이어가 있게 한다.

플레이어 중앙으로 옮기기 위해서 -4한다.



Menu GameObject Create Other Direct Light






Assets에서  Meterial -> Create -> Meterial 이름은 PlayerMat 로 지정한다.

이것을 선택하면 좌측에 Main Color를 녹색으로 바꾼다.






Player를 선택하고 

프로젝트에 PlayerMat를 끌어다가 오른쪽 인스팩토리리에 옮겨 놓으면 Player는 녹색된다.





코딩해 보자

프로젝트의 scripts > create > C# script > 이름은 PlayerControl





이 PlayerControl을 누르면 모노디벨롭이 뜬다. 여기서 코딩한다.






파일 이름과 클래스 이름이 동일하다. <== 매우 중요 틀리면 컴파일 안됨

클래스이름이 스크립의 해더가 된다. 


using UnityEngine;

using System.Collections;


public class PlayerControl : MonoBehaviour {


// Use this for initialization

void Start () {

}

// Update is called once per frame

void Update () {

}

}


열어 보면 MonoBehaviour를 상속 받는다.

start: 이니셜

update: 여기서 그려진다.



using UnityEngine;

using System.Collections;


public class PlayerControl : MonoBehaviour {

public float Speed = 1.0f;

// Update is called once per frame

void Update () {

}

}


이렇게 수정하고 


하이어라키에서 Player를 선택하고 프로젝트에서 PlayerControl를 잡아서 오른쪽으로 옮겨 놓는다.

그러면 speed가 생긴다. Public으로 해서 나오는 것이다. (디자이너에게 수정할 때 이렇게 해 주면 좋다.) 


코드를 아래와 같이 수정해 보고

void Update () {

float moveAmt = Input.GetAxis ("Horizontal") * Speed * Time.deltaTime;

transform.Translate(Vector3.right * moveAmt);

}



플레이 버튼을 누르고 키보드로 좌우를 눌러보면 녹색박스가 좌우로 움직인다.

그리고 인스팩트에서 스피드를 1 -> 10으로 변경해 본다. 그리고 움직이면 움직인다.

플레이중에 스피드를 변경하면 저장안된다. 



저장


File Save Scene 

File Save Project

한다. (play는 스톱해야 한다.)


Edit -> Project Setting -> Input 를 누르면 우측 인스팩터에 Axes가 나온다..

이중 Negative Button left, Positive Button은 Right

따라서 좌우 키를 누르면 코드상에 음수 값 양수값이 GatAxis에서 값이 들어오게 되는 것이다.

경과시간을 곱한다..

업데이트 타임은 deltaTime을 사용한다. (타임의 종류도 많다.)

float moveAmt = Input.GetAxis ("Horizontal") * Speed * Time.deltaTime;


transform.Translate(Vector3.right * moveAmt);

인스팩트에 있는 것으로 숏컷 개념으로 바로 사용하고 

transform 해당 항목을 정의 하면 해당 항목과 링크를 걸어 주는 것이다.


Vector3 는 아래와 같은 방향을 제공한다.

방향은 

업다운

레프트라이트

프론트백




이제 총을 쏴보자..

GameObject create other Shpere

positon은 0.0.0

이름은 Bullet이다.

그리고 메터리얼 만들고 색깔 바꾸고 Bullet에 선택하고 드래그 드럽한다.


스크립터 한다.

Bulletcontrol을 만든다.

내용은

using UnityEngine;

using System.Collections;


public class BulletControl : MonoBehaviour {


public float Speed = 20f;

// Update is called once per frame

void Update () {

float moveAmt = Speed * Time.deltaTime;

transform.Translate(Vector3.up * moveAmt);

}

}




콘솔창을 띄우다.

Window >  console

진행과정을 볼 수 있다.


하이얼라키에서 Bullet을 선택하고 BulletControl을 마우스로 잡아서 오른쪽으로 드래그 드랍한다.

플레이 한다. 


총알 올라가는 것을 제약해야 한다. 

if(transform.position.y > 7.0f){

Destroy(gameObject);

}

gameObject와 같이 소문자는 자기로써.. 삭제된다는 것이 GC전까지..


Assets > Prefabs > Create Prefa  이름 BulletPrefab

만든 Bullet 오브젝트를 끌어서 여기에 저장한다. 

그리고  Bullet는 삭제한다. 



스페이스바에서 총을 쏴보자.

PlayerControl에서 아래 것을 추가한다.


public GameObject bullet;

public GameObject bullet;

void Update () {

float moveAmt = Input.GetAxis ("Horizontal") * Speed * Time.deltaTime;

transform.Translate(Vector3.right * moveAmt);

if(Input.GetKeyDown(KeyCode.Space)){

Instantiate(bullet, transform.position, transform.rotation);

}

}



Player

None 한번도 링크를 건적이 없다.

Missing 링크 걸고 지운것


Prefabs에서 BulletPrefab을 선택해서 마우스로 끌어서 오른쪽에 스크립의 Bullet 항목에  드랍한다.


그리고 플레이 시켜서 스페이바를 눌러 본다.









[적군을 만들자]

적군 만들기: GameObject > Create Other > Cube 이름은 Enemy

적군 색상바꾸기: Materials  Create > Metrial 이름은 EnemyMat 색은 빨간색

Enemy선택해서 EnemyMat를 잡아서 오른쪽에 둔다.

적군 코드를 넣다. : Script Create > C# script 이후 코딩한다.


using UnityEngine;

using System.Collections;


public class EnemyConrol : MonoBehaviour {


public float Speed = 8.0f;

// Update is called once per frame

void Update () {

float moveAmt = Speed * Time.deltaTime;

transform.Translate(Vector3.down * moveAmt);

if(transform.position.y < -7.0f){

// 하단까지떨어지면  다시 상단으로간다 

InitPosition();

}

}

void InitPosition()

{

// 죽으면 상단에 x값을랜덤하게새로생성된다 

transform.position = new Vector3 (Random.Range(-5.0f, 5.0f), 7.0f, 0.0f);

}

}




Prefabs를 선택해서 우측 상단에 Tag를 추가한다. 

선택하면 TagManager가 생긴다.

* 총알은 계속 발사하면, 또는 벽 등으로 구분하기 위해서는 클론된 것들을 Tag로 구분한다.


Tags  선택하고 Element에 bullet를 입력한다.



[충돌체크를 넣는다.]

다시 Prefabs에서 BulletPrefab을 선택하고 오른쪽에


BulletPrefab에 RidgBody를 붙이고 중력값을 큰다.

Menu > Componet > Physics > RigidyBody를 선택한다.

그리고 Sphere Colliderd의 Is Trigger를 체크한다.

<== 이제 물리엔진에 대한 속성을 갖게 된다.


다시 EnemyControl로 이동한다.

//인자로  충돌된 것이날라온다 충돌시자동으로온다 in trigger를체크할경우

void OntriggerEnter(Collider other)

{

if(other.gameObject.tag == "bullet")

{

// 충돌 오브젝트의 테그를보고 총알인지   벽인지 구분한다 

InitPosition();

}

}



[이팩트]

이제 충돌시 이팩트를 넣어 보자.. 

간단한 제공하는 것으로 레가시로 하자.


Assets > Import Package > Particle > 팝업에서 Import버튼을 누른다. 그러면 Assets에 Standard Assets이 추가된다.

여기서 Legacy Particle > Small explosion이 있다. 

이것을 끌어서 히얼아키창에 드레그 드랍한다.


이것을 One Shot을 체크한다.

- 한번 노출 되었다 사라진다.


파티클 사이즈와 에니메이션 몇개 노출될 것인가를 지정ㅎ낟.

Emit

MiknSize

Max Size

..

Max Emission


Rnd Velocity를 3.3.3d으로 변경


다시  Prefabs Explosion을 만들고

Small expiosion을 드레그 드랍으로 끌어 넣고 지운다. 



[사운드 만든다.]

인터넷을 열어서 구글 에서 

http://www.drpetter.se/project_sfxr.html 이동한다.

http://www.drpetter.se/files/sfxr.zip

sfxr.zip을 다운받는다. 

압축을 풀고 실행해서 원하는 사운드를 골라서 저장한다. (붉은색 박스의 버튼으로 저장할 수 있다.)



Assets  에서  Audios폴더 선택하고 오른쪽 마우스 > Import New Asset 으로 파일하나씩 불러온다.

꼭 이 방식으로 한다. 같은 이름을 임포트하지 않기에 프로젝트 뻑나지 않게 한다.

파일 익스플로러로 해당 파일에 옮기지 말것 <=== 매우 중요함

audio show in folder로 익스플로러를 열어 볼 수 있다. 여기다 직접 넣으면 안됨.. ** 


player 오브젝트를 선택하고  Menu > Componet > Audio > Audio source를 선택한다. 

오른쪽을 보면 Audio Source생긴다

audo clip에 audios  파일을  여기다 끌어 놓는다.

play on awake를 체크를 언체크한다. 생성시 계속 출력하기에



스크립트에 코드 추가

//인자로  충돌된 것이날라온다 충돌시자동으로온다 in trigger를체크할경우

void OntriggerEnter(Collider other)

{

if(other.gameObject.tag == "bullet")

{

audio.Play();

Instantiate(explosion, transform.position, transform.rotation);

// 충돌 오브젝트의 테그를보고 총알인지   벽인지 구분한다 

InitPosition();

}

}



prefabs에서 explosion을 선택하고 audio source를 추가한다.

오디오 파이를 오디오 클립에 넣어 준다.

그리고 play on awake를 체크한다. 한번만 출력하게..



Enemy에도 오디오 소스를 추가하고 오디오를 오디오클립에 넣어 준다.


유니티는 3D 사운드를 제공해서 카메라 위치에 따라서 다르게 들린다.

Main Camera에 Audio Listener로 한개의 리스너만 있다. 카메라를 추가해도 메인카메라에 리스너 하나만 있어야 한다.

한신에 오디오리스너는 한개만 존재해야 한다. 


[스코어 추가하자.]

메인하나 추가하고 스코어를 추가한다.

히어아키 GameObject > Create GameObject 이름은 main game object


스크립트 추가한다. Main

using UnityEngine;

using System.Collections;


public class Main : MonoBehaviour {


static public int Score = 0;

static public int Lives = 3;

// Update is called once per frame

void Update () {

}

// 느려터치 유니티 GUI 거의안씀

void OnGUI() 

{

GUI.Label(new Rect(10.0f, 10.0f, 200.0f, 20.0f),"Score : ", Main.Score.ToString());

GUI.Label(new Rect(10.0f, 10.0f, 200.0f, 20.0f),"Lives : ", Main.Lives.ToString());

}


MainGameObject에 Main 스크립트 추가한다.


그리로 블랫에 코드를 추가해서 변경


New Scene을 만든다.

엠프티 오브젝트 추가 WinGameObject를 만든다. 

스크립트 단다. WIn

using UnityEngine;

using System.Collections;


public class Win : MonoBehaviour {



void OnGUI()

{

if(GUI.Button(new Rect(Screen.width/2-100.0f, Screen.height/2-100.0f, 200.0f, 200.0f),"YouWin!"))

{

//버튼눌러지면이곳으로들어온다

Application.loadedLevel("game");  // main scene이노출된다

}

}

}


winGameObject에  win  스크립트를 추가하고

menu save scene을 눌러서 win 으로 저장한다.

Assets을 보면 추가된 것을 볼 수 있다. 


Menu > File > Game Setting > 두개의 신을 추가하다. game, win  순서대로



using UnityEngine;

using System.Collections;


public class Main : MonoBehaviour {


static public int Score = 0;

static public int Lives = 3;

public int FinishScore = 1000;

// Update is called once per frame

void Update () {

if(Score >= FinishScore)

{

Score = 0;

Application.loadedLevel("win");

}

}

// 느려터치 유니티 GUI 거의안씀

void OnGUI() 

{

GUI.Label(new Rect(10.0f, 10.0f, 200.0f, 20.0f),"Score : "+ Main.Score.ToString());

GUI.Label(new Rect(10.0f, 30.0f, 200.0f, 50.0f),"Lives : "+ Main.Lives.ToString());

}

}





내일은 모델, 폴리곤, UI를 붙인다. 드레곤플라이트를 만든다. 배경 만든다.

케릭터, 전투, 이팩트, 채팅, 슈팅,  네트워크해서 드레곤플라이트를 만든다.



에너미를 복사해서 위치를 바꿔주면 여러개의 적이 나온다.




숙제..

1. 플레이어도 죽자. <= 리니지 바디를 붙인다. 

2. 아이템 박스 (스코어를 올려준다.) <= 큐브 만들면 된다. 

3. 아이템 박스 먹으면 다양한 삼지창 방향으로 총이 나가게 한다. <= 방향을 바꿔주면 된다.

4. 스테이지 늘리기 <= 옵션

5. 보스 <= 옵션





실전 Unity3D Engine Programing 과정 1일차 (2013.07.22 (월))


[강사 소개]

국민대학교 게임 교육원 주세영교수  


프리랙 책 집필중

주간에는 게임회사팀장

야간과 주말은 교육장


HG Entertainment -  캐쥬얼슈팅

엔사 - 소셜온라인

CCR - MMORPG

현 GameLoft - Web 3D온라인

현 국민은행 게임 교육원 교수



 GameLoft / 4000명 1006개국.. 급여는 국내 테이블

- 시니어스튜디오 유명한 게임을 많이 만든 스튜디오를 지칭한다.



만든 게임은

페이스북 게임

MMORPG

데이트 온라인 /  SNS 게임

싱크 건담 / 200개 팀대팀전..


서버클라이언 6주 개발

캐릭터 폴리곤 500개.. 

RocketRacer


게임 난이도

온라인 FPS

대전게임

레이싱게임


노리공방

10개월 




[강의 순서]

1. 게임 개발에 대해서

- 패키지, 온라인, 외국계회사 등등의 개발 패턴

- 웹게임과 3D 게임개발

- Unity3D로 만드는 게임

2. Unity3D

- Unity3D

- Unity3D로 만들어진 게임

3. 실무에서의 Unity3D

4. Project - Unity3D

5. 실무에서 유용한 팁과 노하우들



1. 게임 개발 패턴

게임은 어떻게 개발하나


패키지 게임의 게임 개발 패턴

- 스크럼 방식을 주로 사용

- 기획을 중간에 바꾸지 않는다. (수정사항은 다음 게임에)

- 마감은 정확하게 하지만 여유 있는게 포인트

- 업무부하 협동해결제도


-패치는 최후의 수단, 반드시 버그 없는...


온라인 게임의 게임 개발 패터

1. 컨셉기획

2차 

3차


4차 CBT

 5차 검증 및 수정

6차..



제작기간이 길다.


외국계회사의 게임 개발 패턴

GameLoft - 모방과 신속개발이 모토

T모다 - 창의성과 재미있는 게임이 모토


웹게임젲가의 노하우


...


3D rpdla ..


2.Unity3D 

쉽고 빠른 게임 개발 엔진......

엔진은 개발에 필요한 것을 모아둔것...


Componet Centric 엔진 <== 가장 중요한 용어 굉장히 중요한 개념이다.

그레고리  - 게임 엔진 아키텍쳐

유니티는 기존엔진과 많이 다르다.

C++은 탈것 하나만 정의 하면 된다...

- 한번 객체로 설계된 이후 변경이 발생되면 수정이 어렵게 된다.


컨포넌트 센트릭은

어떤 오브젝트의 속성을 정의한다.. 

각 오브젝트의 조합을 해서 기능을 만든수 잇다..

아무것도 오브젝트 하나 만들고 아무 기능이 없다..

이 것이 사람이라 정의 하면, 밤이란 환경을 만나면 잔다... 

어떤 특정 환경에 만나게 되면 기능이 생긴다. 

특정 지역에서 특정 기능을 갖게 된다.

이렇게 하면 확장성이 무긍무진하다.


특성을 모아둔것...


따라서 기획...이 바꿔야 한다..

기능정의를 하고 이 기능이 모여진 것이 사람이 될 수 있고, 적군이 될 수 있다.. 등...

기능만 합치면 무긍무진하다...



 Actor centric (언리얼 게임 엔진)

<- 컨포넌트 센트릭 의 반대 개념..

<- 기능을 다 만들어 놓고 On/Off처리..


컨포넌트센트릭은

- 기능중심의..........


슈팅, 모델링, NGUI, 2D, 네트워크, 에니메이션 케릭터,  3개월과정

슈팅, 디펜스, RPG, LOL 6개월과정



컨포넌트로 자유롭게 게임 오브젝트를 구성 가능하다...


유니티에서 컴포넌트?

트렌스콤포넌트



3D모델


게임 오브젝트.

- 컴포넌트를 담는 그룻


게임환경


유니는 쉽게 접근이 가능하다. 시간화, 실시간 확인 가능

멀티플랫폼...




Programmer -> GraphicArtist

자신이 만든 산출물이 게임에서 어떻게 나올지, 우리 엔진에서

어떻게 보이는 지 궁금한 것이 당연한 아티스트들

그래픽 아티스트가 쉽게 확인가능하기에 책임을 진다.


큰프로젝트일수록 어마어마한 데이터 데이틀(XML, Excel, Orginal-Format)

프로젝트 후반일 수록 개발에 큰 영향을 미치게 된다.


데이터 관리툴...

사고 방지를 위해 데이터 검증 및 관리 툴을 제작

하지만 검증툴 개발 우선순위느..



유니티는

엑셀 -> XML -> 서버, 클라이언트 데이터로 생성된다.



툴을 이용하면 레벨생성기를 제공한다.



Project Issue

- Engleish Spelling Issue

- Debugging

- Edit


리소스에 스펠링이 달라서 문제가 생길 수 있다.

Missile = mesail meesail, misail mysail


PrePop

리소스 값의 모음으로 프리팝을 통해서 사용하면 좋다. 권장한다.



Proejct Editor / 


유니티는 디버깅이 안된다..

브레이크가 안된다.. 메모리가 세고 프로그램 뻣는다.

로그 메시지는 알기 쉽게..

Debug.Log, LogWarning, LogError

왜냐 하면 코어엔진은 C++이지만, 나머지는 닷넷으로 되어 있다..


버전관리툴은 사지도 마라 문제가 많다.

토토즈, 포포즈 사용해라.


Edit and Edit, Again Edit

값을 외부로 뽑아서 지정할 수 있게 해 준다.


기타..

리팩토링, 스크립트는 80라인을 이내로 해야 한다. 

Simple is Best


여기서 정의 하는 클래스는 함수라고 생각해야 한다.

함수하나가 스크립터이다. 기능하나이다.


3.5.7F가 가장 안정적이다. (4.0은 최신 버전으로 써라)


10년된 엔진이지만 최근 5년에 발전했다.


5. Tip

GUI가 매우 느리다.

GUI가 하나 하나 오브젝트로써 이벤트를 갖는다.

이GUI, NGUI 등을 추가로 판다.

멀티스크린UI





www.unifycommunity.com/wiki/index.php?title=main_page



어셋에 좋은 것 판다.


스케일


스케일 팩터 0.01 로 변경된다. 스케일은 변경하면 안된다.

익스포트..것


PSD  파일을 지원한다. 

지원하지만 레이어를 제공하기에 메모리용량이 크기에 게임에서는 쓰지 마라..



메타파일

기획, 디자인, 개발 모두 유니티3D를 사용해야 한다.

한프로젝트르 모두 사용하기에 오히려 위험할 수 있다..

리소스에 메타파일을 기록되어 있다.

메타파일은 수정 이력을 기록되어 있어 리소스와 함께 save해야 한다.


4시간 30분 인포트하는 상황이 생길 수 있다.


리소스 추가 시 메타파일을 같이 추가해야 한다. 


save하고 메타파일과 같이 올려줘야 한다.


메타파일 정말 중요하다... (꼬리면 2일 이상 리로드할 수 있다.)




텍스쳐 형식변경

2의 승수이어야 한다.

change Non Power of 2 (NPOT Gsize)

이미지 넣으면 깨진다.....



라이트맵...

다이렉트 스포트... 파일

다이렉트 텍스쳐이다. 맥에는 이 파일을 못읽는다.

멀티플랫폼시 필히.. 라이트맵은 맥에서 빌드해야 한다. 맥용 버쳘 텍스쳐 포맷으로 나온다. (윈도우 것으로 하면 깨진다.)


폰트..

볼드등은 지원하지 않았지만 4.0에서 지원한다 느리다..



프리로딩..

프리팝.. 그때 그때 화면에서 읽는다. 이러면 화면이 뚝뚝 끈긴다..

만들어서 우주밖으로 보내 둔다...


유니티는 생성은 하고 삭제는 하지 않고 GC가 삭제한다. 

GC가 호출될 때 게임이 끈기는 현상이 생길 수 있다..

GC를 임의로 호출하는 방법도 있다..

프로파일과 사용하는 방법을 제공할 것이다...



게임 개발사 입장에서 유니티 한계


Low Layer 커스트마이징 안된다.

프로젝트만의 툴 커스터마이징이 안된다.

멀티 플랫폼이지만 지원 플랫폼에 따라 복잡도가 증가하는 건 진리

그지 같은 디버깅...



멀티플랫폼 제공하지만 각 플랫폼에 맞게 적용해야 하는 어려움이 있다.


리소스 최적화 방법

플랫폼 별 텍스쳐 크기 지정

텍스쳐 파일 포맷은 최대한 동일하게

공유 매트리얼을 쓰면 좋다.

폴리곤은 압축해서

애니메이션은 적을 수록 좋다.



2033

2048 아이폰 한번에 읽는 량은 50MB만 읽는다..

1024 x 1024  안드리이드





멀티 플랫폼 개발

다른 엔진의 멀티 플랫폼

유니티 엔진에서 멀티 플랫폼

각 플랫폼별 아셋을 변경하기에 오래 걸린다.. 

3개의 버전별로 PC를 준비해서 빌드한다...


N스킬ㄴ 대응

UI윛 보정

마우스 터치스크린 

아이폰은 5개..  아이패드는 11개도 터치 되낟. 넥서스원 2개 최근 안드로이드는 8개..


가속도계중력..


프레임 확보를 위해...(최소프레임은 21 프레임 이상 나와야 한다.)

LOD는 동적 생성보다 GameObject On/Off활용 

Culling은 적극적으로 활용해라 / 카메라 컬링을 해 주며 좋다. 

Shader의 남용 금지

Mesh Merging / 캐릭터의 커스터마이징하다보면 문제가 많은데 메쉬 머지로 하면 드로컬이 하나로 된다.

충돌체크 간략화  / 충돌 위치 파악을 위해서 레일을 쓰면 느리다.. 7프레임 정도 겹치는 형태로 해야 하면 좋다.

Light는 최소화 / 모바일에서는 동작 라이트는 절대 사용하지 마라. 생성해서 사용하지 마라..라이트수만큼 드로컬이 2배씩 증가된다.

Mipmap반드시 켜라 / 

UI리소스는 2의 승수로 만드는 것이 좋다.

애니메이션 최소화

폴리곤 최소화

빌보드는 Shader로 할 것....



웹기만 

구글 웹스토는 


유니티는 다양곳에 사용한다.

- 교육용 40%

- 영화 20%

- 3D화면 제어 


3D GPS, SI솔루션 AR소프트웨어 매장관리 프로그램 키오스크 용 소프트웨어 3D아바타 메신저















API 11에서 제공하는 팝업 메뉴

메뉴에 서브메뉴가 가능해서 기존 노출되는 팝업 메뉴가 사라지고 서브메뉴가 나온다.


public void onPopupButtonClick(View button) {

    PopupMenu popup = new PopupMenu(this, button);

    popup.getMenuInflater().inflate(R.menu.popup, popup.getMenu());


    popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {

        public boolean onMenuItemClick(MenuItem item) {

            Toast.makeText(PopupMenu1.this, "Clicked popup menu item " + item.getTitle(),

                    Toast.LENGTH_SHORT).show();

            return true;

        }

    });


    popup.show();

}


메뉴 xml

<?xml version="1.0" encoding="utf-8"?>

<!-- Copyright (C) 2010 Google Inc.


     Licensed under the Apache License, Version 2.0 (the "License");

     you may not use this file except in compliance with the License.

     You may obtain a copy of the License at


          http://www.apache.org/licenses/LICENSE-2.0


     Unless required by applicable law or agreed to in writing, software

     distributed under the License is distributed on an "AS IS" BASIS,

     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

     See the License for the specific language governing permissions and

     limitations under the License.

-->

<menu xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:id="@+id/search"

          android:icon="@android:drawable/ic_menu_search"

          android:title="@string/popup_menu_search" />

    <item android:id="@+id/add"

          android:icon="@android:drawable/ic_menu_add"

          android:title="@string/popup_menu_add" />

    <item android:id="@+id/edit"

          android:icon="@android:drawable/ic_menu_edit"

          android:title="@string/popup_menu_edit">

        <menu>

            <item android:id="@+id/share"

                  android:icon="@android:drawable/ic_menu_share"

                  android:title="@string/popup_menu_share" />

        </menu>

    </item>

</menu>






'Android > ApiDemo_Graphic' 카테고리의 다른 글

View/Full Screen Modes / Hide Navigation  (0) 2012.12.29

화면에 동적으로 제어하는 코드로써
특징적인 것은 Navigation을 숨기는 코드가 들어 있다.

네비게이션을 옮기는 설정은 SYSTEM_UI_FLAG_HIDE_NAVIGATION인데, 아래와 같은 사항에 주의해서 사용해야 하네요.

SYSTEM_UI_FLAG_HIDE_NAVIGATION은  FLAG_FULLSCREEN와 함께 사용해야 제대로 동작된다고 합니다.

Android 4 treat: View.SYSTEM_UI_FLAG_HIDE_NAVIGATION

"View has requested that the system navigation be temporarily hidden. This is an even less obtrusive state than that called for by SYSTEM_UI_FLAG_LOW_PROFILE; on devices that draw essential navigation controls (Home, Back, and the like) on screen, SYSTEM_UI_FLAG_HIDE_NAVIGATION will cause those to disappear. This is useful (in conjunction with the FLAG_FULLSCREEN and FLAG_LAYOUT_IN_SCREEN window flags) for displaying content using every last pixel on the display. There is a limitation: because navigation controls are so important, the least user interaction will cause them to reappear immediately."

This flag will allow to take over the entire screen, unlike Honeycomb where there was always a navigation bar at the bottom with room for the Back, Home, and Recents button. Unfortunately it won't work for games because of the last sentence.




/*
 * Copyright (C) 2011 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.example.android.apis.view;

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.Window;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.TextView;

import com.example.android.apis.R;

/**
 * This activity demonstrates some of the available ways to reduce the size or visual contrast of
 * the system decor, in order to better focus the user's attention or use available screen real
 * estate on the task at hand.
 */
public class OverscanActivity extends Activity {
    public static class IV extends ImageView {
        private OverscanActivity mActivity;
        public IV(Context context) {
            super(context);
        }
        public IV(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
        public void setActivity(OverscanActivity act) {
            mActivity = act;
        }
        public void onSizeChanged(int w, int h, int oldw, int oldh) {
            mActivity.refreshSizes();
        }
        public void onSystemUiVisibilityChanged(int visibility) {
            mActivity.getState().onSystemUiVisibilityChanged(visibility);
        }
    }

    private interface State {
        void apply();
        State next();
        void onSystemUiVisibilityChanged(int visibility);
    }
    private class NormalState implements State {
        public void apply() {
            display("Normal");
            setFullscreen(false);
            mImage.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
        }
        public State next() {
            return new FullscreenState();
        }
        public void onSystemUiVisibilityChanged(int visibility) {
        }
    }
    private class FullscreenState implements State {
        public void apply() {
            display("FULLSCREEN");
            setFullscreen(true);
        }
        public State next() {
            return new FullscreenLightsOutState();
        }
        public void onSystemUiVisibilityChanged(int visibility) {
        }
    }
    private class FullscreenLightsOutState implements State {
        public void apply() {
            display("FULLSCREEN + LOW_PROFILE");
            mImage.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE);
        }
        public State next() {
            return new OverscanState();
        }
        public void onSystemUiVisibilityChanged(int visibility) {
        }
    }
    private class OverscanState implements State {
        public void apply() {
            display("FULLSCREEN + HIDE_NAVIGATION");
            mImage.setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
        }
        public State next() {
            return new NormalState();
        }
        public void onSystemUiVisibilityChanged(int visibility) {
        }
    }

    private void setFullscreen(boolean on) {
        Window win = getWindow();
        WindowManager.LayoutParams winParams = win.getAttributes();
        final int bits = WindowManager.LayoutParams.FLAG_FULLSCREEN;
        if (on) {
            winParams.flags |=  bits;
        } else {
            winParams.flags &= ~bits;
        }
        win.setAttributes(winParams);
    }

    private String getDisplaySize() {
        DisplayMetrics dm = getResources().getDisplayMetrics();
        return String.format("DisplayMetrics = (%d x %d)", dm.widthPixels, dm.heightPixels);
    }
    private String getViewSize() {
        return String.format("View = (%d,%d - %d,%d)",
                mImage.getLeft(), mImage.getTop(),
                mImage.getRight(), mImage.getBottom());
    }
    void refreshSizes() {
        mText2.setText(getDisplaySize() + " " + getViewSize());
    }
    private void display(String text) {
        mText1.setText(text);
        refreshSizes();
    }
    State getState() {
        return mState;
    }

    static int TOAST_LENGTH = 500;
    IV mImage;
    TextView mText1, mText2;
    State mState;

    public OverscanActivity() {
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // we need to ask for LAYOUT_IN_SCREEN before the window decor appears
        Window win = getWindow();
        WindowManager.LayoutParams winParams = win.getAttributes();
        winParams.flags |= WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
        win.setAttributes(winParams);

        setContentView(R.layout.overscan);
        mImage = (IV) findViewById(R.id.image);
        mImage.setActivity(this);
        mText1 = (TextView) findViewById(R.id.text1);
        mText2 = (TextView) findViewById(R.id.text2);
    }

    @Override
    public void onAttachedToWindow() {
        mState = new NormalState();
        mState.apply();
    }

    @Override
    protected void onResume() {
        super.onResume();
    }

    public void clicked(View v) {
        mState = mState.next();
        mState.apply();
    }
}














'Android > ApiDemo_Graphic' 카테고리의 다른 글

View PopupMenu  (0) 2012.12.29
런처 아이콘 사이즈 Android 2012. 12. 3. 15:13

Size and Format


Launcher icons should be 32-bit PNGs with an alpha channel for transparency. The finished launcher icon dimensions corresponding to a given generalized screen density are shown in the table below.

Table 1. Summary of finished launcher icon dimensions for each generalized screen density.

ldpi (120 dpi)
(Low density screen)
mdpi (160 dpi)
(Medium density screen)
hdpi (240 dpi)
(High density screen)
xhdpi (320 dpi)
(Extra-high density screen)
Launcher Icon Size36 x 36 px48 x 48 px72 x 72 px96 x 96 px

You can also include a few pixels of padding in launcher icons to maintain a consistent visual weight with adjacent icons. For example, a 96 x 96 pixel xhdpi launcher icon can contain a 88 x 88 pixel shape with 4 pixels on each side for padding. This padding can also be used to make room for a subtle drop shadow, which can help ensure that launcher icons are legible across on any background color.


http://developer.android.com/guide/practices/ui_guidelines/icon_design_launcher.html

아직 정확히 어떤 기능을 하는지 확인되지 않고 있음

android:freezesText

If set, the text view will include its current complete text inside of its frozen icicle in addition to meta-data such as the current cursor position. By default this is disabled; it can be useful when the contents of a text view is not stored in a persistent place such as a content provider.

Must be a boolean value, either "true" or "false".

This may also be a reference to a resource (in the form "@[package:]type:name") or theme attribute (in the form "?[package:][type:]name") containing a value of this type.

This corresponds to the global attribute resource symbol freezesText.

android:inputType

The type of data being placed in a text field, used to help an input method decide how to let the user enter text. The constants here correspond to those defined by InputType. Generally you can select a single value, though some can be combined together as indicated. Setting this attribute to anything besides none also implies that the text is editable.

Must be one or more (separated by '|') of the following constant values.

ConstantValueDescription
none0x00000000There is no content type. The text is not editable.
text0x00000001

멀티라인을 지원하지 않아서 키보드에서 Enter 키 입력이 무시된다.
Just plain old text. Corresponds to 

TYPE_CLASS_TEXT |TYPE_TEXT_VARIATION_NORMAL.
textCapCharacters0x00001001Can be combined with text and its variations to request capitalization of all characters. Corresponds to TYPE_TEXT_FLAG_CAP_CHARACTERS.
textCapWords0x00002001Can be combined with text and its variations to request capitalization of the first character of every word. Corresponds toTYPE_TEXT_FLAG_CAP_WORDS.
textCapSentences0x00004001Can be combined with text and its variations to request capitalization of the first character of every sentence. Corresponds toTYPE_TEXT_FLAG_CAP_SENTENCES.
textAutoCorrect0x00008001Can be combined with text and its variations to request auto-correction of text being input. Corresponds to TYPE_TEXT_FLAG_AUTO_CORRECT.
textAutoComplete0x00010001Can be combined with text and its variations to specify that this field will be doing its own auto-completion and talking with the input method appropriately. Corresponds toTYPE_TEXT_FLAG_AUTO_COMPLETE.
textMultiLine0x00020001

문자 입력시 멀티 라인입력이 가능하다.
Can be combined with 

text and its variations to allow multiple lines of text in the field. If this flag is not set, the text field will be constrained to a single line. Corresponds to TYPE_TEXT_FLAG_MULTI_LINE.
textImeMultiLine0x00040001Can be combined with text and its variations to indicate that though the regular text view should not be multiple lines, the IME should provide multiple lines if it can. Corresponds toTYPE_TEXT_FLAG_IME_MULTI_LINE.
textNoSuggestions0x00080001Can be combined with text and its variations to indicate that the IME should not show any dictionary-based word suggestions. Corresponds to TYPE_TEXT_FLAG_NO_SUGGESTIONS.
textUri0x00000011Text that will be used as a URI. Corresponds to TYPE_CLASS_TEXT |TYPE_TEXT_VARIATION_URI.
textEmailAddress0x00000021Text that will be used as an e-mail address. Corresponds toTYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_EMAIL_ADDRESS.
textEmailSubject0x00000031Text that is being supplied as the subject of an e-mail. Corresponds toTYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_EMAIL_SUBJECT.
textShortMessage0x00000041Text that is the content of a short message. Corresponds toTYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_SHORT_MESSAGE.
textLongMessage0x00000051Text that is the content of a long message. Corresponds toTYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_LONG_MESSAGE.
textPersonName0x00000061Text that is the name of a person. Corresponds to TYPE_CLASS_TEXT |TYPE_TEXT_VARIATION_PERSON_NAME.
textPostalAddress0x00000071Text that is being supplied as a postal mailing address. Corresponds toTYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_POSTAL_ADDRESS.
textPassword0x00000081Text that is a password. Corresponds to TYPE_CLASS_TEXT |TYPE_TEXT_VARIATION_PASSWORD.
textVisiblePassword0x00000091Text that is a password that should be visible. Corresponds toTYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_VISIBLE_PASSWORD.
textWebEditText0x000000a1Text that is being supplied as text in a web form. Corresponds toTYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_WEB_EDIT_TEXT.
textFilter0x000000b1Text that is filtering some other data. Corresponds toTYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_FILTER.
textPhonetic0x000000c1Text that is for phonetic pronunciation, such as a phonetic name field in a contact entry. Corresponds to TYPE_CLASS_TEXT |TYPE_TEXT_VARIATION_PHONETIC.
textWebEmailAddress0x000000d1Text that will be used as an e-mail address on a web form. Corresponds to TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS.
textWebPassword0x000000e1Text that will be used as a password on a web form. Corresponds toTYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_WEB_PASSWORD.
number0x00000002A numeric only field. Corresponds to TYPE_CLASS_NUMBER |TYPE_NUMBER_VARIATION_NORMAL.
numberSigned0x00001002Can be combined with number and its other options to allow a signed number. Corresponds to TYPE_CLASS_NUMBER |TYPE_NUMBER_FLAG_SIGNED.
numberDecimal0x00002002Can be combined with number and its other options to allow a decimal (fractional) number. Corresponds to TYPE_CLASS_NUMBER |TYPE_NUMBER_FLAG_DECIMAL.
numberPassword0x00000012A numeric password field. Corresponds to TYPE_CLASS_NUMBER |TYPE_NUMBER_VARIATION_PASSWORD.
phone0x00000003For entering a phone number. Corresponds to TYPE_CLASS_PHONE.
datetime0x00000004For entering a date and time. Corresponds to TYPE_CLASS_DATETIME |TYPE_DATETIME_VARIATION_NORMAL.
date0x00000014For entering a date. Corresponds to TYPE_CLASS_DATETIME |TYPE_DATETIME_VARIATION_DATE.
time0x00000024For entering a time. Corresponds to TYPE_CLASS_DATETIME |TYPE_DATETIME_VARIATION_TIME.

This corresponds to the global attribute resource symbol inputType.


2012년 11월 22일 목요일

게임 개발자를 위한 유튜브 세미나 동영상


지난 11월 15일에 있었던 게임 개발자를 위한 유튜브 세미나의 동영상이 업로드되었습니다. 많은 분들이 참석해 주셔서 즐거운 시간을 보냈구요. 여러모로 유익한 시간이었습니다. 관심 가지고 참석 지원해 주신 분들, 참석해 주신 분들 그리고 동영상을 시청하시는 분들 모두에게 감사드립니다.

세미나 자료: https://docs.google.com/file/d/0B4sGAlXaZCXNUzdGRW94S1BoelE/edit

http://googledevkr.blogspot.kr/2012/11/blog-post_12.html

Published - 유튜브를 활용한 게임 확산 전략 (Start Playing The Distribution Game on YouTube) v00.03.01.pdf .pdf

2012년 11월 7일 수요일

게임 개발자를 위한 유튜브 소개 시간을 마련했습니다~

게임 개발자를 위한 유튜브 소개 시간을 마련했습니다. 여러분의 게임에 유튜브를 결합할 수 있는 다양한 방법들과 성공 사례들을 공유합니다. 그동안 유튜브를 이용자 관점에서 동영상 시청 용도로만 생각하고 계신 분들이 많으실 텐데 유튜브에서 제공되는 다양한 API들이 어떤 기능을 가지고 있으며 어떻게 이용될 수 있는지를 직접 확인하실 수 있습니다.

일시: 2012년 11월 15일 오후 6시 30분 ~ 9시
장소: 구글코리아 사무실

- 제목: Start Playing the Distribution Game on YouTube (Jarek Wilkiewicz, Sang Kim)
- 내용: YouTube has over 800M unique visitors who watch 4 billion hours of video each month. One of the top categories on YouTube is gaming. By integrating your game with YouTube you can share rich and authentic game experiences that are more likely to convert viewers into gamers than any other medium. In this session, we will highlight fun and enlightening integration examples in PC, console and mobile areas. We will cover best practices from both the technical and business perspective. Last but not least, we will share our favorite gameplay videos with you!

관심있는 게임 개발자 여러분들의 많은 참여 부탁드립니다~!

참고
- 저녁 식사가 제공됩니다.
- 본 세션은 영어로 진행되며 별도의 통역은 제공되지 않습니다.
- 주차는 제공되지 않으니 대중교통을 이용해 주시기를 부탁드립니다.

참가 신청하기! - 참가 신청은 11월 13일 오전 10시까지 부탁드립니다. 선착순이 아니며 좌석이 한정되어 있어 참석 대상자로 선정되신 분께는 별도로 연락을 드리겠습니다.

2012 구글 핵페어 구글 2012. 11. 20. 03:39


2012년도 11월 17일 ~ 18일 양일간 강남 CNN the biz 강남 교육 연수센터에서 개최 되었습니다.

Make Faire Seoul과 Google I/O로부터 영감을 얻어서 이런 행사를 만들게 되었다고 하네요. 일반인 24개팀, 학생 16개 팀 총 40개 팀이 참여해서 안드로이드, 크롬, Go, Dart, HTML5, 구글TV, NaCl 등의 기술을 이용해 만든 다양한 성과물을 전시하고 공유하는 행사였습니다. 

전시에 참여한 팀들은 넥서스7과 Google TV 디바이스를 제공하고 우수 프로젝트 팀에는 2013년도 구글 I/O에 참석할 수 있는 혜택을 준다고합니다.


[안드로이드 단말기에서 이미지 프로세싱]

얼굴바라기

내용: 
사람 얼굴을 인식하고 얼굴이 이동하는 방향으로 관절 기능(모터2개)사용해서 스마트폰을 움직이고 자동을 사진을 촬영해줌자동으로 사진이 찍히는 ICS 버전에서 제공되는 Face Detected 기술을 사용해서 재미요소 제공. 

사용기술:
ICS 이상에서 지원하는 얼굴인식 API, ADK(Accessory Development Kit) 사용ICS 이상 지원되는 얼굴인식 기능 사용. 얼굴인식에서 제공하는 사각형을 바탕으로 왕관이라 리폰을 오버레이뷰에 표시하기 

문제점:
얼굴인식 기능은 LG전자 모델에는 탑재되지 않았음, 삼성것은 잘 동작했음 
갤럭시 S3와 아두이노 인터페이스간에 데이터 손신이 발생되었음


Face Shape Tracking Library

내용:
1. 얼굴 위치를 따라다니는 공.
2. 특징점을 연결하여 실시간 얼굴 윤곽선 그리기.
3. 입모양 추적 -> 음식이 나올때만 먹기 게임.

사용기술:
Android, OpenCV, Active Shape Model, JNI, NDK

문제점:
얼굴인식의 세밀함이 떨어져 입모양의 변화등의 처리는 부족함, 머리모양의 특징이 인식되지 않음

향후방향:
오픈소스 공유를 통해서 lib화
http://code.google.com/p/face-shape-tracking-library-for-android/


[WebRTC 기술을 이용한 이미지 프로세싱]

MultipleVideoChat

내용: 
추가적인 플러그인 없이 브라우저간 P2P통신으로 멀티화상채팅 기능을 제공함

사용기술:
크롬, HTML5, WebRTC, AppEngine, Javascript

기타:
WebRTC는 크롬브라우저에 탑재되었으며, 이후 다른 브라우저에도 적용될 예정임

참고:
http://webrtc-multivc.appspot.com


반딧불이의 숲

내용: 미디어아트 분야로 웹캡에 들어오는 영상에 반응하여 움직이는 위치에 해당하는 들풀 이미지가 흔들리는 영상과 소리를 출력해 줌

사용기술: 크롬HTML5, WebRTC, WebGL, Web Audio(미적용)

참고: http://wwww.unid.me/demo/firefly http://github.com/epicure/expr/tree/master/demo/fireflies


[가속도센서, 자이로스코프센서, 지자게 센서]

밴드로이드 내용: 스마트폰의 다양한 센서를 통해 합주 기능을 제공합니다. 가속도 센서는 리코더, 자이로스코프센서는 드럼, 나침반센서로 원거리의 친구들과 합주하고 내용을 유투브로 공유할 수 있음

사용기술: Android, HTML5, Google Maps, App Engine, Go, Youtube

문제점: 나치반센서는 값에 제대로 감지 되지 않았다. 여러종류의 단말기 모두 불안정 값이 도출되어 제대로 구현이 안되었다.


Swing The Music

내용: 흔들어서 음악 재생을 제어한다. 앞으로 뒤로, 위아래로 흔들면 랜덤재생

사용기술: Android Multimedia, 센서

참고사항: http://android.googlesource.com/platform/packages/apps/Music.git (AOSP 음악 플레이어)
http://code.google.com/p/gm-player/  (코드페이지)


[Arduino(ADK), NFC등]

ALRAME

내용:
단말기에 알람을 설정하면 단말기와 알람시계간에 NFC로 쉽게 연결해서 알람 시간을 동기화 한다. 또한 알람은 서버에도 저장되어서 알람이 어디에서 울리고 끌경우 동기화된 기기들이 함께 동작된다. 알람시계는 아두이노에 네트워크 모듈을 연동해서 서버와 연결되어 있고 Pusher로 이벤트를 받는다. 단말기는 GCM으로 받는다. 함께 전시한 메일 프린터기도  소형프린터에 네트워크모듈이 탑재된 아두이노가 메일 수신을 서버로부터 Pusher로 받아서 내용을 프린터로 출력해 주는 기능을 했다.

사용기술:
Android, Arduino, GCM, NFC

참고:
https://github.com/huewu/alarme
http://youtu.be/WR_2pHxrIE4


Project AndroFace 

내용:
젤리빈 사탕 제공 기기 내부에 아두이노가 탑재되어 있고, NFC태그가 부착되어 있고, 넥서스S단말기와 연결되어 있었다. 단말기가 기기의 NFC태그 부분을 터치하여 인식이 되면 단말기가 아두이노에게 명령을 내리고 아두이노에 연결된 모터는 젤리빈 사탕은 한줌 제공한다. 

사용기술:
Android, ADK, NFC

참고:
https://github.com/yunsuchoi/androface


A3CCTV

내용:
아두이노의 근접센서를 사용해서 움직임을 감지하고 연결된 단말기에서 촬영하면 사진은 AppEngine에 등록하고 등록시점에 GCM과 메일로 통지해 주며 웹을 통해서 사진을 확인할 수 있음

사용기술:
Android, Arduino, App Engine, Google TV

참고:
http://youtu.be/D_6XOyZob6w


SGR(술고래-음주측정어플리케이션)

내용:
PC와 아두이노가 연결되어 있고 아두이노는 냄새를 감지하는 센서가 있어서 PC상에 웹에서 냄새 감지 버튼를 눌러서 냄새를 감지하고 관련 정보를 수치화해서 표시해 준다. 수치는 음주 측정기의 수치와 비슷하게 출력된다고 한다.

사용기술:

참고:


Braille Printer(점자 프린터)

내용:
웹에서 문자을 입력받아서 점자로 변환하고 PC와 연결된 아두이노는 모터와 연결되어서 점자를 프라스틱롤에 인쇄를 한다.

사용기술:

참고:


내용:

사용기술:

참고:


CINOW

내용:
습도, 온도 센서가 장착된 아두이노와 이것과 연결된 스마트폰이 해당 기능을 감지하고 GPS의 위치 정보를 모아서 트위터에 특정 해쉬태크와 함께 등록하고 웹 페이지에서 관련 정보로를 모아서 보여주고 지도상에 표시해 준다.

사용기술:

참고:
http://goo.gl/AYp03
http://dev.naver.com/projects/cinow


Anroid Robot with SNS

내용:
전화, 문자, SNS 내용이 수신되면 로보트의 팔과 머리로 알림기능을 한다. OTG를 통해서 마우스가 연결도 가능하다. 

사용기술:
Anroid, Arduino

참고:
http://youtu.be/37UmIh-En_g



[TV 관련 기술]

WebDMB

내용:
DMB 시청을 브라우저에서도 시청할 수 있게 하는 플러그인 기술 소개. 웹표준으로 정착되도록 노력중, 브라우저에 DMB 시청이 가능하다면, 브로그, 포털 등 쉽게 적용이 가능하기에 DMB의 새로운 수익원 창출이 가능할 것으로 예측함. 

사용기술:
Android, HTML5, Plugins

단점:
DMB는 H/W 모듈이 탑재한 단말기에서만 시청이 가능함

참고:
WebRTC와 같은 수준의 웹 표준을 지향하고 있음


U+ TV G

내용:
구글 TV가 탑재된 디바이스를 LG U+ 제공함. 한달에 9,900원으로 서비스를 제공함. 현재는 리모콘에 센서가 적용되어 있지는 않지만 내년도에 적용 예정, 영상은 HD급으로 제공, 일반 UI는 1280해상도 제공. 브라우저, 구글마켓(TV전용앱 노출), 지난방송 다시보기(일주일 지난 프로 무료제공)

사용기술:
Android 3.2, 구글 TV

참고:
디바이스는 구글 TV API가 기본 제공되며, 별도로 U+와 협약을 통해서 U+ 제공 API 사용이 가능함. 방송 시청중 방송안내 검색이 가능한 기능 제공


테즈메니아(Tasmanian)

내용:
스마트리모폰, 인체의 움직임을 감지해서 TV를 콘트롤하거나, 터치패드 이용한 TV 제어

사용기술:
google TV, 자체제작한 HW 리모콘, javascript, HTML5

애로사항:
Wii의 리모콘 기술을 흡사한 TV 리모콘을 제작하고 TV리모콘이 TV마우스포인터를 움직이게 하는 기술을 모토로 하였으나, TV 마우스 포인터 움직이 매우 어려웠다고 함

참고:
http://www.funzin.co.kr

[MIM] 

IRCTalk

내용:
IRC 클라이언트를 모바일환경으로 구현하여 다중채팅이 가능하며, 채팅 기록이 남아있어서 접속이 끈겼다가 재 접속되어도 이전 내용 확인이 가능하면 IRC 사용자면 별도의 친구 관계와 관계없이 장소에 구애받지 않고 접속이 가능하다. 단말기 뿐만 아니라 웹에도 채팅이 가능하다. 

사용기술:
Go, Android, Crome Extension, Google Account API, HTML5, Javascript, CSS, Websocket

참고:
https://github.com/irctalk



[NaCI]

VW

내용:
구글의 Native Client(NaCl) 기술과 Opengl ES 2.0 기술을 활용해서 크롭 브라우저상에서 3D 모델 데이터를 브라우징하는 데모

사용기술:
NaCl, OpenGL ES

참고:


CboxConsole

내용:
조이스틱, 게임콘트롤 등을 USB로 연결해서 NaCl를 통해 브라우저를 제어 하는 기술, 

사용기술:
Chrome, NaCl, AppEngine, HTML5, JavaScript

참고:
https://github.com/CboxConsole
구글 그룹스에서 GDG Chrome 그룹 운영


[Procssing]

디지털 그림자 인형극

내용:
HTML5와 앱엔진으로 그림을 보관하고 노출시킨다. 프로세싱 기반으로 인형극을 만든다. 아두이노를 통해서 아날로그 레버를 통해 캐릭터를 움직이게 한다. 

사용기술:
GAE, HTML5, Arduino, Processing

참고:
https://github.com/akudoku/digital_shadow_puppetry


기타 더 있음 (추가 리포팅 예정)







Using the new Build System Android 2012. 11. 19. 18:01

http://tools.android.com/tech-docs/new-build-system 
We are working on a new build system to replace both the build system inside ADT and Ant.


Using the new Build System

Using the new Build System

The new build system is based on Gradle.
If you are not familiar with Gradle, it is recommended to read at least the first few chapters ofhttp://gradle.org/docs/current/userguide/userguide_single.html

The location of the SDK folder is still needed and is provided by one of the following methods:
  • local.properties file with a sdk.dir property, similar to the current build system. This is located in the project root directory. In multi-project setup, this is next to settings.gradle.
  • ANDROID_HOME environment variable
All other configuration goes in the standard build.gradle file.

Using the new Android plugin for Gradle

The android plugin for gradle is available through Maven:
  • groupId: com.android.tools.build
  • artifactId: gradle
  • version (current milestone): 0.1
Requirements:
Gradle 1.2.
The Android Platform-Tools component in version 15 rc7. To download it, you will need to enable the preview channel in the SDK Manager. Read more about it here.

To use it in your build.gradle file, put at the top:
buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:0.1'
    }
}
apply plugin: 'android'

For library project, the plugin to apply is ‘android-library

In the future we will investigate how to simplify this step.

Basic Project Setup

All configuration related to the Android plugin is done inside the android namespace:
android {
    ...
}

The most important setting is the build target (previously found in project.properties):
android {
    target = ‘android-15’
}

Changing the default configuration is done using the defaultConfig object:
android {
    target = ‘android-15’
    defaultConfig {
        versionCode = 12
        versionName = “2.0”
    }
}

Default Config can be configured with the following properties:
  • packageName (String)
  • versionCode (int)
  • versionName (String)
  • minSdkVersion (int)
  • targetSdkVersion (int)
  • testPackageName (String)
  • testInstrumentationRunner (String)
  • signingStoreLocation (String)
  • signingStorePassword (String)
  • signingKeyAlias (String)
  • signingKeyPassword (String)
  • buildConfig (String...)
Note: it is best not to put signing passwords in the build script, and instead have your CI server does the signing itself, or use a local gradle.properties file.
However, you can use these signing properties if you share a single debug keystore across developers.

Build Types and Product Flavors

Creating new build types or editing the built-in debug and release is done with the buildTypes element. This configures the debug build type and adds another one called “staging”:
android {
    buildTypes {
        debug {
            packageNameSuffix = “.debug”
        }
        staging {
            packageNameSuffix = “.staging”
            debuggable = true
            debugSigned = true
        }
    }
}

Build Types can be configured with the following properties:
  • debuggable (bool; default:false; true for debug)
  • debugSigned (bool; default:false; true for debug)
  • debugJniBuild (bool; default:false; true for debug)
  • packageNameSuffix (String; default:null)
  • runProguard (bool; default:false) // unused right now.
  • zipAlign (bool; default:true; false for debug)
  • buildConfig (String...)
Creating product flavors is done with the productFlavors element:
android {
    defaultConfig {
        versionCode = 12
        minSdkVersion = 8
    }
    productFlavors {
        freeapp {
            packageName = “com.example.myapp.free”
            minSdkVersion = 10
        }
        paidapp {
            packageName = “com.example.myapp.paid”
            versionCode = 14
        }
    }
}

Flavors can be configured with the same properties as the default config. If both define a properties, then the flavor overrides the default config.

Flavor Groups

Using multi-flavor variants is done with the following two steps:

  • Defining the flavors groups. The order is important, they are defined higher priority first.
  • Assigning a group to each flavor.
android {
    flavorGroups “abi”, “version”

    productFlavors {
        freeapp {
            group = “version”
            ...
        }
        x86 {
            group = “abi”
            ...
        }
    }
}

Other Options

BuildConfig

BuildConfig is a class that is generated automatically at build time.
Similar to the old build system, the class is generated with a DEBUG boolean field. In this case it maps to the value ofBuildType.debuggable.

On top of this you can now insert new items in the class. This is done by providing full Java lines.
This is possible from the defaultConfig, any flavors, any build types. All lines are aggregated together in the same class for a given variant.

For instance: 
android {
    target = "android-15"

    defaultConfig {
        buildConfig "private final static boolean DEFAULT = true;", \
                "private final static String FOO = \"foo\";"
    }

    buildTypes {
        debug {
            packageNameSuffix = ".debug"
            buildConfig "private final static boolean STAGING = false;"
        }
        staging {
            packageNameSuffix = ".staging"
            buildConfig "private final static boolean STAGING = true;"
        }
        release {
            buildConfig "private final static boolean STAGING = false;"
        }
    }
}

Aapt Options

To provide options to aapt, the aaptOptions element is used. Right now two options are supported (more will come later):
  • noCompress (String list): list of extension to not compress
  • ignoreAssetsPattern: Assets to be ignored. Default pattern is: !.svn:!.git:.*:<dir>_*:!CVS:!thumbs.db:!picasa.ini:!*.scc:*~
android {
    target = "android-15"

    aaptOptions {
        ignoreAssetsPattern = “!.svn:!.git:.*:<dir>_*:!CVS:!thumbs.db:!picasa.ini:!*.scc:*~”
        noCompress "txt", “foo”
    }
}

Using Library Projects

As mentioned above, the plugin for library projects is android-library. It uses the same Maven artifact.

Libraries don’t have flavors, and only two build types. They can be configured this way:
android {
    target = “android-15”
    debug {
        ...
    }
    release {
        ...
    }
}

Using a library is done one of the following way:
Uploading to a maven repo is done the same way a regular Java library is uploaded. See more information here:http://www.gradle.org/docs/current/userguide/maven_plugin.html

Using a library through a repo is the same as a regular Java library, using the groupIdartifactId and version.

Standard Tasks

Java projects built with Gradle use 4 main tasks:
  • assemble -- assemble the software
  • check -- builds and run the checks and tests
  • build -- runs assemble and check
  • clean
The Android plugin use the same tasks but extends the first two to handle build variants.

For instance, a project with flavor1 and flavor2 and the two default build types will have the following 4 tasks:
  • assembleFlavor1Debug
  • assembleFlavor2Debug
  • assembleFlavor1Release
  • assembleFlavor2Release
On top of these, 4 other assemble tasks are available
  • assembleDebug -- builds all debug variants (for all flavors)
  • assembleRelease -- builds all release variants (for all flavors)
  • assembleFlavor1-- builds all flavor1 variants (for all build types)
  • assembleFlavor2-- builds all flavor2 variants (for all build types)
Adding new flavors and new build types will automatically create new assemble type tasks.

Additionally, assemble tasks for test apps are created:
  • assembleFlavor1Test -- builds the test app for the flavor1 app.
  • assembleFlavor2Test -- builds the test app for the flavor2 app.
  • assembleTest -- builds all test apps.
The default assemble tasks will call all (non test) assemble<flavors><buildtypes> tasks to build all variants of the application.

The check task is augmented similarly:
  • checkFlavor1Debug -- tests the flavor1 variant
  • checkFlavor2Debug -- tests the flavor2 variant
The default check tasks calls all check<flavor> tasks.

Checks are run by doing the following
  1. Install apps (if testing a library, only the test app)
  2. run tests
  3. Uninstall apps
Install / uninstall is done with the following tasks
  • install<flavors><type> for each variant
  • uninstall<flavor><type> for each variant
Additionally uninstallAll attempts to uninstall all variants.

Building an android application takes a lot of steps and each are available to be run on demand.
Here’s a full list of tasks used to build an application:
  • prepare<Variant>Dependencies
  • process<Variant>Manifest
  • generate<Variant>BuildConfig
  • crunch<Variant>Res
  • process<Variant>Res
  • compile<Variant>Aidl
  • compile<Variant>
  • dex<Variant>
  • package<Variant>

Customizing the tasks

Gradle provides an API to query for tasks by name or by classes.

The classes used by the Android tasks are the following:
  • Compile -- java compilation
  • CrunchResourcesTask
  • ProcessManifestTask
  • GenerateBuildConfigTask
  • ProcessResourcesTask
  • CompileAidlTask
  • DexTask
  • PackageApplicationTask
  • ZipAlignTask
In the future we intend to provide a custom API to access flavors, build types, and variants, as well as task inputs/output to do easier manipulation of the tasks.

Working with and Customizing SourceSets

Starting with 0.2, the build system uses its own SourceSet objects intead of the ones provided by the Java plugin.
You can use them to configure the location of all source elements as well as (in the case of the java source and resource folders) customize filters and exclusion.

The default config creates two sourcesets: "main" and "test". All flavors and build types automatically create their own sourceset, named after the flavor/build type name. Additionally, all flavors create a corresponding test flavor named "test<Flavorname>"

Default sourceset location is under src/<sourceset>
Sourceset have the following properties:
  • manifest, type AndroidSourceFile, default location src/<sourceset>/AndroidManifest.xml
  • res, type AndroidSourceDirectory, default location src/<sourceset>/res/
  • assets, type AndroidSourceDirectory, default location src/<sourceset>/assets/
  • aidl, type AndroidSourceDirectory, default location src/<sourceset>/aidl/
  • renderscript, type AndroidSourceDirectory, default location src/<sourceset>/renderscript/
  • jni, type AndroidSourceDirectory, default location src/<sourceset>/jni/
as well as the normal java project sourceset properties:

AndroidSourceFile and AndroidSourceDirectory have a single configurable property, respectively srcFile andsrcDir.

Example of reconfiguring the sourcesets to match an existing project structure:
android {
    sourceSets {
        main {
            manifest {
                srcFile 'AndroidManifest.xml'
            }
            java {
                srcDir 'src'
                exclude 'some/unwanted/package/**'
            }
            res {
                srcDir 'res'
            }
            assets {
                srcDir 'assets'
            }
            resources {
                srcDir 'src'
            }
        }
        test {
            java {
                srcDir 'tests/src'
            }
        }
    }
}

For more information about working with Sourceset, see: http://gradle.org/docs/current/userguide/java_plugin.html#sec:source_sets
Note that for Android projects, the sourceSets container must be modified inside the android container.

세로 프로그래스바와 씨크바 예제이다. 

[xml 구성시 주의 사항]

1. Thumb 이미지 보다 크게 layout_width 폭을 지정하면 프로그래스바 폭이 두껍게 된다.

2. 프로그래바 폭을 조절하려면 android:maxWidht에 값을 넣어 준다.


        <com.tokaracamara.android.verticalslidevar.VerticalSeekBar

            android:id="@+id/SeekBar02"

            android:layout_width="56px"

            android:layout_height="fill_parent"

            android:maxWidth="10px"

            android:progressDrawable="@drawable/progress_vertical"

            android:thumb="@drawable/seek_thumb_wide" />


[샘플 소스]

아래 프로젝트에 AbsVerticalSeekBar파일의 한 곳이 오류가 잇어서 수정했다. 

썸브 바운데리 설정시 LeftBounday가 -1로 되는 것을 방지했다. gap 부분

VerticalSlidebarExample.zip


[참고 사이트]

http://code.google.com/p/trimirror-task/source/checkout

http://560b.sakura.ne.jp/android/VerticalSlidebarExample.zip

12장 애니메이션

온라인주소: http://lingostar.co.kr/wp/archives/648


Animation , Dynamic UI

에니메이션은 UI적인 접근을 위한 것이다

에니메이션 오버뷰 / 잔상 효과,  

 - NSTimer / 초창기 사용
- NSAnimation / 10.4때 만들어졌다. 거창해 보이지만 그냥 NSTimer에 이지인, 이지아웃기능을 넣은 간단한 것이다.

  • CoreAnimation / 에니메이션 엔진을 만들었다.

Dynamic UI의 정수: 타임머신, 스페이시스, 아이툰즈의 커버플로우,
 - 반복적인 움직임.. 


View Animation을 몸풀기로 만들어 보자

 - 간단히 에니메이션을 얻어오는 것이다.
 - 비깅 컴인만 해 주면 에니메이션된다.

 - 만들자 MovingMoving

 1) UIView, Button을 넣는다.

 - 아웃렛, 액션 하나가 있어야 할 것이다

 

헤더파일

 

#import <UIKit/UIKit.h>

 


 

@interface MovingMovingAppDelegate : NSObject <UIApplicationDelegate> {

 

UIWindow *window;

 

UIView *movingView;

 

}

 


 

- (IBAction)move:(id)sender;

 

@property (nonatomic, retain) IBOutlet UIWindow *window;

 

@property (nonatomic, retain) IBOutlet UIView *movingView;

 


 

@end

 

 

임플리멘테이션파일

#import "MovingMovingAppDelegate.h"

 


 

@implementation MovingMovingAppDelegate

 


 

@synthesize window;

 


 


 

- (void)applicationDidFinishLaunching:(UIApplication *)application {    

 


 

// Override point for customization after application launch

 

    [windowmakeKeyAndVisible];

 

}

 


 


 

- (void)dealloc {

 

    [window release];

 

    [super dealloc];

 

}

 


 

- (IBAction)move:(id)sender

 

{

 


 

movingView.center = CGPointMake(240, 400);

 

movingView.backgroundColor = [UIColoryellowColor];

 

movingView.transform = CGAffineTransformMakeRotation(90);

 

}

 


 

@end


 

이제 에니메이션 주자.

- (IBAction)move:(id)sender
{

 


 

[UIViewbeginAnimations:@"Moving"context:nil];

 

[UIViewsetAnimationDuration:2];

 

movingView.center = CGPointMake(240, 400);

 

movingView.backgroundColor = [UIColoryellowColor];

 

movingView.transform = CGAffineTransformMakeRotation(90);

 

 

 

[UIViewcommitAnimations];

 

}

 

 

블럭이 천천히 음직이려면

[UIViewsetAnimationCurve:UIViewAnimationCurveEaseInOut];

 

여기서 보면 에니메이션의 시작과 종료의 기준은 아래 것이다.

[UIViewbeginAnimations:@"Moving"context:nil]; // 시작

[UIViewcommitAnimations];  // 종료


현재까지는 코어에니메이션을 사용한 것이다. 코어 그래픽스는 어제 한 것이다. 

 

코코아터치는 코어 에니메션에 떠받혀져 있다.

따라서 UIKit은 코어에니메이션을 잠간 빌려서 사용할 수 있다.
에니메이션을 동작을 잠간 고민해 보자

 

상기 에니메이션 불럭을 바깥으로  컬러변경코드를 옮겨 보자.

[UIViewbeginAnimations:@"Moving"context:nil];

 

[UIViewsetAnimationDuration:2];

 

[UIViewsetAnimationCurve:UIViewAnimationCurveEaseInOut];

 

 

 

movingView.center = CGPointMake(240, 400);

 

movingView.transform = CGAffineTransformMakeRotation(90);

 

 

 

[UIViewcommitAnimations];

 

 

 

movingView.backgroundColor = [UIColoryellowColor];

 

그럼 움직이기전에 컬러값이 변경된후 움직이게 된다.

이렇게 되는 이유는 왜 그런지를 생각해 보라..

1초 딜레이를 주고 변하는 코드는 다음과 같다

[movingViewperformSelector:@selector(setBackgroundColor:) withObject:[UIColoryellowColor]afterDelay:1];


여기까지가 12-2까지 이다 

12-3 ~ 4까지 읽어 보라
12-5를 실습하자.

CoreAnimation Role을 만들어보자. (12.4.3 설명)

CABasicAnimation, CAKeyframeAnimation만 사용할 수 있고 나머지는 직접적으로 사용할 수는 없다.

CAAnimationGroup: 

패스를 갖는(반복)은 CAKeyframeAnimation으로 만들어야 한다.


아래 두개는 서로 약간다르다.

CALayer, CGLayer: 


CALayer : 코어에니메이션의 오브젝트 GPU가속이 되는 오브젝트로 하드웨어 가속이 가능하다. 

   이차원, 삼차원정의를 갖는 이차원 평명

  콘테츠로는 

   Quartz image, Quartz drawing, CoreText(Mac), OpenGL, Quarz Composer, QuickTime..(Mac)

Keyframe Animation Code

-  패스를 따라서 움직일 것이다.


새로운 프로젝터:

1.Helicopter를 만든다.

2.리소스를 넣어준다.  (Copy items into destination group's forlder를 체크해 준다)



스크린샷_2010-01-23_오후_3.07.47.png

Heli_1, 2, 3을 복사한다.

책 350페이지를 보자.

 

 

에니메이션 3가지 종류

1)UIView

 시작과 끝을 정의해서 한것.. MovingMoving

2)ImageAnimation 

    사람이 걷는 경우 걷는 이미지가 여러장이 있다. 이것은 코어에니메이션과는 전혀 상관이 없다. / 아주 많이 사용하는 방식

   Helicopter프로젝터 방식읻.

3)


4.인터페이스 빌더에서 미디어에서 이미지르 끌어다 놓는다

   버턴 두개.

5.프레임웤 추가 QuartzCore framework추가 / 임포트 하구
6. 아웃렛와 액션을 만든다. / 저장하고

 

#import <UIKit/UIKit.h>

#import <QuartzCore/QuartzCore.h>


@interface HelicopterAppDelegate : NSObject <UIApplicationDelegate> {

UIWindow *window;

UIImageView *helicopterImageView;

}


- (IBAction)toggleFly:(id)sender;

- (IBAction)moveThroughPath:(id)sender;


@property (nonatomic, retain) IBOutlet UIWindow *window;

@property (nonatomicretainIBOutlet UIImageView *helicopterImageView;

@end

7.인터페이스빌더에서 상호 연결스크린샷_2010-01-23_오후_3.17.59.png


8. 초기 정의와 액션을 코딩한다.

 

#import "HelicopterAppDelegate.h"

 


 

@implementation HelicopterAppDelegate

 


 

@synthesize window;

 

@synthesize helicopterImageView;

 


 

- (void)applicationDidFinishLaunching:(UIApplication *)application {

 


 

UIImage *heli1 = [UIImageimageNamed:@"Heli_1.png"];// 로컬의 이미지를 리소스를 읽어 오는 방법

 

UIImage *heli2 = [UIImage imageNamed:@"Heli_2.png"];

 

UIImage *heli3 = [UIImage imageNamed:@"Heli_3.png"];

 

NSArray *animationImageArray = [NSArray arrayWithObjects:heli1, heli2, heli3, nil];

 

helicopterImageView.animationImages = animationImageArray;

 

 

 

// Override point for customization after application launch

 

    [windowmakeKeyAndVisible];

 

}

 


 


 

- (void)dealloc {

 

    [window release];

 

    [super dealloc];

 

}

 


 

- (IBAction)toggleFly:(id)sender

 

{

 


 

if ([helicopterImageViewisAnimating]) {

 

[helicopterImageViewstopAnimating];

 

else{

 

[helicopterImageViewstartAnimating];

 

}

 

 

 

}

 

- (IBAction)moveThroughPath:(id)sender

 

{

 


 

}

 


 


 

@end

9. KeyframAnimation을 만들어보자

 

- (IBAction)moveThroughPath:(id)sender

 

{

 

// Create path

 

CGPoint currCenter = helicopterImageView.center;  // 시작포인터가된다.

 

// S자곡선을 3가지콘트롤포인터가생긴다.

 

CGMutablePathRef aniPath = CGPathCreateMutable();

 

CGAffineTransform xform = CGAffineTransformIdentity;

 

CGPoint dest1 = CGPointMake(160200);

 

CGPoint dest2 = CGPointMake(160400);

 

 

 

// 이후패스를넣어준다. 콘트롤두개인베젤코드

 


 

CGPathMoveToPoint(aniPath, &xform, currCenter.x, currCenter.y);

 

CGPathAddCurveToPoint(aniPath, &xform, 050050, dest1.x, dest1.y);

 

CGPathAddCurveToPoint(aniPath, &xform, 310350310350, dest2.x, dest2.y);

 

 

 

// Create Animation Object 에니메이션을 설계하는 것이다.

 

CAKeyframeAnimation *keyAni = [CAKeyframeAnimationanimation];

 

keyAni.duration = 4.0;

 

keyAni.path = aniPath;

 

 

 

// Apply Animation  CALayer 패스를 넘기겨 주는 것이다

 

// 뷰뒤에레이어가다숨어있다고보면이해가편해진다.

 

[helicopterImageView.layeraddAnimation:keyAni forKey:@"position"];//순간 GPU올라간다.

 

}

 

베젤 곡선을 코드 한줄로 변경해 보자.

//CGPathAddCurveToPoint(aniPath, &xform, 0, 50, 0, 50, dest1.x, dest1.y);

//CGPathAddCurveToPoint(aniPath, &xform, 310, 350, 310, 350, dest2.x, dest2.y);

CGPathAddCurveToPoint(aniPath, &xform, -20050500350, dest2.x, dest2.y);


결과는 다음과 같다.

스크린샷_2010-01-23_오후_3.42.40.png

 

에니메이션의 설계......음직임의 설계는 동일하며 내용만 달라진다... 애플도 이런 효고를 많이 사용한다.

추가로 하자.

버턴도 패스에 따라서 에니메이션 된다.

215페이지에 보면 UIView의 하부 것들은 모두 에니메이션 처리가 가능하다.

뷰에니메이션을 쌓주면 가능하다. 모든 프로퍼티가 다 에니메이션이 되는 것은 아니다. 

뷰의 프로퍼티가 되는 것은 에니메이션이된다.

 


모델과 프린젠테이션이 나누어져 있다.
에니메이션되는 것은 프리젠테이션이 되는 것이고 모델은 그대로 있다.

그래서 버턴이 움직일때 모델은 그대로 그 위치에 있어서 터치시 이벤트를 받고

움직이는 버턴을 클릭했을때는 반응이 없는 것이다

12.5.7
게임을 위한 확장

 중간에 장애물이 있을때에 대한 처리.. NSTimer를 사용해서 충돌 체크해 주는 것이다.
 360페이지 CGRectIntersectsRect이 중요한 함수 일 것이다


12.6.0 

코어에니메이션에 대해서 조금더 볼 것이다.

코아슬라이드라는 맥용 어플리케이션이 있다.

큰이미지는 UIView로는 할 수 없구. 타일드해야 해야 하는데 CALayer의 타일링 레이어를 사용해야 하다.

      큰PDF 파일을 헨들링

 

불꽃예제.. 2개의 큐뷰가 돌면서 전체를 큐뷰로해서 다시 돌리는 것 등이 가능하다.
레이어를 여러개 두어서.. 움직이게 된다. 

 

레이어로 모든 뷰를 바꿀수 있다.
단지 에니메이션되는 것이 아니라 GPU를 사용해서 콤포지트하기 위해서 가장 좋다.

모든 시작적인 미디어들을 레이어를 사용하는 것이 확장성과 빠르게 동작한다.

레이어를 서브클래서해서 사용할 수 있는 것은 더욱 많이 있다.
예제와 함께 어드벤스드하게 강의을 준비할 것이다. 

 

코어 에니메이션 책이 있다. 그런데 좀 부실하다. 애플의 가이드를 보고 하는 것이 좋다.


12.6 예제는 심화.. 뷰 트렌지션이다. 17.게임킷은 심화에서 하자.


Save - 아카이빙.. 

큰개념만 설명하고 18장으로 넘어가자..

380
과제 1.  <<  시도해 봐라..  헬리곱터가 심플스케치에서 손가락으로 움직인 경로로 헬리곱터가 움직이면 제미 있을 것이다.
과제2

쓸만한 위젯 lib Android 2012. 11. 16. 09:44



http://d.hatena.ne.jp/thorikawa/20101130/p1

Android 응용 프로그램에서 사용할 수있는 편리한 UI 라이브러리Add StarrgfxshepherdMastershepherdMastergabuchancanotnanatsumaKazzzdeg84jmab

Android 애플 리케이션 말하면 UI 생명! 라는 것으로 괴짜 분들이 만들어지는 편리한 UI 라이브러리를 찾아낸 한 스크린 샷과 함께 정리해 있습니다 .

여러분 모두 소스와 일부 샘플 응용 프로그램을 게시되어 있으므로 당장이라도 시도 할 수 있습니다.

(작가 분들, 싣기로 문제가있는 것 같다면 수고 스럽겠지만 연락해주십시오)

Quick Action

  • 공식 Twitter 어플 바람에 터치 한 부분에 풍선을 볼 수
  • 레이아웃도 지정 가능

YAM의 잡기장 : Android Quick Action의 Android 라이브러리 프로​​젝트를 만들어 보았다

f : id : thorikawa : 20101130005022p : image

Drag and Drop ListView

  • 드래그 앤 드롭으로 정렬 가능한 목록 보기
  • 비슷한 같은 것은 여러가지 있지만 이것이 가장 사용하기 쉬웠다!

사용자가 정렬 가능한 ListView를 조금 리치에 해 보았다 - 내일의 열쇠

f : id : thorikawa : 20101130005750p : image

Calendar

  • 뷰에서 공휴일 을 판별 표시 가능한 달력 보기
  • 상하 좌우 톡에서 월을 전환 할 수도 있으므로, 제스처 조작을 실현하고 싶은 사람은 그 부분 만이라도 참고가 될지도

CalendarView 공개했습니다 - Kazzz의 일기

f : id : Kazzz : 20101112133610p : image

3D ListView

  • 3D 로 회전하면서 스크롤 하는 목록 보기
  • 개인적으로 좋아 합니다만, 사용 장소가 떠오르지 않는다 w

Android Tutorial : Making your own 3D list - Part 3 (final part) | Developer World

f : id : thorikawa : 20101130005020p : image

CoverFlow

Interfuser : Android Coverflow Widget V2

f : id : thorikawa : 20101130005019p : image

ChartLibrary

afreechart - Project Hosting on Google Code

f : id : thorikawa : 20101130005446p : image : left

f : id : thorikawa : 20101130005447p : image : left

f : id : thorikawa : 20101130005540p : image

Zoomable ImageView

  • 길게 누르면 터치로 확대 · 축소 할 수있게된다 ImageView
  • 이미지 가 화면보다 클 것으로 표시 위치를 이동할 수있게된다
  • 이동시 후치까지 가면 바운드 애니메이션 된

Android one finger zoom tutorial - Part 4 | Developer World

Drag and Drop ImageView

  • 이미지 를 드래그 앤 드롭하여 버튼 을 두드리는
  • 아래의 스크린 샷이라고 분 에서 아니지만, 이미지 를 드롭 할 때 회전하는 것입니다. 그 애니메이션이 멋진

Android에서 드래그 앤 드롭 - hidecheck의 일기

f : id : thorikawa : 20101130010017p : image

NumberPicker

  • 증가 · 감소의 간격을 조정 가능한 수치 피커

사용자 NumberPicker 만들기 - 냐ン다후루 일기

f : id : thorikawa : 20101130084357p : image

Color Picker

  • 색상 선택기
  • 이쪽은 구형

Android에서 색상 선택기를 만들자 - 내일의 열쇠

f : id : thorikawa : 20101130005917p : image


Color Picker 11 / 30 추가)

YAM의 잡기장 : Android ColorPickerDialog을 만든

f : id : thorikawa : 20101130233733p : image

Vertical Slider 11 / 30 추가)

Vertical Seekbar - Android Developers | Google 그룹

f : id : thorikawa : 20101130233734p : image

그 밖에도

이런 편리한있어! 라고하는 것이 있으면 가르쳐주세요


프로그래머로 사는 법 2012. 11. 15. 16:14

http://blog.hanb.co.kr/375

[프로그래머로 사는 법 기획연재 08 : 대한민국에서 나이 많은 개발자가 살아남는 법]

사용자 삽입 이미지


* 본 내용은 도서의 내용을 일부 발췌하여 작성한 것입니다. 도서에는 더 많은 이야기를 담고 있답니다 :)
변종원(43세)
CodeIgniter 한국사용자포럼 운영진으로 활동했으며, 웹사이트 통합 관리 시스템 webmaker3를 개발했다. 2011 PHPFest에서 CodeIgniter 관련 발표를 했으며, 그 외 다수의 세미나에서 발표했다. 현재 ㈜프리비 개발팀 부장으로 재직 중이다.


곰곰이 생각해보니 연차가 올라감에 따라 급여도 올려야 하고 그러다 보면 순수하게 개발자로서 받을 수 있는 급여수준을 넘어가는 순간이 있는데 계속 개발만 하면 다행이지만 회사 입장에서 나이 어린 개발팀장이 자기보다 수준도 높고 나이 많은 차장급 개발자를 관리하는 게 쉽지가 않습니다. 주변의 개발자를 보면 빠르면 30대 초반 또는 중반에 개발팀장을 하고 그 이후에 CTO급이 되지 못하면 기술 영업으로 가거나 전직을 해야 했습니다.

한때 우스갯 소리로 사수가 없는 초급 개발자에게 개발하다가 막히면 회사 근처의 통닭집 사장님에게 물어보면 금방 해결된다는 말을 했습니다. 많은 개발자가 외식 프랜차이즈 쪽으로 전업했던 때가 있었습니다. IT 회사 근처의 통닭집 중에 그런 분이 계셨고 그분들께 물어보면 다양한 경험을 하셨던 분이었기에 쉽게 답변을 얻을 수있었다고 합니다. 안타까운 현실이지만 저도 전업을 심각하게 고민을 했던 때가 있었습니다.

5년 차 정도까지는 급여에 만족하면서 지냈는데 경력이 7년이 넘어가면서 급여는 기대만큼 오르지 않고 제2의 IMF라는 시기와 맞물리고 급여가 밀리는 등 39살이던 2008년 즈음에 전업을 심각하게 고민해야 했습니다. 그러던 중에 자영업을 하다가 부동산 개발회사를 거쳐 모 보험사 컨설턴트로 일하고 있는 대학교 친구를 우연히 만나게 됐고 외제차를 타고 다니는 친구와 제 처지를 비교할 수밖에 없었습니다. 그 친구와 저는 사람 만나는 것을 좋아하는 성향이 비슷했기에 제게 보험 쪽 입사를 권유했고, 모 보험사 지점에서는 저의 대학 시절 활동(과 홍보부장, 농구동아리 회장)과 PC 통신 시절의 하이텔 지역모임 대표시삽 활동 등을 인정하여 6개월에 걸쳐 부지점장과 수차례에 걸친 미팅과 교육, 2번의 시험을 거쳤고, 최종적으로 지점장 인터뷰까지 끝내고 출근할 날만 정하면 되는 상태였는데 마지막으로 생각을 해봤습니다. “내가 개발을 그만두고 다른 일을 하면서 정말 후회하지 않을 수 있을까?” (제 신념 중의 하나가 후회 없는 삶입니다) 그리고 여태까지 쌓아왔던 경험(인사, 총무, 경리, 물류, 교육, 배송, 제휴, 기획, 사이트 운영, 쇼핑몰, 솔루션 개발)과 SVN, 이슈트래커, 스크럼 등 을 회사에 정착시켰던 경험과 노하우를 이대로 사장하기엔 너무나 아까웠습니다.

전직을 하게 되면 그동안 쌓아왔던 다양한 경험과 노하우를 사장시킬 수 있기에 “앞으로 나는 개발자로 살아야겠다”고 생각을 굳게 다짐을 했고 현재 회사에 입사할 때 “나는 계속 개발을 하고 싶다”고 이야기했습니다(회사는 다 어쩔 수 없나 봅니다. 요즘은 살짝 관리도 넘나듭니다. PM도…).

물론 주변에 성공적으로 전업을 한 분도 계십니다. 저보다 3살 많은 형님인데 비슷한 시기에 웹 개발에 입문하여 그분은 보안 솔루션 기술 영업으로 전직했다가 지금은 취미 생활을 직업으로 삼아 일산에서 나름 알려진 목공방을 운영하고 계십니다. 또 한분은 저와 같은 시기에 저는 개발자로, 그분은 관리자로 시작하여 5년 정도 같이 근무했습니다. 그 후에는 자기 사업을 하겠다고 독립하셨고 이러저러한 과정을 거쳐 지금은 그분도 취미 생활이었던 여행을 업으로 삼고 계십니다. 제 경우에는 개발 자체가 저에게는 즐거움이었기 때문에 계속 개발을 할 수 있었던 것 같습니다. 개발이 주는 즐거움이 스트레스를 덜 받게 해주었던 것 같습니다.

2008년 CodeIgniter를 만나게 되면서 제 개발자 인생에 큰 전환점이 됩니다. 날코딩에 지쳐 있을 때였고 SNS를 개발하는 회사에 입사했는데 처음 시작하는 회사라 프레임워크 도입을 강력하게 주장하여 영문 매뉴얼과 외국 포럼의 Q&A를 보면  개발을 시작했습니다. 그러다 최용운이라는 친구가 매뉴얼을 한글로 번역을 하여 PHPSCHOOL에 공개를 했고 자주 의견을 나누던 사람들 몇몇이 모여 술 한 잔을 하기로 한 날 CodeIgniter 한국사용자포럼이 발족됩니다. 돈을 모아 도메인을 구입하고 CodeIgniter로 사이트를 만들고 소스도 공개하고 운영을 시작한 것이 2009년입니다.

초기에는 친목 수준이었는데 한글 매뉴얼과 질문답변 그리고 PHP에서도 프레임워크를 이용하고자 하는 열망이 맞물려 국내 PHP 프레임워크 모임 중에서는 제일 활발하게 활동을 하고 있습니다.

공통 관심사를 가진 사람들이 모여 포럼을 운영하고 소스를 공개하고 노하우를 나누다 보니 저에게 어느새 금전적 보상이 돌아오기 시작했습니다. CodeIgniter 개발 의뢰라던가 교육, 이직 시 유리함, 더 나아가서는 컨설팅까지 단순히 회사 내부의 개발자로서만이 아니라 외부에서도 인정을 받을 수 있었습니다.

포럼에서 한 달에 한 번 정모를 진행하는데 1년에 한 번 정도를 빼고 항상 정모를 진행합니다. 엠티도 가고 술자리도 하고 세미나도 하고 하는데 술자리에서 포럼 후배들에게 하는 이야기가 있습니다. “힘들게 이 자리까지 왔는데 더 가겠다.”라고요. 주변에 누군가 나이가 많은데도 현업에서 개발을 하고 높은 연봉과 부수입을 가진 선배 개발자가 있다면 “나도 저렇게 할 수 있겠다. 저런 길이 있을 수 있겠구나.”라고 생각하고 그런 길을 갈 수 있을 거로 생각합니다.

가끔 면접을 보다 보면 이력서에 “백발이 될 때까지 개발을 하고 싶다.”라고 쓰여 있는 것을 보게 됩니다. 저도 그런 생각으로 현재의 길을 가고 있습니다. 그러기 위해서 개발자에게 신기술 습득과 트렌드에 대한 이해, 지속적인 공부는 필수입니다. 자기 자신의 계발을 위해 투자하지 않는 개발자는 진정한 개발자가 아닙니다. 회사에서 시켜서, 남들이 하니까 따라 해서는 오래 살아남을 수가 없습니다.

저는 5년 차쯤에 항상 하던 일과가 하나 있었습니다. PHPSCHOOL이나 기타 사이트에 새로 공개된 프로그램을 설치해보고 소스를 열어보고 공부하는 것입니다. 거기서 얻는 것은 새로운 알고리즘과 새로운 경험입니다. 문제를 다른 시각에서 바라보고 개발한 소스를 보게 되면 제가 만든 소스와 비교를 해봅니다. 똑같은 문제를 어떤 시각에서 보느냐에 따라 다른 해결 방법과 소스가 나옵니다. 맞고 틀리고의 문제가 아닙니다. 다른 사람의 개발 방식이 효율 면에서 떨어질 수 있지만, 나중에 다른 상황에서는 그런 방식을 써야 할 수도 있습니다.

너무 잡다하게 많은 것을 알고 있을 필요는 없지만 힘들었던 시절에 습득했던 다양한 경험과 다른 사람의 소스를 보고 분석하는 일을 습관처럼 반복한 결과 지금은 문제 해결과 변수 제거에 많은 도움을 주고 있습니다.

포럼에서 질문, 답변을 하다 보면 내가 접하지 못했던 상황과 내가 모르는 질문이 올라옵니다. 내가 접하지 못했던 상황을 만났을 때 제 PC에서 재현을 해봅니다. 어떤 때는 PHP 버그라던가 CodeIgniter 버그 같은 것도 발견하게 됩니다. 그러면서 그러한 상황에대한 간접 경험을 할 수 있고 모르는 질문은 구글이나 다른 검색 사이트를 통해 찾아보거나 올라온 소스를 실행해보거나 아니면 프레임워크 코어 소스를 열어서 찾아봅니다. 그러면서 또 하나의 지식을 습득합니다.

웹메이커3라는 솔루션을 만들 때는 CodeIgniter 외국포럼의 wiki에 올라온 라이브러리나 소스를 모두 설치해서 실행해보고 소스를 본 적도 있습니다. 솔루션에 사용할기술을 선별하고 당장 사용하지 않더라도 나중을 위해 어느 분야에 사용하면 좋을지 기록하고 보관했습니다. CodeIgniter의 라이브러리는 클래스 형태라서 누군가가 개발한 대부분의 PHP 클래스를 그대로 사용할 수 있습니다. CodeIgniter 입문 초기에는 시간이 날 때마다 PHPCLASSES.ORG라는 PHP 클래스 소개 사이트에서 많은 시간을 보내기도 했습니다.

이런 일련의 활동이 내 지식을 살찌게 하고 내 가치를 높이고 PHP만 다룰 수 있는 43세인 제가 개발자로 살 수 있는 비법(?)입니다.

언젠가부터 꿈인 연구소에서의 개발자도 언젠가는 이루고자 합니다. 현재는 회사의 개발 규약을 정하고 소스와 개발에 적용하게 하고 Redmine이라는 이슈 트래커를 이용하여 업무를 히스토리화 하고 SVN을 통해 협업 개발을 할 수 있는 환경을 구축하였고 스크럼 개발방법론에서 일부 차용하여 회의를 진행하고 있습니다. 개발에 집중할 수 있도록 시스템을 구축하는 것도 경험 많은 개발자의 롤이라는 생각이 들고 앞으로는 정말 개발에만 매진할 수 있는 그런 환경을 만들고 싶습니다.

후배 개발자에게 당부하고 싶은 것은 지속적인 자기계발을 하라는 것입니다. 구글플러스에 개발자의 비애 비슷한 내용으로 신기술이 지속해서 나오기 때문에 계속 공부해야 한다고 썼더니 의사도 마찬가지라고 어떤 의사분이 써주셨습니다. 새로운 수술방법이 나오고 기계가 나오면 방법을 습득해야 먹고 살 수 있다고… 개발자도 마찬가지입니다. 바뀌는 트렌드에 대응하지 못하는 개발자는 도태될 수밖에 없습니다. 개발자는 타성에 젖어 있으면 안 됩니다. 끊임없이 노력해야 합니다. 얼리어답터가 되어야개발자로서 오래 살아남을 수 있습니다.

간혹 포럼의 팁 게시판을 보면 PHP 함수 하나면 처리할 수 있는 것을 함수를 만들어서 올리는 사람이 있습니다. 공부하는 목적에는 맞으나 프로젝트 시간에 쫓기는 상황에서 알고리즘을 공부하고 있는 우를 범하지 말았으면 합니다. 어떤 것이 우선순위가높은지 잘 판단해야 합니다.

같이 일했던 개발자 중에 클래스, 인터페이스를 구조적으로 잘 만드는 개발자가 있었습니다. 빡빡한 프로젝트 일정 속에서 어느 날부터인가 일 진척도가 떨어지길래 봤더니 API에 1:1로 대응하는 클래스를 만들고 있었습니다. 외국에서 개발된 API라 사용법이 불편하기는 했는데 굳이 전부를 1:1로 만들지 않아도 되는데 숲을 보지 못하고 시간을 허비하고 있었습니다. 프로젝트 초기 스터디가 가능한 시간이 있을 때나 유지보수로 넘어가서 하면 좋을 일이었는데 프로젝트 중간에 사용법이 불편하다고 자기 입맛에 맞게 API에 1:1로 대응되게 100여 개의 함수를 만들고 있었던 것은 일의 순서, 무엇이 중요한지 파악하지 못해서 그런 것입니다. 나중에 범용 함수 하나로 통일했습니다. API라는 것이 함수명, request, response로 구성된 것이라 범용적으로 처리가 가능했습니다.

또 하나의 케이스는 변화에 익숙하지 않은 개발자입니다. 닷넷 개발사 개발팀장으로 근무할 때였는데 현재도 그렇지만 닷넷 개발자 구하기가 쉽지 않습니다. 지원한 이력서를 보던 중에 53세의 닷넷 개발자가 있었고 화려한 경력과 기술 등이 마음에 들어 면접을 보게 됐고 나이가 많음에도 하고자 하는 열정이 보여서 구인을 했습니다. 그런데 웹보다는 CS 프로그램 위주로 개발하시던 분이라 웹의 생태에 대해 이해도가 떨어지고 변화를 빨리 받아들이지 못하는 성향이어서 4개월이 지나서야 제대로 커뮤니케이션이 될 정도였습니다. 자신감은 충만했지만, 변화를 두려워(?)하여 회사에 도움이 되고자 했던 구인이 오히려 다른 개발자의 리소스를 할당하게 만드는 상황이 되었습니다. 그리고 간혹 직위는 대리인데 PM만큼 넓은 시야를 가진 사람이 있습니다. 나중에는 좋습니다. 그런데 이런 유형은 개발할 때 개발자가 신경 쓰지 않아도 될 부분을 신경을 써서 개발에 집중을 못 하는 경우가 있습니다. 큰 시야는 가지고 있지만 제대로 볼 수가 없으므로 마음만 다급하고 복잡합니다. 반대로 아주 좁은 시선을 가지고 있는 개발자가 있습니다. 소위 땅파기를 잘하는 개발자입니다. 숲을 봐야 하는데 나무만 보는 격입니다. 개발과 관련이 있지만, 특정 부분에 집착하거나 해결하지 못할 경우 혼자서 끝까지 해결하려고 하는 경우인데 이런 때에도 시간을 허비하게 됩니다. 사원, 대리, 과장, 차장, 부장 직위가 올라갈수록 사물을 보는 시야가 넓어지고 단락을 구분할 수 있게 되며 문제점을 빨리 파악을 할 수 있습니다. 선택을 빨리할 수 있고 우선순위를 빨리 결정할 수 있습니다. 

개발자로 오래 살아남으려면 사회 풍토도 바뀌어야 하지만 개발자 본인의 부단한 노력이 필요하고 약간의 운(시류)도 필요합니다. 6년을 넘게 재직했던 회사에서 이직해야 했을 때 “내가 갈 곳이 있을까?”라는 생각을 했습니다. 걱정도 많이 했는데 필요로하는 곳은 있었고 요즘은 개발을 빨리하느냐 잘하느냐 이런 것보다는 내가 가지고 있는 경험을 요구하는 것이 아닌가 생각이 됩니다. 신생 회사는 조직 세팅이라던가 개발 시스템 구축이라던가 한 명의 시니어가 멘토 역할, 길잡이 역할을 수행해주기를 바랍니다.

현재 재직 중인 회사에서 제가 나이가 제일 많습니다. 직책은 없습니다. 명함의 직위는 부장이고 이전 회사가 연구소 구조라서 수석 연구원이었기에 회사 직원들은 그냥변 수석님이라고 부릅니다. 직책이 없지만 웹, 모바일 모두 관여하고 있고 개발 시스템 구축, 스크럼 회의 등을 진행하고 있기에 제가 미치는 영향력은 적지 않습니다. 회사에서 앱 게임 출시를 목전에 두고 있는데 그 앱 게임과 통신을 위한 서버 전문 프레임워크를 구상하고 개발하여 실무에 사용하고 있고(앱 뿐만 아니라 하이브리드 앱, 모바일 웹, 웹에도 대응할 수 있도록 개발되어 있습니다. CodeIgniter기반) 매출이나 수익에 크게 신경 쓰지 않고 개발 쪽에만 전념하고 있습니다.

조직의 기대도 만족 시켜야 하고 제 꿈도 펼치기 위해서는 부단한 노력이 필요합니다. PHP 솔루션으로 신규 사업 제안도 하고 여러 가지 역할을 하고 있지만 제 본분은 개발자이고 제가 꿈꾸는 백발이 성성한 개발자가 되기 위해서 열심히 노력하고 있습니다. 언어의 종류와 관계없이 개발자의 길을 가고자 한다면 끊임없는 자기계발과 업무에 대한 공부가 필요하다는 것을 다시 한번 강조하고 싶습니다. 열정만 있는 개발자가 되지 말고 열정과 실력을 겸비한 개발자가 되세요.

대한민국의 모든 개발자 분들 힘내세요!
 

<한빛미디어 페이스북 놀러가기>

간단한 기술 서적을 읽고 내용을 공유합니다.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

제목: 스마트폰과 태블릿 호환을 위한 안드로이드 앱 프로그래밍

저자: 고강태

출판사: 한빛미디어

스마트폰과 태블릿 호환을 위한 안드로이드 앱 프로그래밍

이 책은 eBook, Free DRM  형태로  발행된 책으로 아래 사이트에서 구입할 수 있다.

http://bit.ly/SQIFc6

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


[도입 글]

최근 앱을 개발하면서 노이로제 처럼 다가오는 것이 멀티 해상도 지원이다.

그냥 480x800 해상도만 개발하고 앱을 출시하까해도 뭔가 캥기는 것이

최신 스마트폰들이 고행도를 지원하기 때문이다.

더욱이 구글에서 내놓은 넥서스 7과 같이 태블릿도 다양한 형태로 출시되고 있기에

멀티 해상도에 대한 지원을 간과할 수 없는 부분이 되고 있다.


그래서 개발 초기부터 멀티 해상도를 위한 레이아웃이나 이미지 컷을 준비하고

마지막에 레이아웃별로 포팅 작업을 하게된다...


이런 시점에 이 책은 한번 정도 주의 깊게 읽어볼 필요가 있다.

이런 멀티해상도와 태블릿용과 호환되는 앱을 개발하기 위해서 가추어야 할 것을 핵심만 요약해서 알려주기에 좋다. 

1부에서는 스마트폰 앱과 태블릿 앱 개발의 차이점

2부에서는 태블릿 앱 개발을 위한 안드로이드 프로그래밍

3부에서는 스마트폰과 태블릿 호환 앱 개발


목차 내용을 보다시피, 내용이 상당히 깔끔하다.

역사, 필요성등 군더더기가 없이 필요한 내용을 바로 볼 수 있다.

이것은 한빛 미니어가 내 세우는 eBook에 특징이라고 한다. 

500페이지의 내용 대신 핵심만 들어있는 100페이지의 책..

이런 점은 잘 선택한 편집 방향이라고 생각한다. 


이 책은 스마트폰과 태블릿의 호환성 관점에서 글을 기록하지만,

최신 안드로이드 OS가 4.0으로 어찌보면 최신 OS가 요구하는 개발방식을 

사용하는 안드로이드 개발을 소개한다고 봐도 될 듯하다.

그러면서 구형 OS에서 최신 기술을 적용하는 방법을 소개하는 것과 같은 개념으로

인식해도 되는 것이다. 


[책을 구체적으로 들여다 보자]

책의 내용을 보려면 목차를 보는 것이 빠르다 

목차는 다음과 같다.


1부 스마트폰 앱과 태블릿 앱 개발의 차이점

0 1 태블릿 앱 개발의 필요성 2

0 2 다양한 단말기에 호환 가능한 앱 개발 3

2.1 태블릿 앱 개발 시 고려할 사항 3

2.1.1 안드로이드가 지원하는 화면 크기 5

2.1.2 레이아웃 최적화를 위한 기본적인 프로그래밍 방법 6

2.1.3 설정 식별자를 이용한 레이아웃 최적화 7

2.1.4 앱이 지원할 화면 크기를 정확하게 manifest에 정의 9

2.2 화면호환 가이드 10

2.2.1 화면호환 모드 버전 11

2.2.2 화면호환 모드 사용 안 하기 12

2.2.3 화면호환 모드 사용하기 13

2.3 프래그먼트 가이드 14

2.3.1 프래그먼트 사용 시 주의사항 16

2.4 태블릿 레이아웃 가이드 17

2.4.1 새로운 크기 식별자 18

2.4.2 새로운 식별자 사용 예 19

2.4.3 화면 크기 선언 20

2부 태블릿 앱 개발을 위한 안드로이드 프로그래밍

0 3 태블릿 UI/UX의 특징 22

3.1.1 홀로그래픽 UI 22

3.1.2 기존 데스크톱 OS와 유사한 UX 24

3.1.3 편리한 입력 24

3.1.4 큰 화면에 적합한 레이아웃 25

0 4 태블릿 앱 프로그래밍 27

4.1 액티비티 27

4.2 액티비티 생명주기 32

4.3 프래그먼트 37

4.3.1 프래그먼트를 이용한 앱 개발 39

4.4 프래그먼트 동적 처리 45

4.4.1 main.xml 수정 45

4.4.2 분할된 프래그먼트를 하나로 전환 46

4.5 프래그먼트 생명주기 49

4.5.1 프래그먼트 생명주기 순환 49

4.6 프래그먼트 사이의 통신 58

4.6.1 Fragment2에 버튼 추가 58

4.6.2 Fragment2 클래스 수정 59

4.6.3 프래그먼트 동작 61

4.7 인터페이스를 통한 상호 통신 61

4.7.1 Fragment1에 인터페이스 선언 62

4.7.2 Fragment2 구현 65

4.7.3 HoneyActivity 수정 67

4.8 액션 바 69

4.8.1 액션 바 보이기/숨기기 69

4.8.2 액션 바에 액션 아이템 추가하기 72

4.8.3 액션 아이템과 앱 아이콘 수정 78

3부 스마트폰과 태블릿 호환 앱 개발

0 5 Support Library Package를 이용한 앱 개발 82

5.1.1 Android Support Library 사용 84

5.1.2 HoneyActivity 변경 89

5.1.3 Fragment1 변경 92

5.1.4 Fragment2 수정 94

0 6 멀티팬과 싱글팬을 이용한 앱 개발 99

6.1.1 레이아웃 재배치 100

6.1.2 Fragment2Activity 추가 104

6.1.3 Fragment2 클래스 수정 106

6.1.4 HoneyMessage 리스너 개선 108


책의 전체적인 흐름의 해상도 편화에 대한 대응을 위하여 필요한 내용을 여러 단계를 통해서 설명을 하게된다.

1. 레이아웃을 사용되는 속성값 들의 소개

- wrap_content, fill_parent,, match_parent, dp, sp

- 사이즈, 밀도, 해상도, 비율

- 프로젝트 res 폴더를 통해서 다양한 이미지 적재 방법

* 절대로 px과 같은 값을 레이아웃에 직접 정의하지 말것을 당부한다.


2. 메니페스트에 다양한 옵션들

- 화면모드

- support-screens, minSdk, TargetSdk

- 태블릿을 위한 새로운 식별자, sw<N>dp, w<N>dp, h<N>dp


4.Fragment

- 태블릿 또는 스마트폰에서 사용할 수 있는 프래그먼트 개발 방법 및 데이터 전송방법

   액티비가 중계 역활을 하는 프레그먼트간의 통신 방식 (아래에 좀더 내용을 기록했다.)

- 스마트폰, 태블릿 구분이 최신 개발 기술이다. 


5. 액션바 

- 허니컴 이상에서 지원하는 액션바 개발 방법

- 액션바 꾸미기 등

6. Support Library Package 

- 절대적으로 중요한 부분으로 최신 OS기능 특히 프래그먼트를 구현 OS에서 사용할 때 필수적인 lib로 

  이것을 사용방법을 안내한다.

- 최신 기능을 모두 포함하는 것은 아니지만, 프래그먼트 등 주요한 것이 OS 구분없이 사용 가능하게 해준다.


7. 멀티팬 처리

- 화면을 분활해서 구성할 수 있다.

- 작은 화면과 큰화면에 대응이 용이하다. 


[내용중에 기억할 사항들]

Fragment간에 통신 방식은 Activity가 중계해 주는 인테페이스 방식을 사용한다. 

(4.7 인터페이스를 통한 상호 통신)


<pre class="brush: java">

Fragment1 fragment1;

Fragment2 fragment2;

/** Called when the activity is first created. */

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

fragment1 = (Fragment1)getFragmentManager()

.findFragmentById(R.id.fragment1);

fragment2 = (Fragment2)getFragmentManager()

.findFragmentById(R.id.fragment2);

fragment1.setHoneyListener(this);

fragment2..setHoneyListener(this);

}

@Override

public void sendMessage(CharSequence msg) {

fragment2.setMessage(msg);

}

@Override

public String getNextMessage() {

return null;

}

</pre>


2.3.1 프래그먼트 사용 시 주의사항

다음은 프래그먼트 사용 시 주의해야 할 사항들입니다.

● 프래그먼트 클래스를 XML에 지정하면 교체가 안 됩니다.

● 프래그먼트는 FrameLayout의 ID를 지정하고 FrameTransaction을 사용해서

add, replace해야 합니다.

● 프래그먼트 사이의 데이터 전송에는 매개변수로 번들Bundle 객체를 사용합니다.

● FragmentTransaction을 사용할 때 반드시 commit()을 사용해야 합니다.

● FragmentTransation을 사용해서 프래그먼트를 추가할 때 애니메이션은 add,

replace를 지정하기 이전에 선언해야 합니다.

● FragmentTransaction의 addToBackStack() 메소드를 지정하지 않으면, Back

CHAPTER 02 다양한 단말기에 호환 가능한 앱 개발 가이드 17

Key 사용 시 해당 액티비티가 종료됩니다.

● 프래그먼트 하나가 다른 프래그먼트를 직접 컨트롤하도록 구현해서는 안 됩니다.

● 프래그먼트 내용을 변경하는 코드는 모두 해당 프래그먼트 클래스 내에 있어야

합니다.

● 프래그먼트와 액티비티의 통신을 위해 프래그먼트에서 리스너 인터페이스를 제

공하고, 이것을 액티비티에서 구현해서 사용합니다.

● 프래그먼트를 내장하고 있는 액티비티와 콜백 인터페이스를 통해서 앱 내부의

프래그먼트와 통신합니다.

● 이벤트 발생 시 콜백 인터페이스를 통해 자신을 호스팅하는 엑티비티에 내용을

전달하도록 구현합니다.


이상으로 책의 내용을 간단히 기록해 보았다.


어찌보면 책의 내용을 모두 기록한 것이 아니니, 소개 내용 자체가 부족할 수 있겠지만,

책 차제에는 충분한 설명, 코드 예제, 설명에 해당하는 그림등 이해하는데 부족함 없이 내용이 들어 있다.


끝으로 

이북이 장점이 Free PDF라서 DRM이 없기에, PC에서 스마트폰에서 넣고 다니면서 볼 수 있기에

매우 좋은 듯하다.

이렇게 eBook으로 소개되는 책이 점점 더 많이 늘어 나길 기대해 본다. 










'Android' 카테고리의 다른 글

Using the new Build System  (0) 2012.11.19
세로 SeekBar 예제 소스  (0) 2012.11.16
쓸만한 위젯 lib  (0) 2012.11.16
MIV에 대해서  (0) 2011.07.21
Application Class에 대해서  (0) 2011.07.15
MIV에 대해서 Android 2011. 7. 21. 09:54

MIV는 Mobile In Vechicel의 약자로 자동차에 모발일 기술이 접목되어 자동차 상태 및 제어를 스마트폰에서 할 수 있도록 하는 기술이다. 

- Application Class는 Context를 상속받는다.
- Application Class는 singletone 형태로 모든 컨포넌트와 클래스에서 접근이 용이하다.
- Application Class를 상속 받아서 정의하는 것과 사용이 매우 간단하다.
- 다른 SingleTone은 공통의 인스턴스로 사용하는 것을 보장하지 않는다.
- Process가 다르면 Application Class라 할지라도 다른 인스턴스를 갖게된다. 


[생성]
Application Class를 상속 받아서 클래스를 만든다.

Class MyClass extended Applicaiton {

}


[정의]
androidmenifast.xml에 등록한다.

<application 

      android:icon="@drawable/icon" 

      android:label="@string/app_name"

      android:name=".MyClass"

      >
---- 

 

[사용] 
인스턴스를 확보해서 사용하면 된다.

MyClass ACN = (MyClass)getApplicationContext();



 

'Android' 카테고리의 다른 글

Using the new Build System  (0) 2012.11.19
세로 SeekBar 예제 소스  (0) 2012.11.16
쓸만한 위젯 lib  (0) 2012.11.16
[책] 스마트폰과 태블릿 호환을 위한 안드로이드 앱 프로그래밍  (0) 2012.09.16
MIV에 대해서  (0) 2011.07.21