실전 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