GameMode, GameState, PlayerController, UserWidget, Pawn

박정훈·2025년 3월 24일

언리얼 네트워크

목록 보기
3/7

💥주요 클래스별 역할과 기능

✅GameMode (게임 모드)

  • 핵심 기능: 게임의 규칙과 흐름을 총괄하는 관리자 클래스입니다. 게임이 시작되면 한 번만 생성되며, 해당 레벨에서 게임 규칙, 승패 조건, 점수 계산, 플레이어 스폰 등의 로직을 담당합니다​
    FORUMS.UNREALENGINE.COM
    예를 들어 FPS 게임에서 점수 계산이나 라운드 관리 같은 전역 규칙을 GameMode가 처리합니다.

  • 일반적인 사용 예시: 새로운 플레이어가 접속하면 GameMode가 플레이어용 Pawn(캐릭터)과 PlayerController를 생성하고 연결해줍니다. 또한 게임 시작/종료 같은 이벤트 제어나 특정 시간이 흐르면 승리팀 결정 등 게임 상태 전환 로직을 GameMode에 작성합니다. 게임 모드를 변경하면 게임의 규칙(예: 팀 데스매치, 깃발 뺏기 등)이 바뀌는 효과를 줍니다.

  • 관리하는 것들: GameMode는 해당 레벨에서 사용될 기본 클래스들을 지정합니다. 예를 들어 DefaultPawnClass, PlayerControllerClass, HUDClass 등을 설정하여 게임이 시작될 때 어떤 Pawn, 어떤 PlayerController와 HUD(UI)를 쓸지 결정합니다​
    FORUMS.UNREALENGINE.COM
    또한 플레이어 스폰 지점 관리 및 매치 상태(Match State) 관리도 합니다 (AGameMode에는 경기 진행 상태를 나타내는 상태 머신이 내장되어 있음). 다만 GameMode는 서버에서만 존재하고 클라이언트에는 존재하지 않기 때문에, 클라이언트가 알아야 할 정보는 GameState에 전달하여 동기화합니다​
    REDDIT.COM

✅GameState (게임 상태)

  • 핵심 기능: GameMode가 가진 게임 진행 정보를 전체 플레이어들에게 공유할 목적의 클래스입니다. 쉽게 말해 게임의 현재 상태를 담고 있으며, 서버와 클라이언트 모두에 존재합니다. GameMode가 관리하는 정보를 복사하거나 요약해서 가지고 있고, 이를 모든 클라이언트와 동기화(Replicate)합니다​
    REDDIT.COM

  • 일반적인 사용 예시: 남은 시간, 전체 점수, 현재 라운드 상태 등 게임 세션 전반의 데이터를 저장합니다. 예를 들어 시간제한이 있는 경기에서는 남은 시간을 GameState에 저장하고, 모든 클라이언트의 화면에 시간이 동일하게 보이도록 합니다. 점수판(스코어보드) 정보를 유지해 각 플레이어의 점수, 킬/데스 등을 PlayerState와 함께 관리하고, 게임이 끝났을 때 승패 판단에도 활용됩니다.

  • 관리하는 것들: GameState는 전체 게임에 대한 상태 데이터를 관리합니다. 대표적으로 모든 플레이어들의 PlayerState 목록을 관리합니다 (PlayerState는 각 플레이어의 점수나 이름 등 개별 정보를 담는 별도 객체). GameMode가 변경하는 전역 변수(예: 현재 게임 단계, 점수 등)를 받아 저장하며, 이를 클라이언트와 자동으로 동기화해줍니다​
    REDDIT.COM
    즉, GameMode가 “서버 전용 두뇌”라면, GameState는 그 결과를 “모두와 공유하는 게시판” 역할로 이해할 수 있습니다.

