실시간 이동 네트워크


개요

이 프로젝트는 수업 과제의 일환으로, 제공된 네트워크 프레임워크를 확장하여 클라이언트-서버 간 움직임 동기화, 입력 처리, 그리고 서버와 클라이언트 양쪽의 명령 송수신 시스템을 직접 구현했습니다. 실시간 멀티플레이 상호작용에 적합한 반응성 높고 모듈화된 설계를 구축하는데 중점을 두었습니다.

저장소 보기


클라이언트-서버 명령 처리 시스템 설계도

  • 흰색 테두리: 처리 흐름
  • 노란색 테두리: 네트워크 관련 클래스

🖧 서버 핵심 구성 요소

1. GameLogic 클래스

NetworkServerProcessing에서 호출되는 핵심 편의 함수를 제공하며 서버의 플레이어 상태를 관리합니다. 메시지를 직접 처리하지 않고, 연결된 플레이어 목록을 유지하며, 새로 접속한 클라이언트에 고유 seed를 할당하고, 연결, 연결 해제, 이동 같은 런타임 이벤트 갱신을 위한 데이터 조회를 지원합니다.

플레이어 등록, 제거, 조회를 위한 서버 측 함수를 제공합니다.

public class GameLogic : MonoBehaviour
{
	public int m_PlayerSeed = 1000;
	public List<PlayerData> m_ConnectedPlayers = new List<PlayerData>();
 
	void Start() { NetworkServerProcessing.SetGameLogic(this); }
	
	public PlayerData Add(int clientConnectionID);
	public PlayerData Remove(int clientConnectionID);
	public PlayerData Search(int playerSeed);
}

  • 핵심 함수들
    • PlayerData Add(int clientConnectionID):
      • 새 클라이언트가 연결될 때 호출됩니다. 고유한 seed를 할당하고 접속 세션 목록에 플레이어를 등록합니다. 초기화 과정에서 NetworkServerProcessing에 의해 호출됩니다.
    • PlayerData Remove(int clientConnectionID):
      • 접속한 클라이언트의 연결 해제 시 호출됩니다. 접속 세션 목록에서 플레이어를 찾아 제거하며, NetworkServerProcessing이 게임 상태를 갱신할 때 사용됩니다.
    • PlayerData Search(int playerSeed):
      • 고유한 seed를 기준으로 플레이어를 찾아 반환합니다. 서버가 게임 플레이 중, 이동이나 입력을 갱신 및 적용할 때 사용됩니다.

2. NetworkServerProcessing 클래스

서버 측의 핵심 명령어 처리기 역할을 합니다. 이 정적 클래스는 연결된 클라이언트로부터 메시지를 받아 처리하고, 프로토콜 식별자를 분석하여 적절한 로직을 실행합니다. 연결, 연결 해제, 이동 같은 상태를 갱신하도록 GameLogic과 연계합니다.

프로토콜 메시지를 분석하고 경로를 지정하며 전달하는 중앙 관리 함수를 제공합니다.

static public class NetworkServerProcessing
{
	static public void ReceivedMessageFromClient(string msg, int clientConnectionID, TransportPipeline pipeline);
	static public void SendMessageToClient(string msg, int clientConnectionID, TransportPipeline pipeline);
	
	static public void ConnectionEvent(int clientConnectionID);
	static public void DisconnectionEvent(int clientConnectionID);
}

  • 핵심 함수
    static public void ReceivedMessageFromClient(string msg, int clientConnectionID, TransportPipeline pipeline)
    {
        string[] csv = msg.Split(',');
        int signifier = int.Parse(csv[0]);
        
        switch (signifier)
        {
    		case ClientToServerSignifiers.PTC_PLAYER_MOVE:
    		{
    		    string seed = csv[1];
    		    string posX = csv[2], posY = csv[3], posZ = csv[4];
    		
    		    // 서버 측 위치 데이터 업데이트
    		    gameLogic.Search(int.Parse(seed))?.SetData(posX, posY, posZ);
    		
    		    // 업데이트된 위치를 모든 연결된 클라이언트에 전달
    		    string msgOut = $"{ServerToClientSignifiers.PTS_PLAYER_MOVE},{seed},{posX},{posY},{posZ}";
    		    foreach (PlayerData data in gameLogic.m_ConnectedPlayers)
    		        SendMessageToClient(msgOut, data.m_ClientConnectionID, TransportPipeline.ReliableAndInOrder);
    		}
    		break;
    		
    		case ClientToServerSignifiers.PTC_PLAYER_MOVE2:
    			// [생략] 위의 입력 기반 버전
    			break;
    	}
    }
    클라이언트로부터 수신된 메시지를 처리하고, 식별자를 분석해 실시간 서버 측 명령을 실행합니다. 세션 및 이동을 갱신 및 적용하기 위해 GameLogic과 연계합니다.

