유니티에서 프라우드넷으로 네트워크를 구축할 때 위치동기화를 위한 추측항법 사용법


이 문서를 보기전에 필요한 지식 :

1. 프라우드넷 서버와 클라이언트 구축법.

2. 프라우드넷의 네트워크 전송 방식인 ReliableSend와 UnreliableSend의 차이점

3. 프라우드넷의 RMI, Proxy와 Stub에 대한 지식.

4. 프라우드넷을 통한 게임 네트워크에서 캐릭터 위치 동기화 하는 방법.

5. 유니티 프로젝트에서 프라우드넷을 사용하는 방법.



1. 캐릭터의 위치 동기화란

캐릭터가 움직일 수 있는 네트워크 게임에서 캐릭터의 위치란 아주 중요한 것이다. 상대방 캐릭터의 실제 위치가 내가 인식한 위치가 다르다면 문제가 될 것이다. 다음의 그림을 보자 : 


만약 상대방 캐릭터의 위치 동기화에 실패했다고 가정해보자. 파란색 원이 내 캐릭터이고 분홍색 원은 상대 캐릭터의 실제 위치이며 빨간 원은 위치동기화에 실패해서 상대 캐릭터가 잘못 이동한 위치라고 했을 때, 내가 보이는 대로 빨간색 적을 공격한다면 데미지가 적에게 들어가야할까? 내 입장에서 보이는대로 빨간색 상대를 공격해서 데미지가 들어간다면 나는 납득할 수 있을지 모르겠지만 상대방의 입장에서는 납득하기 어려울 것이다. 상대방이 보기에는 내가 다른 방향을 보고 공격하는데 자신한테 데미지가 들어오는 것으로 보일 것이기 때문이다.


이렇듯이 캐릭터의 위치 동기화는 중요하다. 여기에 자주 사용되는 방법은 자신의 캐릭터의 위치가 변경될 때마다 상대방의 클라이언트에 그 변경된 위치를 알려주는 것이다.


캐릭터의 위치를 다른 클라이언트에게 전송하는 Proxy

    public void SyncUnitPos(string unitName, UnityEngine.Vector3 pos, Quaternion rot)
    {
        RmiContext context = RmiContext.UnreliableSend;

        client.c2cMessageSender.SyncUnitPos(otherClientHostID, context, unitName, pos, rot);
    }


다른 클라이언트로부터 받은 캐릭터의 위치를 자신의 클라이언트에서 찾아서 적용하는 Stub

client.c2cMessageReceiver.SyncUnitMove = (HostID remote, RmiContext rmiContext, string unitName, UnityEngine.Vector3 pos, Quaternion rot) =>
{

Character moveCharacter = AllSpawnUnitDictionary[unitName];

moveCharacter.transform.position = pos;

moveCharacter.transfrom.rotation = rot;

};


위의 프록시와 스텁으로 단순하게 캐릭터의 위치를 보내고 받아서 적용하는 것이다. 이 방법은 단순하지만 중요한 단점이 있다. 그것은 바로 여러가지 상황에 따라 캐릭터의 위치를 전송받는 측에서의 캐릭터의 움직임이 끊어져 보일 수 있다는 것이다. 특히 캐릭터의 위치 동기화처럼 네트워크 전송량이 많은 것을 처리할 때는 호스트 간에 메시지를 주고받는 시간이 짧은 UnreliableSend를 사용하기 때문에 메시지의 도착의 확실성이 보장되지 않기 때문에 중간에 잃어버리는 메시지가 많다면 캐릭터의 움직임이 끊어져서 버벅거리면서 이동하는 것처럼 보이거나 텔레포트하는 것처럼 보일 확률이 높아진다.



2. 추측항법이란

이것을 해결하기 위한 방법이 바로 추측항법이다. 추측항법의 사전적 의미는 다음과 같다 :


가장 최근에 구한 정확한 선위를 기초로 선박의 침로와 항정에 의하여 현재의 선위를 추측 및 추정하거나 또는 출발지와 목적지의 경 · 위도를 알고 앞으로 취해야 할 침로 및 항정을 구하는 항법을 추측 항법이라고 한다. 선박이 항행 중에 가장 필요로 하는 것이 바로 선박의 현재 위치이다. 선박의 실측 위치를 얻을 수 없을 때에는, 가장 최근에 실제로 관측하여 구한 실측 위치를 기준으로 하여 그 후에 조타한 진침로와 선속 또는 기관의 회전수로 구한 항정에 의하여 선위를 결정하는 것을 선위의 추측이라 하고, 이와 같이 하여 결정된 선위를 추측위치라 한다.


