C#/네트워크

[Unity][서버] 서버 - Unity 연동 (2)

goliot 2024. 6. 23. 01:31
반응형

서버 연동 마무리

기본 서버 수정

  • 실제 게임에서 사용하는 서버처럼, 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개가 모두 무작위로 움직이게 잘 동작한다.

 

 

반응형