언리얼 멀티플레이(4) - 게임플레이 프레임워크

yys·2026년 6월 10일

TIL

목록 보기
57/86

언리얼 게임플레이 프레임워크의 핵심 클래스들인 GameMode / GameState / PlayerState / PlayerController / Pawn·Character 은 각각 멀티플레이어에서 존재하는 위치가 다르다. 이번에는 이 클래스들의 복제와 존재 여부를 알아보려고 한다.

GameMode — 서버 전용 규칙 관리자


GameMode 는 게임의 규칙을 정의하는 클래스다. 무엇을 어디에 spawn 할지(GameState / PlayerController / PlayerState / HUD / default pawn 의 기본 클래스), 어느 PlayerStart 에서 pawn 을 띄울지, 팀전인지, 승리 조건이 무엇인지 등의 게임의 골격이 전부 여기서 결정된다.

GameMode의 주요한 내용들은 다음과 같다.

  • 클라이언트에는 GameMode아예 없다. 즉, 서버에만 존재하고 복제되지 않으며 클라에서 접근하면 null 이 나온다.
  • 그러므로 GameMode 에 복제 변수를 둬도 클라로 안 가고, Client / Multicast RPC 도 동작하지 않는다.
  • 이미 서버 전용이라 HasAuthority() 체크도 의미가 없다 (항상 true).
  • 클라로 데이터를 내려보내려면 복제되는 다른 클래스(GameState 등)를 경유해야 한다.

GameMode vs GameModeBase

클래스특징
GameModeBase기본형, 클래스 지정, spawn, 입퇴장 처리
GameModebase 상속 + match state(시작/종료, delayed start) 추가

경기 시작 / 종료를 내포하는 매치 개념이 필요 없으면 GameModeBase 로 충분하다. 시작 대기 같은 흐름이 필요하면 GameMode 를 쓴다.

GameState


GameMode 가 비복제라 클라와 공유할 상태를 담기엔 부적합하다. 그 역할을 하는 게 GameState 다. 팀 점수, 누가 어느 팀인지, 남은 시간 같은 게임 전체의 전역 상태를 담는다.

GameState의 특징은 다음과 같다.

  • GameState 는 복제되는 액터이기 때문에 서버와 모든 클라가 접근할 수 있다. 서버에서 복제 변수를 바꾸면 클라로 내려간다.
  • net update frequency 는 10(초당 10회), priority 1

참고로 GameMode를 쓰면 반드시 GameState 를 써야 한다. base 끼리, non-base 끼리만 호환된다.

GameState 에 팀 배열 두 개를 복제 변수로 두고, GameMode::PostLogin 에서 접속한 플레이어를 더 적은 팀에 넣으면 자동으로 균등 분배된다.

// ATestProjectGameState.h
UPROPERTY(Replicated)
TArray<TObjectPtr<APlayerController>> Team1;

UPROPERTY(Replicated)
TArray<TObjectPtr<APlayerController>> Team2;

// ATestProjectGameState.cpp
void ATestProjectGameState::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
    Super::GetLifetimeReplicatedProps(OutLifetimeProps);
    DOREPLIFETIME(ThisClass, Team1);
    DOREPLIFETIME(ThisClass, Team2);
}

void ATestProjectGameState::AddTeamMember(APlayerController* Player)
{
    // 균등 배치
    if (Team1.Num() > Team2.Num())
        Team2.Add(Player);
    else
        Team1.Add(Player);
}

// ATestProjectGameMode.cpp
// 플레이어 접속 후 불리는 콜백 함수
void ATestProjectGameMode::PostLogin(APlayerController* NewPlayer)
{
    Super::PostLogin(NewPlayer);
    if (ATestProjectGameState* MPGameState = GetGameState<ATestProjectGameState>())
        MPGameState->AddTeamMember(NewPlayer);
}

4인 접속 시 정확히 2:2 로 나뉜다. 클라에서 GameState 를 얻을 땐 UGameplayStatics::GetGameState(this) 로 가져온다 (index 가 없어서 멀티에서도 안전하다).

PlayerState


PlayerState플레이어 한 명의 현재 정보를 담으며, 특징은 다음과 같다.

  • 복제되기 때문에 서버/클라 모두 접근 가능하다. 또한 모든 플레이어의 PlayerState 가 모두에게 복제되므로 클라에서 다른 인원의 점수/팀도 볼 수 있다.
  • PlayerController 와 같은 owning net connection 을 공유한다. PS 는 PC 에 할당된 액터라 PC 의 connection 을 물려받는다. 덕분에 PlayerState 도 RPC 를 보낼 수 있다
  • net update frequency 는 1(초당 1회), priority 1 이기 때문에 느린 편이며, 만약 즉각 반영이 필요하면 SetNetUpdateFrequency(10) 등으로 올린다.

PlayerController


PlayerController(PC)는 Pawn에 빙의함으로써 조종할 수 있는 클래스로 특징은 다음과 같다.

  • 서버와 자신(클라) 사이의 Owning Net Connection을 지니고, 이를 통해 서버가 누가 Pawn을 조종하는 지를 식별할 수 있음
  • Player Controller는 오직 자신(Owner)과 서버에만 존재
  • 입력을 처리한다.
  • net update frequency 100 / priority 3으로, 다른 클래스에 비해 빠르며 우선순위가 높다.

Pawn / Character


Pawn / Character 은 게임 세계에서 실제로 돌아다니는 아바타이다. PC 가 빙의해서 키·마우스로 조종한다.

Pawn / Character의 특징으로는 다음과 같다.

  • 모든 클라에 복제된다. (클라의 인식 거리 안에 있을 때 가정) 그렇기 때문에 플레이어끼리 서로 캐릭터를 볼 수 있다.
  • PC 가 possess 중일 때만 owning net connection 을 가진다.
  • 입력을 받을 수 있다.
  • net update frequency 100 / priority 3으로, 다른 클래스에 비해 빠르며 우선순위가 높다.

Static Accessor 함수


UGameplayStatics 에는 다음과 같이 index 를 받는 static accessor 가 있다.

  • GetPlayerController
  • GetPlayerCharacter
  • GetPlayerPawn
  • GetPlayerState 등등...

이때 인덱스로 보통 0을 넘기는데, 문제는 이 index 0 이 머신마다 다른 대상을 가리킨다.

실행 환경GetPlayerController(.., 0) 가 가리키는 것
Standalone자신 PC
Dedicated server첫 번째 클라의 PC
Listen host보장 없음

대안

올바른 대상을 항상 보장받으려면 관계로 접근한다.

// index 0 — 머신마다 다른 대상
APlayerController* PC = UGameplayStatics::GetPlayerController(this, 0);

// 관계 기반 — 항상 올바른 대상
APlayerController* MyPC = Cast<APlayerController>(GetController());

자주 쓰는 관계 접근들:

출발함수도착
Actor / ComponentGetOwner()소유 액터
PawnGetController()그 Pawn 의 컨트롤러
ControllerGetPawn()그 컨트롤러의 Pawn
Widget / HUDGetOwningPlayer()owning PC

참고로 GetGameState 는 게임당 하나뿐이라 index 가 없어 안전하다.

참고 자료
언리얼 게임플레이 프레임워크 문서: https://dev.epicgames.com/documentation/unreal-engine/gameplay-framework-quick-reference-in-unreal-engine

profile
게임 개발 지망생

0개의 댓글