반응형
서버 연동 마무리
기본 서버 수정
- 실제 게임에서 사용하는 서버처럼, GameRoom과, PDL을 수정한다.
class GameRoom :IJobQueue
{
List<ClientSession> _sessions = new List<ClientSession>();
JobQueue _jobQueue = new JobQueue();
List<ArraySegment<byte>> _pendingList = new List<ArraySegment<byte>>();
public void Push(Action job)
{
_jobQueue.Push(job);
}
public void Flush()
{
foreach (ClientSession s in _sessions)
s.Send(_pendingList);
//Console.WriteLine($"Flushed {_pendingList.Count} items");
_pendingList.Clear();
}
public void Broadcast(ArraySegment<byte> segment)
{
_pendingList.Add(segment);
}
public void Enter(ClientSession session)
{
// 플레이어 추가하고
_sessions.Add(session);
session.Room = this;
// 신입생한테 모든 플레이어 목록 전송
S_PlayerList players = new S_PlayerList();
foreach(ClientSession s in _sessions)
{
players.players.Add(new S_PlayerList.Player()
{
isSelf = (s == session),
playerId = s.SessionId,
posX = s.PosX,
posY = s.PosY,
posZ = s.PosZ,
});
}
session.Send(players.Write());
// 신입생 입장을 모두에게 알린다
S_BroadcastEnterGame enter = new S_BroadcastEnterGame();
enter.playerId = session.SessionId;
enter.posX = 0;
enter.posY = 0;
enter.posZ = 0;
Broadcast(enter.Write());
}
public void Leave(ClientSession session)
{
// 플레이어 제거하고
_sessions.Remove(session);
// 모두에게 알린다
S_BroadcastLeaveGame leave = new S_BroadcastLeaveGame();
leave.playerId = session.SessionId;
Broadcast(leave.Write());
}
public void Move(ClientSession session, C_Move packet)
{
// 좌표를 바꾸고
session.PosX = packet.posX;
session.PosY = packet.posY;
session.PosZ = packet.posZ;
// 모두에게 알린다
S_BroadcastMove move = new S_BroadcastMove();
move.playerId = session.SessionId;
move.posX = session.PosX;
move.posY = session.PosY;
move.posZ = session.PosZ;
Broadcast(move.Write());
}
}
<?xml version="1.0" encoding="utf-8" ?>
<PDL>
<packet name="S_BroadcastEnterGame">
<int name="playerId"/>
<float name="posX"/>
<float name="posY"/>
<float name="posZ"/>
</packet>
<packet name="C_LeaveGame">
</packet>
<packet name="S_BroadcastLeaveGame">
<int name="playerId"/>
</packet>
<packet name="S_PlayerList">
<list name="player">
<bool name="isSelf"/>
<int name="playerId"/>
<float name="posX"/>
<float name="posY"/>
<float name="posZ"/>
</list>
</packet>
<packet name="C_Move">
<float name="posX"/>
<float name="posY"/>
<float name="posZ"/>
</packet>
<packet name="S_BroadcastMove">
<int name="playerId"/>
<float name="posX"/>
<float name="posY"/>
<float name="posZ"/>
</packet>
</PDL>
- isSelf를 통해, 이게 내가 조작하는 캐릭터인지, 다른 사람이 조작하는 캐릭터인지 구분한다.
클라이언트 측 수정
- 기본 캐릭터 동작을 제어할 Player, 그리고 내가 조작하는 Player인 MyPlayer를 구현
public class Player : MonoBehaviour
{
public int PlayerId { get; set; }
}
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);
C_Move movePacket = new C_Move();
movePacket.posX = UnityEngine.Random.Range(-5, 5);
movePacket.posY = UnityEngine.Random.Range(-5, 5);
movePacket.posZ = 0;
_network.Send(movePacket.Write());
}
}
}
- 또한, 생겨날 모든 캐릭터들의 서버 동작을 제어할 PlayerManager를 둔다
public class PlayerManager
{
MyPlayer _myPlayer;
Dictionary<int, Player> _players = new Dictionary<int, Player>();
public static PlayerManager Instance { get; } = new PlayerManager();
public void Add(S_PlayerList packet)
{
Object obj = Resources.Load("Player");
foreach(S_PlayerList.Player p in packet.players)
{
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);
}
}
}
}
- 이렇게 하여, 이미 방에 들어온 플레이어, 새로 들어온 플레이어, 나의 동작 모두를 제어할 수 있게 된다.
구동 테스트
- 서버측에서 만든, DummyClient 10개와, 유니티 클라이언트 1개가 모두 무작위로 움직이게 잘 동작한다.
반응형
'C# > 네트워크' 카테고리의 다른 글
[c#][서버] Command 패턴 / JobQueue (0) | 2024.06.14 |
---|---|
[c#][서버] 채팅 테스트 (0) | 2024.06.14 |
[c#][서버] PacketGenerator (3) (0) | 2024.06.12 |
[c#][서버] PacketGenerator(2) (0) | 2024.06.12 |
[c#][서버] Packet Generator - 코드 작성 자동화 (2) | 2024.06.09 |