✅PlayerController (플레이어 컨트롤러)

  • 핵심 기능: PlayerController는 플레이어(사용자)의 입력을 게임 세계에 전달하는 중계자입니다. 쉽게 말해 플레이어 자신을 나타내는 컨트롤러로서, 키보드/마우스/패드 입력을 받고 이를 처리하거나 Pawn에 명령을 내립니다. 또 한편으로 네트워크 플레이 시 플레이어별 통신 창구 역할도 합니다.

  • 일반적인 사용 예시: 1인칭/3인칭 게임에서 카메라 제어나 입력 반응을 PlayerController에서 처리합니다. 예를 들어 사용자가 점프 키를 누르면 PlayerController가 이를 감지하여 서버의 PlayerController로 신호를 보내고, 서버가 해당 플레이어의 Pawn에게 “점프” 명령을 수행하게 합니다. 또한 플레이어가 UI를 열거나 메뉴를 여는 입력도 Controller에서 받아서 UI(UserWidget)를 띄우는 작업을 수행할 수 있습니다. Pawn이 파괴되거나 사망해도 PlayerController는 계속 남아있기 때문에, 새로운 Pawn을 생성하여 다시 소유(Possess)하게 함으로써 리스폰(respawn) 기능을 구현합니다.

  • 관리하는 것들: PlayerController는 자신이 조종할 Pawn을 소유하고 관리합니다. 한 명의 플레이어당 하나의 PlayerController가 있으며, 그 컨트롤러가 현재 조종 중인 Pawn을 가리킵니다​
    FORUMS.UNREALENGINE.COM
    또한 PlayerController는 카메라(PlayerCameraManager)와 입력 컴포넌트를 관리하며, 플레이어별로 HUD(UI 화면)를 생성하는 역할도 합니다. 네트워크 관점에서 서버는 모든 PlayerController를 가지지만, 각 클라이언트는 자기 자신의 PlayerController만 복제받아 가지고 있습니다​
    REDDIT.COM
    이를 통해 각 클라이언트는 자신의 컨트롤러로만 조작이 가능하고, 서버는 모든 플레이어의 컨트롤러 입력을 조율합니다. PlayerController는 또 RPC(Remote Procedure Call)를 사용해 클라이언트-서버 간 메시지 전달을 합니다 (예: 클라이언트 -> 서버로 공격 명령, 서버 -> 특정 클라이언트로 UI 팝업 명령 등).

✅Pawn (폰, 플레이어 캐릭터)

  • 핵심 기능: Pawn은 게임 세계 속에서 움직이고 상호작용하는 실제 캐릭터 또는 물체입니다. 플레이어가 직접 조종할 수 있는 플레이어 캐릭터(Character)나 탈것, 드론 등이 모두 Pawn의 하위 클래스입니다. Pawn은 PlayerController에 의해 소유(possess)될 수 있으며, 소유되고 있을 때 플레이어 입력에 반응해 움직이거나 행동합니다​
    FORUMS.UNREALENGINE.COM

  • 일반적인 사용 예시: FPS/TPS 게임의 주인공 캐릭터, 레이싱 게임의 차량, RTS의 유닛 등이 Pawn으로 구현됩니다. Pawn 클래스(특히 Character)는 이동 기능을 내장하고 있어 이동 입력 처리, 중력/충돌 처리 등을 합니다​
    FORUMS.UNREALENGINE.COM
    게임 플레이 중 점프, 달리기, 공격 애니메이션 등 다양한 동작을 Pawn에 구현하고, PlayerController로부터 입력을 받아 실행합니다. 또한 Pawn은 AI의 조종을 받을 수도 있어서, AIController가 Pawn을 소유하면 NPC로 동작하게 됩니다.

  • 관리하는 것들: Pawn은 자신의 상태(예: 체력, 스태미나)와 동작 로직(예: 움직이는 방법, 피격 시 반응)을 관리합니다. Pawn은 PlayerController와 연결되어 있어 Controller가 전달한 입력(전진, 공격 등)을 실제 행동으로 바꿉니다. 멀티플레이에서는 Pawn(및 하위 Character)이 네트워크 복제 설정이 되어 있으면, 서버에서의 위치/상태 변화가 모든 클라이언트에 복제됩니다​
    REDDIT.COM
    따라서 모든 클라이언트는 다른 플레이어들의 Pawn을 볼 수 있고, 특정 플레이어가 조종하는 Pawn은 그 플레이어의 PC에 의해 업데이트(Autonomous Proxy)되며, 다른 플레이어들에게는 움직임 등이 시뮬레이션(Proxies)되어 보입니다. Pawn은 보통 자신의 PlayerState(플레이어별 상태 데이터 객체)를 참조하여 해당 플레이어의 점수나 이름 등을 필요 시 활용합니다.

