Player와 MyPlayer 스크립트 두 개를 만들고 MyPlayer는 Player를 상속받도록 한다. (MyPlayer:나, Player:일반유저) 플레이어가 공통적으로 갖는 것들은 Player에다 넣어준다.
MyPlayer에서 이동 패킷을 만들고 서버로 보내주기 위해 NetworkManager에 Send함수를 새로 정의해주었다. NetworkManager는 유니티 상에서 게임오브젝트가 컴포넌트로 갖고 있기 때문에 GameObject.Find 함수를 이용해 찾아와주었다.
아래 코드와 같이 작성해주면 NetworkManager를 통해 이동 패킷을 일정 시간 마다 한번씩 Send해줄 것이다.
public class MyPlayer : Player
{
NetworkManager _network;
void Start()
{
StartCoroutine("CoSendPacket");
_network = GameObject.Find("NetworkManager").GetComponent<NetworkManager>();
}
void Update()
{
}
IEnumerator CoSendPacket()
{
while (true)
{
yield return new WaitForSeconds(0.25f); // 3초에 한번씩
C_Move movePacket = new C_Move();
movePacket.posX = UnityEngine.Random.Range(-50, 50);
movePacket.posY = 0;
movePacket.posZ = UnityEngine.Random.Range(-50, 50);
_network.Send(movePacket.Write());
}
}
}
public enum PacketID
{
S_BroadcastEnterGame = 1,
C_LeaveGame = 2,
S_BroadcastLeaveGame = 3,
S_PlayerList = 4,
C_Move = 5,
S_BroadcastMove = 6,
}
서버로부터 패킷을 받아오면 위와 같이 패킷의 id가 있는데 패킷의 id에 따라 처리해주는 핸들러도 각각 다르게 정의해줘야 한다.
이름에서 유추할 수 있듯이
class PacketHandler
{
public static void S_BroadcastEnterGameHandler(PacketSession session, IPacket packet)
{
S_BroadcastEnterGame pkt = packet as S_BroadcastEnterGame;
ServerSession serverSession = session as ServerSession;
PlayerManager.Inst.EnterGame(pkt);
}
public static void S_BroadcastLeaveGameHandler(PacketSession session, IPacket packet)
{
S_BroadcastLeaveGame pkt = packet as S_BroadcastLeaveGame;
ServerSession serverSession = session as ServerSession;
PlayerManager.Inst.LeaveGame(pkt);
}
public static void S_PlayerListHandler(PacketSession session, IPacket packet)
{
S_PlayerList pkt = packet as S_PlayerList;
ServerSession serverSession = session as ServerSession;
PlayerManager.Inst.Add(pkt);
}
public static void S_BroadcastMoveHandler(PacketSession session, IPacket packet)
{
S_BroadcastMove pkt = packet as S_BroadcastMove;
ServerSession serverSession = session as ServerSession;
PlayerManager.Inst.Move(pkt);
}
}
public class PlayerManager
{
MyPlayer _myPlayer;
Dictionary<int, Player> _players = new Dictionary<int, Player>();
public static PlayerManager Inst { get; } = new PlayerManager();
public void Add(S_PlayerList packet)
{
Object obj = Resources.Load("Player"); // 프리팹으로 만들어둔 플레이어 가져옴
foreach(S_PlayerList.Player p in packet.players) // S_PlayerList 안에 Player라는 리스트가 존재. 이 안에 나 포함 현재 접속되어 있는 플레이어 정보 담겨져 있음.
{
GameObject go = Object.Instantiate(obj) as GameObject;
if(p.isSelf)
{
MyPlayer myPlayer = go.AddComponent<MyPlayer>();
myPlayer.PlayerId = p.playerId;
myPlayer.transform.position = new Vector3(p.posX, p.posY, p.posZ);
_myPlayer = myPlayer;
}
else
{
Player player = go.AddComponent<Player>();
player.PlayerId = p.playerId;
player.transform.position = new Vector3(p.posX, p.posY, p.posZ);
_players.Add(p.playerId, player);
}
}
}
public void Move(S_BroadcastMove packet)
{
if (_myPlayer.PlayerId == packet.playerId)
{
_myPlayer.transform.position = new Vector3(packet.posX, packet.posY, packet.posZ);
}
else
{
Player player = null;
if(_players.TryGetValue(packet.playerId, out player))
{
player.transform.position = new Vector3(packet.posX, packet.posY, packet.posZ);
}
}
}
// 내가 접속한 상태에서 누군가 새로 접속했을 때
public void EnterGame(S_BroadcastEnterGame packet)
{
if (packet.playerId == _myPlayer.PlayerId)
return;
Object obj = Resources.Load("Player");
GameObject go = Object.Instantiate(obj) as GameObject;
Player player = go.AddComponent<Player>();
player.transform.position = new Vector3(packet.posX, packet.posY, packet.posZ);
_players.Add(packet.playerId, player);
}
public void LeaveGame(S_BroadcastLeaveGame packet)
{
if(_myPlayer.PlayerId == packet.playerId)
{
GameObject.Destroy(_myPlayer.gameObject);
_myPlayer = null;
}
else
{
Player player = null;
if(_players.TryGetValue(packet.playerId, out player))
{
GameObject.Destroy(player.gameObject);
_players.Remove(packet.playerId);
}
}
}
}
두 가지 중에서 선택을 하면 되는데 이번 강의에서는 첫번째 방법으로 구현한다.
PlayerManager에서 S_BroadcastMove 타입의 패킷이 왔다는 것은 1번 방법대로 진행된 것이기 때문에 _myPlayer에 접근하여 position을 변경해준다.
public void Move(S_BroadcastMove packet)
{
if (_myPlayer.PlayerId == packet.playerId)
{
_myPlayer.transform.position = new Vector3(packet.posX, packet.posY, packet.posZ);
}
else
{
Player player = null;
if(_players.TryGetValue(packet.playerId, out player))
{
player.transform.position = new Vector3(packet.posX, packet.posY, packet.posZ);
}
}
}
NetworkManager에서 매 프레임마다 하나의 패킷만 처리하는 식으로 이전에 구현했었는데 그 부분을 수정한다.
void Update()
{
IPacket packet = PacketQueue.Inst.Pop();
if(packet != null)
{
PacketManager.Inst.HandlerPacket(_session, packet);
}
}
NetworkManager.cs
void Update()
{
List<IPacket> list = PacketQueue.Inst.PopAll(); // 해당 프레임에 들어온 모든 패킷 다 꺼냄
foreach (IPacket packet in list)
PacketManager.Instance.HandlePacket(_session, packet);
}
PacketQueue.cs
public List<IPacket> PopAll()
{
List<IPacket> list = new List<IPacket>();
lock(_lock)
{
while (_packetQueue.Count > 0)
list.Add(_packetQueue.Dequeue());
}
return list;
}