🎮 클라이언트 핵심 구성 요소

1. GameLogic 클래스

NetworkClientProcessing에서 호출되는 핵심 게임플레이 처리 함수를 제공하여 클라이언트에서 플레이어 객체 상태를 관리합니다. 메시지를 직접 분석하기보다는, 서버 지시에 따라 생성된 플레이어를 관리하고 위치를 갱신하며 연결 해제 시 제거하는 역할을 합니다.

플레이어의 생성, 동기화, 제거를 위한 클라이언트 측 함수를 제공합니다.

public class GameLogic : MonoBehaviour
{
	public Player m_prefabPlayer;
	public Player m_prefabOthers;
	public List<Player> m_ConnectedPlayers = new List<Player>();
 
	void Start() { NetworkClientProcessing.SetGameLogic(this); }
 
	public void SpawnMySelf(int mySeed, Vector3 position);
	public void SpawnOthers(int otherSeed, Vector3 position);
	public void MovePlayer(int movedPlayerSeed, Vector3 targetPos);
	public void MovePlayer(int movedPlayerSeed, Vector3 targetPos, Vector2 inputKeys);
	public void OtherPlayerLeft(int leftPlayerSeed);
}


2. NetworkClientProcessing Class

클라이언트 측의 핵심 명령 처리기 역할을 합니다. 이 정적 클래스는 서버로부터 메시지를 받아 프로토콜 식별자를 분석하여 적절한 게임 로직을 실행합니다. 서버 기반 상태 변경에 맞춰 플레이어를 생성, 동기화, 제거하기 위해 GameLogic과 연계하여 동작합니다.

프로토콜 메시지를 분석하고 경로를 지정하며 전달하는 중앙 관리 함수를 제공합니다.

static public class NetworkClientProcessing
{
	static public void ReceivedMessageFromServer(string msg, TransportPipeline pipeline);
	static public void SendMessageToServer(string msg, TransportPipeline pipeline);
 
	static public void ConnectionEvent();
	static public void DisconnectionEvent();
}

  • 핵심 함수
    static public void ReceivedMessageFromServer(string msg, TransportPipeline pipeline)
    {
    	string[] csv = msg.Split(',');
    	int signifier = int.Parse(csv[0]);
     
    	switch (signifier)
    	{
    		case ServerToClientSignifiers.PTS_CONNECTED_NEW_PLAYER:
    			// [생략] 로컬 플레이어 초기화 및 생성
    			break;
    		case ServerToClientSignifiers.PTS_CONNECTED_NEW_PLAYER_RECEIVE_DATA:
    			// [생략] 서버에서 기존 플레이어 데이터를 받아 생성
    			break;
    		case ServerToClientSignifiers.PTS_CONNECTED_PLAYERS_RECEIVE_NEW_PLAYER_DATA:
    			// [생략] 다른 클라이언트에 새로 접속한 원격 플레이어 생성
    			break;
    		case ServerToClientSignifiers.PTS_PLAYER_MOVE:
    			// [생략] 원격 플레이어 위치 업데이트 (Type A)
    			break;
    		case ServerToClientSignifiers.PTS_PLAYER_MOVE2:
    			// [생략] 원격 플레이어 위치 및 입력 업데이트 (Type B)
    			break;
    		case ServerToClientSignifiers.PTS_PLAYER_LEFT:
    			// [생략] 연결이 끊긴 플레이어를 씬에서 제거
    			break;
    	}
    }
    서버로부터 수신된 메시지를 처리하고, 식별자를 분석해 클라이언트 측에 적용합니다. 생성, 이동, 플레이어 제거를 위해 GameLogic과 연계합니다.

⚠️ 참고: 로직은 생략되어 있습니다. 전체 구현은 저장소에서 확인할 수 있습니다.


맺는 말

이 프로젝트는 모듈화된 프로토콜 기반실시간 멀티플레이어 동기화 방식을 보여줍니다. 서버-클라이언트 간 로직을 일치시키고 명확한 메시지 처리 방식을 통해, 확장 가능하고 반응성이 뛰어난 플레이어 이동 시스템의 탄탄한 기반을 제공합니다. 또한 클라이언트-서버 구성실시간 게임 설계의 기본 개념에 대한 제 이해를 반영했습니다.