[네이버 지식백과] 추측항법 [dead reckoning sailing, 推測航法] (선박항해용어사전, 한국해양대학교)


간단하게 말하면 가장 최근에 확인한 실제 위치를 기준으로 하여 현재의 속도와 방향을 더하여 현재 위치를 추정해내는 것이다. 실제로는 자신의 위치를 특정하기 어려운 항해에서 사용되는 방법인데 이것을 네트워크 게임의 개발에 응용한 것이다. 개념은 기존의 캐릭터의 위치와 방향을 가져와서 바로 적용하는 것에 더해서 캐릭터의 이동 방향과 속도를 함께 가져와서 다음 캐릭터 위치 동기화 신호가 오기전까지 받은 이동 방향과 속도를 이용해서 추정해낸 위치로 미리 캐릭터를 이동시키는 것이다.


우리는 이제 추측항법을 구현해야 하겠지만 다행이도 이러한 기능을 구현하는데 도움을 주는 클래스가 이미 프라우드넷에 구현이 되어있다. Nettention.Proud.PositionFollower 라는 클래스이다.


그리고 캐릭터의 위치를 동기화 하는 Proxy와 Stub은 다음과 같이 변경될 것이다.


캐릭터의 위치를 다른 클라이언트에게 전송하는 Proxy

    public void SyncUnitPos(string unitName, UnityEngine.Vector3 pos, UnityEngine.Vector3 vel, Quaternion rot)
    {
        RmiContext context = RmiContext.UnreliableSend;

        client.c2cMessageSender.SyncUnitPos(otherClientHostID, context, unitName, pos, vel, rot);
    }


다른 클라이언트로부터 받은 캐릭터의 위치를 자신의 클라이언트에서 찾아서 적용하는 Stub

client.c2cMessageReceiver.SyncUnitMove = (HostID remote, RmiContext rmiContext, string unitName, UnityEngine.Vector3 pos,  UnityEngine.Vector3 vel, Quaternion rot) =>
{

Character moveCharacter = AllSpawnUnitDictionary[unitName];
moveCharacter.MoveRemote(pos, vel, rot);

};


그냥 pos와 rot을 넣어주기만 하면 되던 이전과는 달리 처리할 것이 많으니 Character 클래스에 MoveRemote라는 함수를 만들었다.


Character 클래스에서 처리할 것

public class Character

{

// 이 캐릭터가 네트워크를 통해서 간접적으로 조종되는 캐릭터인가?

bool isRemote;

// 추측 항법을 도와줄 프라우드넷의 클래스이다.

Nettention.Proud.PositionFollower posFollower = new Nettention.Proud.PositionFollower();


// 네트워크의 stub을 받아 Follower의 위치를 설정한다

public void MoveRemote(Vector3 pos, Vector3 vel, Quaternion rot)

{

var npos = new Nettention.Proud.Vector3();
npos.x = pos.x;
npos.y = pos.y;
npos.z = pos.z;
      
var nvel = new Nettention.Proud.Vector3();
nvel.x = velocity.x;
nvel.y = velocity.y;
nvel.z = velocity.z;


posFollower.SetTarget(npos, nvel);

transform.rotation = rot;

}


// Follower에 따라서 캐릭터를 이동시킨다

public void FollowRemoteMove()

{

posfollower.FrameMove(Time.deltaTime);

var pos = new Nettention.Proud.Vector3();

var vel = new Nettention.Proud.Vector3();

posfollower.GetFollower(ref pos, ref vel);
unit.transform.position = new Vector3((float)pos.x, (float)pos.y, (float)pos.z);
unit.transform.rotation = Quaternion.Lerp(unit.transform.rotation, destRot, Time.deltaTime * 100);

}


void Update()

{

if (isRemote) FollowRemoteMove();

}

}


이 방법을 사용할 때는 자신의 캐릭터와 상대방의 캐릭터, 즉 직접 조종하는 캐릭터와 네트워크를 통해서 조종되는 캐릭터를 명확하게 구분해야 한다.

반응형
  1. 팩맨 2017.08.07 13:38

    도움받고 갑니다 감사합니다. ^^

+ Recent posts