✅UserWidget (유저 위젯, UI 요소)

  • 핵심 기능: UserWidget은 UMG(Unreal Motion Graphics) 시스템의 UI 클래스입니다. 게임 화면에 표시되는 체력바, 맵, 인벤토리 창 등의 사용자 인터페이스(UI)를 만들기 위한 것입니다. UserWidget은 화면에 그려지는 위젯이며, 논리적으로는 Actor가 아니기 때문에 월드에 존재하는 게임 객체와는 별도로, 플레이어의 화면에서만 동작합니다.

  • 일반적인 사용 예시: 게임이 시작할 때 각 플레이어의 화면에 HUD(Head-Up Display)를 표시하기 위해 UserWidget을 생성하여 뷰포트에 추가합니다. 예를 들어 체력바 위젯은 PlayerController (혹은 HUD 클래스)가 현재 플레이어의 Pawn 체력을 가져와 화면에 표시하고, 미니맵 위젯은 GameState나 Pawn의 위치 정보를 활용하여 그립니다. 메뉴 화면, 인벤토리 창, 퀘스트 로그 등도 모두 UserWidget으로 만들어지며, 필요 시 생성 및 제거하거나 값을 업데이트하면서 사용됩니다.

  • 관리하는 것들: UserWidget 자체는 UI 요소의 배치와 표시 업데이트를 관리합니다. 텍스트, 이미지, 프로그래스바 등의 하위 위젯을 포함하여 화면에 어떻게 보일지를 정의합니다. 플레이어의 특정 데이터(예: 체력, 탄약) 표시를 위해 보통 Pawn이나 PlayerState, GameState의 데이터를 읽어와 반영하며, Tick이나 Event로 갱신합니다. 중요하게도, UserWidget은 클라이언트 전용으로 각 클라이언트의 화면에서만 생성 및 관리됩니다. 서버는 UI를 생성하지 않고, 서버-클라이언트 간에 UI자체를 보내지도 않습니다. (필요한 데이터만 서버가 보내주고, 클라이언트가 그 데이터를 받아 자신의 UI에 표시합니다.) 예를 들어 체력이 변하면 서버의 Pawn이 체력 변화를 복제하고, 클라이언트에서는 그 값을 받아 자신의 체력바 위젯을 업데이트하는 식입니다. 전통적으로 언리얼에서는 AHUD라는 HUD 클래스도 있는데, 이것 역시 오직 로컬 owning client에만 존재하며​
    WIZARDCELL.COM
    UUserWidget은 그러한 HUD에서 화면에 추가하는 형태로 쓰이거나 PlayerController가 직접 생성하여 사용합니다. 요약하면 UserWidget/UI는 네트워크로 동기화되지 않고, 각 클라이언트가 자체적으로 관리하도록 디자인되어 있습니다.

💥클래스들 간의 상호 작용

