Unity NGO로 구현한 Pong 멀티플레이 구조 정리
댓글 0
댓글을 작성하려면 로그인이 필요합니다.
아직 댓글이 없습니다. 첫 번째 댓글을 작성해보세요!
댓글을 작성하려면 로그인이 필요합니다.
아직 댓글이 없습니다. 첫 번째 댓글을 작성해보세요!
이 프로젝트는 Host가 Server와 Player 역할을 동시에 수행하고, 다른 플레이어가 Client로 접속하는 Host-Client 구조로 구현했다.
구조를 이해하는 핵심은 각 데이터를 누가 변경하고, 게임의 최종 결과를 누가 결정하는지 구분하는 것이다.
| 기능 | 담당 |
|---|---|
| 패들 입력과 이동 | 각 패들의 Owner Client |
| 공 이동과 충돌 판정 | Server |
| 점수와 승패 판정 | Server |
| 씬 전환과 오브젝트 생성 | Server |
| 화면과 UI 표시 | 각 컴퓨터에서 개별 처리 |
핵심 원칙: Client는 자신의 패들을 움직이고, Server는 게임의 최종 결과를 결정한다.
NGO의 기본 동작 방식
멀티플레이 게임이라고 해서 Host와 Client가 하나의 GameObject를 함께 사용하는 것은 아니다.
각 컴퓨터에는 Player, Ball, GameManager 등의 복사본이 따로 존재한다. NGO는 이 복사본들을 NetworkObjectId로 연결하고, 동기화가 필요한 데이터만 네트워크를 통해 전달한다.
주요 구성 요소는 다음과 같다.
NetworkManager: 연결, 접속자, Network Prefab, 네트워크 씬을 관리한다.UnityTransport: IP와 포트를 이용해 실제 네트워크 데이터를 전송한다.NetworkObject: 네트워크 오브젝트의 ID와 소유권을 관리한다.NetworkBehaviour: RPC, NetworkVariable, IsServer, IsOwner 등을 사용할 수 있게 한다.NetworkVariable: 위치처럼 일정 시간 동안 계속 유지되어야 하는 상태를 동기화한다.ServerRpc: Client가 Server에 요청을 보낼 때 사용한다.ClientRpc: Server가 Client들에게 결과나 명령을 전달할 때 사용한다.NetworkBehaviour를 상속했다고 해서 일반 변수나 Transform이 자동으로 동기화되는 것은 아니다. 동기화할 데이터는 NetworkVariable이나 RPC를 이용해 직접 지정해야 한다.
RPC의 방향은 다음처럼 기억하면 된다.
ServerRpc: Client → ServerClientRpc: Server → Clients프로젝트 전체 실행 흐름
전체적인 실행 순서는 다음과 같다.
Init 씬에서 NetworkManager를 생성한다.GameInitializer가 Menu 씬을 불러온다.StartHost(), Client는 StartClient()를 실행한다.Lobby 씬으로 이동시킨다.InGame 씬을 로드한다.PlayerPaddle 두 개와 Ball을 네트워크 스폰한다.NetworkVariable과 ClientRpc를 통해 모든 화면에 반영한다.Init에서 Menu로 이동할 때는 일반 SceneManager를 사용한다. 이 시점에는 아직 네트워크 세션이 시작되지 않았기 때문이다.
반면 Lobby에서 InGame으로 이동할 때는 NetworkManager.SceneManager를 사용한다. 그래야 Server와 연결된 모든 Client가 같은 씬으로 함께 이동한다.
컴포넌트 연결 구조
Init 씬의 Network Manager 오브젝트에는 다음 컴포넌트가 연결되어 있다.
NetworkManagerUnityTransportGameInitializerNetworkManager는 씬이 바뀌어도 사라지지 않고 전체 네트워크 세션을 관리한다.
Network Prefab 목록에는 PlayerPaddle과 Ball이 등록되어 있다. Server가 프리팹을 스폰하면 Client는 등록된 목록에서 같은 프리팹을 찾아 로컬에 생성한다.
Lobby Manager에는 다음 컴포넌트가 연결되어 있다.
NetworkObjectLobbyManager : NetworkBehaviourLobbyManager는 Ready 버튼을 처리하고, ServerRpc와 ClientRpc를 이용해 플레이어들의 준비 상태를 맞춘다.
Game Manager에는 다음 컴포넌트가 연결되어 있다.
NetworkObjectGameManager : NetworkBehaviourGameManager는 플레이어와 공을 생성하고, 점수와 승패를 관리한다.
PlayerPaddle 프리팹은 다음과 같이 구성되어 있다.
NetworkObjectPlayerPaddlePositionSyncSpriteRendererBoxCollider2DPlayerPaddle은 입력과 로컬 이동을 담당하고, PositionSync는 이동 결과를 다른 참가자에게 전달한다.
Ball 프리팹은 다음과 같이 구성되어 있다.
NetworkObjectBallPositionSyncSpriteRendererBall 프리팹 자체에는 Collider가 없다. Server에서 실행되는 Ball 스크립트가 Physics2D.Raycast()를 이용해 Wall, Paddle, ScoringZone의 Collider를 직접 검사한다.
연결과 플레이어 생성
UnityTransport의 포트를 7777로 설정한다.NetworkManager.StartHost()를 호출한다.Lobby 씬을 네트워크 방식으로 로드한다.NetworkManager.StartClient()를 호출한다.Host의 ApprovalCheck()는 최대 참가 인원을 두 명으로 제한한다.
이때 CreatePlayerObject를 false로 설정해 접속 순간에는 플레이어 오브젝트를 자동으로 만들지 않는다. 플레이어는 실제 게임이 시작되는 InGame 씬에서 GameManager가 직접 생성한다.
PlayerPaddle 프리팹을 Instantiate한다.SpawnAsPlayerObject(clientId)를 호출한다.Ball은 일반 NetworkObject.Spawn()으로 생성하기 때문에 Server가 Owner가 된다.
즉, 패들은 지정된 Client에게 소유권이 부여되지만 공은 Server가 소유하고 관리한다.
Lobby의 RPC 흐름
Lobby의 준비 상태는 일반 Dictionary에 저장된다.
일반 Dictionary는 NGO가 자동으로 동기화하지 않기 때문에 RPC를 이용해 각 참가자의 상태를 직접 맞춰야 한다.
Client가 Ready 버튼을 누르면 다음 순서로 처리된다.
SetClientIsReadyServerRpc()를 호출한다.SetClientIsReadyClientRpc()를 호출한다.InGame 씬을 로드한다.LobbyManager의 Owner는 Server다.
그러나 일반 Client도 이 오브젝트의 ServerRpc를 호출해야 한다. 따라서 Ready 요청 RPC에는 RequireOwnership = false가 사용된다.
Client가 Ready를 요청하면 Server가 상태의 원본을 변경하고, 그 결과를 다시 모든 Client에게 전달한다.
패들 위치 동기화
PlayerPaddle.Update()는 모든 컴퓨터에서 실행된다.
하지만 IsOwner 검사를 사용해 해당 패들의 Owner만 입력을 처리하도록 제한한다.
패들 이동 흐름은 다음과 같다.
Transform을 이동시킨다.PositionSync가 위치를 NetworkVariable에 기록한다.PositionSync가 전달받은 위치를 Transform에 적용한다.PositionSync에서 사용하는 NetworkVariable의 권한은 다음과 같다.
| 권한 | 대상 |
|---|---|
| 읽기 | 모든 참가자 |
| 쓰기 | 해당 NetworkObject의 Owner |
따라서 각 Client는 자신이 소유한 패들의 위치만 변경할 수 있다.
이 프로젝트는 NetworkTransform 대신 직접 만든 PositionSync를 이용해 위치를 동기화한다.
공, 충돌, 점수 동기화
공의 이동과 충돌 계산은 Server에서만 실행한다.
Physics2D.Raycast()로 Wall과 Paddle 충돌을 검사한다.ScoringZone에 닿으면 GameManager에 점수 처리를 요청한다.PositionSync의 NetworkVariable로 전달한다.점수의 원본도 Server에만 존재한다.
playerScores 배열을 변경한다.UpdateScoreTextClientRpc()로 점수를 전달한다.NetworkObject를 Despawn한다.EndGameClientRpc()로 승자의 Client ID를 전달한다.이 프로젝트의 동기화 방식 요약
| 대상 | 동기화 방식 |
|---|---|
| 네트워크 씬 전환 | NetworkManager.SceneManager |
| 패들 생성 | SpawnAsPlayerObject(clientId) |
| 공 생성 | NetworkObject.Spawn() |
| 공 제거 | NetworkObject.Despawn() |
| Ready 요청 | ServerRpc |
| Ready 결과 전달 | ClientRpc |
| 패들 위치 | Owner가 쓰는 NetworkVariable |
| 공 위치 | Server가 쓰는 NetworkVariable |
| 패들 색상과 시작 위치 | ClientRpc |
| 점수와 게임 종료 UI | ClientRpc |
| 공 충돌과 승패 판정 | Server에서만 실행 |
가장 중요한 기준은 다음과 같다.
NetworkVariable을 사용한다.IsOwner로 제한한다.현재 구현에서 주의할 점
GameManager의 연결 종료 이벤트 등록에는 오타가 있다.
OnClientDisconnected 핸들러는 OnClientConnectedCallback이 아니라 OnClientDisconnectCallback에 등록해야 한다.
현재 상태에서는 게임 도중 상대가 나가더라도 종료 처리가 실행되지 않을 수 있다.
OnNetworkSpawn()은 Server뿐만 아니라 Client에서도 실행된다.
따라서 UpdateScoreTextClientRpc()처럼 Server만 호출해야 하는 RPC는 반드시 IsServer 조건 안에서 호출해야 한다.
현재 구현에서는 Owner Client가 패들의 위치를 직접 결정한다.
입력 반응성은 좋지만 Server가 Client의 위치를 그대로 신뢰하기 때문에 속도 조작이나 순간이동에 취약하다.
학습용 프로젝트에는 적합하지만, 실제 서비스에서는 이동 속도와 위치 변화량을 Server가 검증하거나 Server 권한 이동 구조를 적용할 필요가 있다.
현재 PositionSync에는 보간 처리가 없다.
따라서 네트워크 상태에 따라 상대 패들과 공이 끊겨 보일 수 있다. 이후에는 NetworkTransform, Lerp 또는 별도의 보간 처리를 적용해 움직임을 부드럽게 만들 수 있다.
마무리
이 Pong 프로젝트의 핵심 네트워크 흐름은 다음 문장으로 정리할 수 있다.
Client가 자신의 패들을 움직여 위치를 전송하고, Server가 그 위치를 기준으로 공의 충돌과 점수, 승패를 결정한 뒤 결과를 모든 Client에게 전달한다.
이 구조를 이해하면 NGO의 핵심 개념인 NetworkObject, 소유권, 네트워크 스폰, RPC, NetworkVariable, Server 권한, 네트워크 씬 전환을 하나의 프로젝트 안에서 함께 정리할 수 있다.
Unity, Netcode for GameObjects, NGO, 멀티플레이, Pong, C#unity-ngo-pong-multiplayer-structure
XR을 활용한 게임 개발 3기(유니티) 수강생입니다. 곧 수료 하지만 앞으로 이곳에 가끔 저의 개발 경험이 나 지식 기록할까 합니다. 더 나아가 이 사이트가 제 개인위키의 역할을 할 수 있으면 좋겠습니다. 한국 게임 시장을 흔들겠습니다

게임 광고 수익은 단순히 광고를 붙이는 것이 아니라, 여러 광고 네트워크를 경쟁시켜 가장 높은 수익을 만드는 구조입니다.

안녕하세요. 플밍 4기 입니다. 게임 개발을 배우기 전 네트워크 엔지니어 도메인에서 익히고 배웠던 네트워크 이론에 대한 기초 입니다. 학습에 도움이 되길 바라며 공유 드립니다.
XR을 활용한 게임 개발 3기(유니티) 수강생입니다. 곧 수료 하지만 앞으로 이곳에 가끔 저의 개발 경험이 나 지식 기록할까 합니다. 더 나아가 이 사이트가 제 개인위키의 역할을 할 수 있으면 좋겠습니다. 한국 게임 시장을 흔들겠습니다



게임 광고 수익은 단순히 광고를 붙이는 것이 아니라, 여러 광고 네트워크를 경쟁시켜 가장 높은 수익을 만드는 구조입니다.


안녕하세요. 플밍 4기 입니다. 게임 개발을 배우기 전 네트워크 엔지니어 도메인에서 익히고 배웠던 네트워크 이론에 대한 기초 입니다. 학습에 도움이 되길 바라며 공유 드립니다.