언급된 클래스들은 각각 독립적으로 동작하는 것처럼 보이지만, 게임 플레이를 구현하기 위해 서로 밀접하게 연동됩니다. 주요 상호작용 관계는 다음과 같습니다:

  • GameMode ↔ GameState: GameMode(서버 전용)는 게임 진행 정보를 관리하고 결정합니다. 그리고 이러한 정보를 GameState에 반영합니다. 예를 들어 GameMode에서 점수가 올라가면 GameState의 점수 변수를 증가시키고, GameState는 그 값을 클라이언트들에게 복제하여 모두가 동일한 점수를 보게 합니다​
    REDDIT.COM
    GameMode는 자신이 가진 게임 규칙 데이터를 직접 클라이언트에 공유할 수 없기 때문에, GameState를 통해 간접적으로 클라이언트들과 소통한다고 볼 수 있습니다. 클라이언트들은 GameState를 참조하여 현재 게임 상황(점수, 남은 시간 등)을 알 수 있습니다.

  • GameMode → PlayerController/Pawn: GameMode는 새로운 플레이어가 참여하면 해당 플레이어 전용의 PlayerController를 생성합니다​
    WIZARDCELL.COM
    또한 게임 시작 시 또는 플레이어가 사망 후 재등장(respawn) 시 기본 Pawn(Character)를 생성하여 해당 PlayerController가 Possess(소유)하도록 연결해줍니다​
    WIZARDCELL.COM
    이 과정에서 GameMode는 어떤 Pawn 클래스를 쓸지, 어디에 스폰할지 등을 결정합니다. 요컨대, GameMode는 게임 참가자의 Controller와 Pawn을 짝지어 세팅해주는 역할을 합니다.

  • PlayerController ↔ Pawn: PlayerController와 Pawn은 플레이어 한 명의 분신을 이룹니다. PlayerController가 Pawn을 소유하면, 이후에는 PlayerController가 입력을 받아 Pawn에게 전달하여 움직이게 합니다​
    FORUMS.UNREALENGINE.COM
    예를 들어 사용자가 앞으로 가기 키를 누르면 PlayerController에서 이를 감지하여 Pawn의 MoveForward 함수를 호출하고, Pawn은 자신의 위치를 변경합니다. 반대로 Pawn의 상태가 변하면 (예: 체력 감소) PlayerController나 UI에 이를 알려주거나 PlayerState에 기록할 수도 있습니다. Pawn이 파괴되면 PlayerController는 소유를 잃지만 여전히 존재하며, 이때 GameMode의 로직에 따라 새로운 Pawn을 생성해 다시 PlayerController에 연결할 수 있습니다. 이런 구조 덕분에 플레이어의 캐릭터가 바뀌어도(예: 사람 ↔ 차량) 컨트롤러는 지속되어 일관된 입력 처리가 가능합니다.

  • PlayerController ↔ UserWidget(UI): PlayerController는 게임 시작 시 자신의 HUD나 UI 위젯을 생성하는 책임을 지기도 합니다. 예를 들어 로컬(Player) Controller의 BeginPlay에서 CreateWidget을 호출하여 UserWidget(예: 체력/탄약 표시 UI)을 만들고 화면에 추가합니다. 이렇게 생성된 UI 위젯은 PlayerController가 가지고 있는 Pawn이나 PlayerState의 정보를 실시간으로 받아와 표시합니다. PlayerController는 또 UI와의 상호작용(예: 사용자가 UI 버튼 클릭)을 처리하여 게임 세계에 반영하거나, UI를 켜고 끄는 토글을 구현하기도 합니다. 한편, 서버에서 어떤 이벤트가 발생하여 특정 플레이어의 화면에 메시지를 띄우고 싶다면, 서버의 PlayerController에서 클라이언트 RPC를 호출해 해당 클라이언트의 UI 위젯 함수를 실행시키는 방법을 씁니다. 요약하면 PlayerController는 게임 세계와 UI 사이의 다리 역할을 하여, 플레이어에게 정보를 보여주고 입력을 받아들이는 창구가 됩니다.

  • GameState ↔ PlayerState ↔ UI: GameState는 여러 플레이어들의 공용 정보를, PlayerState는 각 플레이어 개별 정보를 나타냅니다. 예를 들어 GameState가 전체 남은 시간을 가지면, PlayerState는 각 플레이어의 점수를 가질 수 있습니다. 이러한 정보들은 모두 클라이언트에 복제되므로, 클라이언트의 UI(예: 스코어보드 위젯)는 GameState의 게임 시간이나 각 PlayerState의 점수 데이터를 읽어와 표시합니다. 즉, UI는 필요한 경우 GameState/PlayerState를 통해 다른 플레이어들의 정보까지 표시할 수 있습니다. (질문 범위를 벗어나지만, 일반적으로 PlayerState는 Pawn이 파괴되어도 지속되므로 플레이어의 누적 점수 등을 유지하는 데 쓰입니다.)

이와 같이 각 클래스들은 서버에서는 GameMode를 중심으로, 클라이언트에서는 PlayerController를 중심으로 상호작용하며 게임을 움직입니다. GameMode는 월드의 구성과 규칙을 관리하고, PlayerController는 플레이어视点에서 세계와 소통하며, Pawn은 세계 내 물체로서 움직이고, GameState는 상태를 동기화하고, UserWidget은 화면에 정보를 표시하는 식입니다.

💥싱글플레이 vs 멀티플레이에서의 클래스 동작 비교

같은 게임이라도 싱글플레이(로컬에서 혼자 플레이)와 멀티플레이(특히 리슨 서버, 플레이어 중 한 명이 호스트가 되어 서버를 겸하는 방식)에서는 위 클래스들의 생성과 분포, 역할에 차이가 있습니다. 아래에서 각 시나리오별로 어떤 객체가 어디서 생성되는지 그리고 서버-클라이언트 구조에서 데이터가 어떻게 흐르는지를 설명하겠습니다.

✅싱글플레이의 경우

싱글플레이에서는 게임을 구동하는 주체가 한 개뿐이므로, 개념적으로 로컬 플레이어 = 서버라고 볼 수 있습니다. 이 경우:

  • GameMode: 로컬 게임 시작 시 한 개의 GameMode 인스턴스가 생성됩니다. 이는 플레이어의 로컬 머신에서 돌아가며, 모든 게임 규칙 로직을 처리합니다. (멀티플레이의 서버 역할을 혼자 수행한다고 보면 됩니다.)

  • GameState: GameMode와 함께 하나 생성되며, 로컬에서 게임 상태를 저장합니다. 싱글플레이에서는 굳이 네트워크 동기화가 필요 없지만, 내부적으로 GameState를 활용하여 게임 진행 정보를 관리할 수 있습니다 (클라이언트=서버이므로 동일한 객체라고 생각해도 무방).

  • PlayerController: 플레이어가 하나이므로 한 개의 PlayerController가 생성됩니다. 이 컨트롤러가 로컬 플레이어의 입력을 받아 Pawn을 움직입니다. 싱글플레이에서는 이 PlayerController가 서버이자 클라이언트 역할을 동시에 하기 때문에, 특별히 RPC를 구분할 필요 없이 바로 Pawn을 조작합니다. (내부적으로는 서버 권한이 있으므로 곧바로 Pawn을 움직이고 결과를 화면에 보여줍니다.)

  • Pawn: GameMode의 설정에 따라 플레이어 시작 위치에 기본 Pawn이 스폰되고, PlayerController가 이를 Possess합니다. 플레이어는 이 Pawn을 움직이며 게임을 진행합니다. 싱글플레이에서는 Pawn의 상태 변경이 즉시 로컬 화면에 반영됩니다 (네트워크 지연이 없으므로 바로바로 반응).

  • UserWidget: 플레이어의 화면에 표시할 UI 위젯들을 로컬에서 생성합니다. 예를 들어 시작 시 HealthBar 위젯을 만들어 화면에 올려놓고, 게임 진행 중 필요에 따라 업데이트합니다. 이때 필요한 데이터(체력 값 등)는 동일 프로세스 내 Pawn이나 GameState로부터 직접 가져옵니다. 싱글플레이에서는 모든 것이 한 곳에서 돌아가기 때문에 자료를 주고받는 구조를 깊게 생각하지 않아도 됩니다. (GameMode도 동일 프로세스에 있고, PlayerController도 마찬가지니 그냥 함수 호출로 접근 가능).

요약하면, 싱글플레이에서는 모든 클래스 객체들이 한 곳(로컬 머신)에 존재하며, 서로 직접 참조하고 함수 호출로 상호작용합니다. 서버-클라이언트 구분이 없기 때문에 GameMode와 PlayerController 간에도 제약 없이 통신할 수 있고, GameState도 그냥 로컬 데이터일 뿐입니다. 다만 Unreal 엔진은 싱글플레이에서도 멀티플레이 구조를 따라 설계되어 있어서, 내부적으로는 서버/클라이언트 개념이 적용되어 있지만(로컬도 서버 권한으로 동작), 개발자가 신경쓰지 않고 단순히 로컬 객체 다루듯 하면 됩니다.


✅멀티플레이 (리슨 서버)의 경우

리슨 서버 방식 멀티플레이에서는 한 플레이어가 호스트(서버)가 되고, 나머지 플레이어들은 클라이언트로 접속합니다. 이 구조에서는 클래스의 인스턴스들이 서버와 각 클라이언트에 분산되어 존재합니다. 핵심 포인트는 서버가 권한을 가지고 게임을 진행하며, 클라이언트는 자신의 입력을 서버에 보내고 서버의 결정을 받아 보는 식으로 동작한다는 것입니다. 아래는 멀티플레이 상황에서 각 클래스의 생성 위치와 역할 변화입니다:

  • 💢서버(호스트) 측:

    • GameMode는 오직 서버에만 생성됩니다 (호스트 플레이어의 게임 인스턴스 내). 모든 게임 규칙 로직은 서버에서 실행됩니다​
      REDDIT.COM
      예를 들어 점수 계산, 승리 판정, 새로운 라운드 시작 등의 코드는 서버 GameMode가 처리하고, 클라이언트에는 GameMode 객체가 아예 없습니다.

    • GameState는 서버에 하나 생성되며, 서버가 이를 업데이트합니다. 동시에 이 GameState는 Replicate 설정이 되어 있어 연결된 모든 클라이언트에 사본이 생깁니다. 서버 GameState의 중요한 변수들은 자동으로 클라이언트 GameState에 동기화되어, 클라이언트들도 해당 정보를 읽을 수 있습니다​
      REDDIT.COM
      (예: 남은 시간 100초 -> 서버 GameState 변경 -> 엔진이 이를 클라이언트 GameState로 전파 -> 모든 플레이어 화면에 100초 표시)

    • PlayerController는 플레이어마다 서버에 하나씩 존재합니다. 호스트 자신도 플레이어이므로 호스트용 PlayerController가 서버에 생성되고, 추가로 다른 원격 플레이어가 접속하면 그들 각각에 대응하는 PlayerController가 서버에 생성됩니다. 서버는 이렇게 가진 PlayerController들을 통해 각 플레이어의 상태를 관리하고 입력을 받아 처리합니다.

    • PlayerState는 각 플레이어당 서버에 하나씩 생성됩니다. GameMode는 플레이어 접속 시 PlayerController와 함께 PlayerState도 만들고, GameState의 PlayerArray에 추가합니다. 각 PlayerState에는 해당 플레이어의 이름, 점수 등을 저장하고, 이것도 Replicate되어 클라이언트에 복제됩니다.

    • Pawn(플레이어 캐릭터)은 각 플레이어마다 서버에 하나씩 생성됩니다. GameMode가 정한 Pawn 클래스(예: DefaultPawnClass)에 따라, 플레이어마다 자신의 Pawn을 서버에서 스폰합니다. 그리고 해당 플레이어의 PlayerController가 그 Pawn을 Possess하여 조종권을 갖습니다. 모든 Pawn은 서버 세계에 실제 객체로 존재하며, 움직임/충돌/공격 판정 등 모든 게임 실제 로직은 서버의 Pawn들이 처리합니다.

    • UserWidget/HUD: 서버는 게임 세계만 관리하고, UI는 생성하지 않습니다. (호스트의 경우 서버이면서 자기 자신도 클라이언트이므로, 호스트 플레이어 화면의 UI는 결국 호스트 클라이언트 부분에서 처리됩니다. 서버 논리 자체에 UI는 관여하지 않습니다.)

  • 💢클라이언트(원격 플레이어) 측:

    • GameMode는 클라이언트에 존재하지 않습니다. 따라서 클라이언트의 게임 코드에서는 GameMode에 직접 접근할 수 없으며, 필요 시 서버에 RPC를 보내서 GameMode의 정보를 얻어야 합니다. (예: 클라이언트가 어떤 게임 규칙 데이터를 요청하면, 서버 GameMode에서 처리하여 결과를 보내주는 식)

    • GameState는 각 클라이언트에 하나씩 복제본이 존재합니다. 이 복제본은 서버의 GameState와 계속 동기화되므로, 클라이언트에서도 GetGameState()로 현재 게임 상태 정보를 읽을 수 있습니다. 단, 클라이언트 GameState는 수정 권한이 없고 읽기 전용 데이터로 취급됩니다. (클라이언트가 GameState 변수를 바꿔도 로컬에만 영향 있고 서버에 반영되지 않음) 오직 서버만이 GameState를 공식적으로 업데이트할 수 있습니다.

    • PlayerController는 각 클라이언트에 자기 자신의 것 하나만 존재합니다​
      REDDIT.COM
      즉, 클라이언트 A는 PlayerController A만 가지고 있고, 다른 플레이어 B,C의 컨트롤러 객체는 클라이언트 A에 없습니다 (필요도 없습니다). 이 로컬 PlayerController는 로컬 입력을 받아들이고 로컬 Pawn 카메라를 제어하거나 UI를 표시하는 역할을 합니다. 동시에, 이 PlayerController는 자신의 서버 측 대응 객체와 연결되어 있어서, 서버 PlayerController에게 RPC 호출을 통해 신호를 보낼 수 있습니다. 예를 들어 클라이언트에서 “발사” 클릭 시 로컬 PlayerController -> (서버 RPC) -> 서버의 해당 PlayerController로 전달 -> 서버에서 Pawn의 발사 함수 실행 이런 흐름입니다. 반대로 서버가 특정 클라이언트에 뭔가 알리고 싶을 때 (예: 채팅 메시지 도착), 서버 PlayerController에서 클라이언트 PlayerController로 Client RPC를 호출해 클라이언트에서 팝업을 띄울 수도 있습니다. 정리하면, 클라이언트는 자기 자신의 PlayerController를 통해서만 서버와 소통합니다.

    • PlayerState는 모든 플레이어의 것이 클라이언트에도 복제됩니다. GameState가 PlayerState 목록을 갖고 있고 이것이 복제되므로, 클라이언트에서도 다른 플레이어들의 PlayerState (점수, 이름 등)를 볼 수 있습니다. 이를 이용해 스코어보드 UI를 채우거나, 팀 정보 등을 표시합니다. 단, 마찬가지로 PlayerState 역시 클라이언트에서는 읽기 전용입니다.

    • Pawn(플레이어 캐릭터)는 모든 플레이어의 Pawn이 클라이언트에 복제되어 존재합니다​
      REDDIT.COM
      즉 서버에 5명의 Pawn이 있으면, 각 클라이언트도 5명의 Pawn 복사본을 가집니다. 이 Pawn들은 네트워크 통해 동기화되므로, 서버에서 움직인 만큼 위치/애니메이션 정보 등이 갱신되어 클라이언트 Pawn에도 반영됩니다. 다만, 클라이언트 중 자신이 조종하는 Pawn의 경우 Autonomous Proxy로서 약간 특별합니다: 해당 클라이언트는 자신의 입력을 바로 자기 Pawn 복제본에 적용해 즉각적인 반응을 볼 수 있고, 동시에 그 입력을 서버에도 보내 승인받습니다. 다른 플레이어들의 Pawn은 Simulated Proxy로 동작하며, 서버의 권한으로 움직인 결과만을 수신하여 표시합니다.

    • UserWidget(HUD): 각 클라이언트는 자신만의 UI 위젯을 로컬에서 생성하여 사용합니다. 예를 들어 클라이언트가 게임 시작 신호를 받으면, 자기 화면에 HealthBar 위젯, AmmoCount 위젯 등을 생성합니다. 그런 다음 이 위젯들은 로컬 Pawn이나 GameState/PlayerState 복제본을 참조하여 수치를 갱신합니다. 예를 들어 체력바 위젯은 GetOwningPlayerPawn() 등을 통해 자기 Pawn을 가져와 체력 변수를 읽고 표시합니다. 멀티플레이에서도 UI 갱신은 로컬에서 일어나며, 필요한 데이터(체력 변화, 점수 변화 등)는 서버->클라이언트로 복제되어 온 값을 활용합니다. 주의할 점은, UI 위젯은 네트워크 객체가 아니므로 서버와 직접 통신할 수 없고, 서버와 상호작용이 필요하면 PlayerController 등을 통해 해야 합니다. (예: 클라이언트 UI에서 “준비 완료” 버튼을 누르면, UI -> 로컬 PlayerController 스크립트 -> 서버 RPC 호출 -> 서버에서 처리).


멀티플레이(리슨 서버) 환경에서 서버와 클라이언트에 생성되는 주요 객체들과 데이터 흐름을 도식으로 나타낸 그림입니다. 서버에는 GameMode(게임 규칙), GameState(게임 상태), 각 PlayerController(플레이어 연결), 각 Pawn(플레이어 캐릭터), 각 PlayerState(플레이어 정보)가 존재하고, 클라이언트에는 GameState, 자신의 PlayerController, 모든 Pawn의 복제본, 모든 PlayerState의 복제본, 그리고 UI가 존재합니다. 점선 화살표는 데이터의 복제(Replication) 흐름을 나타내며, 파란색 화살표는 클라이언트 입력이 서버로 전송(RPC)되는 경로, 녹색 화살표는 클라이언트에서 UI가 게임 데이터 변화를 반영하는 흐름을 나타냅니다.


게임 프로그래밍 초보자의 입장에서 정리하자면, 싱글플레이에서는 모든 것이 한 군데 모여 있어 비교적 단순하지만, 멀티플레이에서는 서버와 클라이언트로 역할이 구분됩니다. 서버는 권위를 갖고 게임의 진실된 상태를 관리하며 (GameMode, 서버의 GameState/Pawn 등), 클라이언트는 서버의 결정을 실시간으로 받아서 보여주는 역할을 합니다 (복제된 GameState/Pawn을 통해). 클라이언트의 입력은 PlayerController를 통해 서버에 전달되고, 서버는 그것을 처리하여 결과를 다시 모든 클라이언트에 방송하는 식입니다. 이러한 구조를 이해하는 것이 멀티플레이 게임 개발의 핵심이며, Unreal 엔진이 제공하는 GameMode, GameState, PlayerController, Pawn, PlayerState 등의 클래스를 적절히 활용하면 복잡한 네트워크 동기화도 비교적 쉽게 관리할 수 있습니다​
REDDIT.COM
REDDIT.COM


마지막으로 정리하면:

  • GameMode는 게임 규칙의 서버측 관리자,
  • GameState는 모든 참여자가 볼 수 있는 게임 상태 저장소,
  • PlayerController는 플레이어 입력 처리 및 서버-클라이언트 연결자,
  • Pawn은 게임 세상 속 플레이어 캐릭터 또는 조종되는 객체,
  • UserWidget(UI)은 화면 표시용 인터페이스 (로컬 전용),
    이며, 멀티플레이에서는 이들 간에 권한과 데이터 흐름의 분리가 발생합니다. 이 개념들을 이해하면 Unreal Engine의 게임 프레임워크를 활용해 싱글 및 멀티플레이 로직을 구축하는 데 큰 도움이 될 것입니다.
    FORUMS.UNREALENGINE.COM
    REDDIT.COM

0개의 댓글