[Unreal Engine] Gameplay Framework 1

Imeamangryang·2025년 7월 25일
post-thumbnail

출처 ㅣ Cedric Neukirchen - Multiplayer Network Compendium


Gameplay Framework

해당 페이지에서는 언리얼 엔진의 "Gameplay Framework"에서 가장 일반적으로 사용되는 클래스들에 대해 설명합니다. 또한 이러한 클래스들이 어떻게 사용되는지에 대한 간단한 예제도 제공합니다.

이 목록은 프레임워크에 대한 소개를 목적으로 하며, 이후 다른 내용에서 이 클래스들을 언급할 수 있도록 하기 위함입니다.

예제들은 모두 복제(Replication)에 대한 지식을 필요로 합니다. 만약 예제를 이해하기 어렵다면, 복제(Replication) 관련 챕터를 먼저 읽은 후 다시 참고해도 괜찮습니다.

게임 장르에 따라 이 클래스들을 다르게 사용할 수도 있습니다. 여기서 제시하는 예제와 설명이 클래스를 활용하는 유일한 방법은 아닙니다.


AGameMode

⚠️ 주의
4.14 버전부터 AGameMode 클래스가 AGameModeBase와 AGameMode로 분리되었습니다. GameModeBase는 기능이 더 적으며, 일부 게임은 기존 AGameMode 클래스의 모든 기능이 필요하지 않을 수 있습니다.

AGameMode 클래스는 게임의 규칙을 정의하는 데 사용됩니다.
여기에는 APawn, APlayerController, APlayerState 등과 같은 다른 게임플레이 프레임워크 클래스를 스폰하는 것이 포함됩니다.

이 클래스는 서버에서만 사용할 수 있습니다. 클라이언트는 AGameMode 클래스의 인스턴스를 가지지 않으며, 이를 가져오려고 하면 nullptr만 반환됩니다.

Examples & Usage

GameMode의 사용 예시는 오래된 1인칭 슈팅 게임(예: Unreal Tournament)에서 볼 수 있습니다:

데스매치, 팀 데스매치, 깃발 뺏기 등

즉, GameMode는 다음과 같은 것들을 정의할 수 있습니다:

  • 팀이 있는지, 모두가 각자 경쟁하는지
  • 승리 조건
    • 개인 또는 팀이 몇 번의 킬을 달성해야 하는지
  • 점수 획득 방식
    • 상대를 처치하기
    • 깃발을 훔치기
  • 사용할 캐릭터
  • 허용되는 무기
    • 권총만?
    • 탄창 하나만?

멀티플레이어 환경에서는 GameMode가 플레이어 관리 및 경기 흐름을 제어하는 데 도움이 되는 다양한 기능을 제공합니다.

Blueprint Examples

Functions

첫 번째로 살펴볼 부분은 블루프린트 버전의 Override Function 섹션입니다:

이 함수들에 대한 로직을 구현하여 게임의 규칙에 맞게 동작을 변경할 수 있습니다.
예를 들어, GameMode가 DefaultPawn을 스폰하는 방식이나 게임이 시작할 준비가 되었는지 판단하는 방법을 변경할 수 있습니다.

예시로는 모든 플레이어가 서버에 접속하여 준비가 되었는지 확인하는 로직이 있습니다.

하지만 경기 중에 발생하는 특정 상황에 반응할 수 있는 이벤트들도 있습니다.

그 중 자주 사용하는 좋은 예시는 'Event OnPostLogin'입니다.

이 이벤트는 새로운 플레이어가 게임에 접속할 때마다 호출됩니다. 이후 연결 과정에 대해 더 배우게 되겠지만, 지금은 이대로 진행하겠습니다.

이 이벤트는 연결된 플레이어의 UConnection이 소유한 유효한 PlayerController를 전달합니다(이 부분도 나중에 더 설명합니다).

이를 통해 해당 플레이어와 상호작용할 수 있습니다.
예를 들어, 새로운 Pawn을 스폰하거나 PlayerController를 배열에 저장해두는 용도로 사용할 수 있습니다.

앞서 언급했듯이, GameMode를 사용하여 게임의 전체적인 경기 흐름을 관리할 수 있습니다. 이를 위해 'Ready To Start Match'와 같은 오버라이드 가능한 함수들이 있습니다.

이 함수들과 이벤트들은 현재 MatchState를 제어하는 데 사용할 수 있습니다. 대부분은 'Ready To Start Match' 함수가 TRUE를 반환할 때 자동으로 호출되지만, 직접 수동으로 사용할 수도 있습니다.

'New State'는 단순한 'FName' 타입입니다. 이제 “왜 이 부분이 AGameState 클래스에서 처리되지 않는가?”라는 질문을 할 수 있습니다.

실제로 이러한 GameMode 함수들은 GameState와 긴밀하게 협력합니다.

이것은 클라이언트가 접근할 수 없는 MatchState를 관리할 수 있는 지점을 제공하기 위한 것입니다. GameMode는 서버에만 존재하기 때문입니다!

Variables

GameMode에서 사용할 수 있는 중요한 변수들도 있습니다.

다음은 이미 상속된 변수들의 목록입니다. 일부는 GameMode 블루프린트의 ClassDefaults에서 설정할 수 있습니다.

대부분은 'Default Player Name'처럼 쉽게 이해할 수 있습니다.
이 변수는 연결된 모든 플레이어에게 기본 PlayerName을 부여하며, 이는 APlayerState 클래스를 통해 접근할 수 있습니다.

또는 'bDelayedStart'는 'Ready To Start Match'의 기본 구현이 모든 조건을 충족하더라도 게임 시작을 지연시킵니다.

중요한 변수 중 하나는 'Options String'입니다. 이 옵션들은 '?'로 구분되며, 'OpenLevel' 함수나 콘솔 명령어로 'ServerTravel'을 호출할 때 전달할 수 있습니다.

'Parse Option'을 사용하여 'MaxNumPlayers'와 같은 전달된 옵션을 추출할 수 있습니다.

UE C++ Examples

모든 블루프린트 작업은 C++에서도 구현할 수 있습니다.
동일한 정보를 반복하지 않기 위해 이전 블루프린트 예제를 C++로 재현하는 코드 예제를 제공합니다.

'ReadyToStartMatch'는 'BlueprintNativeEvent'이므로 실제 C++ 구현 함수 이름은 'ReadyToStartMatch_Implementation'입니다. 이 함수를 오버라이드해야 합니다.

// .h
// 이 매치에서 필요한/허용되는 최대 플레이어 수
int32 MaxNumPlayers;

// ReadyToStartMatch 오버라이드 구현
virtual bool ReadyToStartMatch_Implementation() override;
// .cpp
bool ATestGameMode::ReadyToStartMatch_Implementation()
{
    Super::ReadyToStartMatch();

    return MaxNumPlayers == NumPlayers;
}

'OnPostLogin' 함수는 virtual이며 C++에서는 'PostLogin'이라고 부릅니다.

// PlayerController 목록
UPROPERTY()
TArray<APlayerController*> PlayerControllerList;

// PostLogin 함수 오버라이드
virtual void PostLogin(APlayerController* NewPlayer) override;
void ATestGameMode::PostLogin(APlayerController* NewPlayer)
{
    Super::PostLogin(NewPlayer);

    PlayerControllerList.Add(NewPlayer);
}

AGameState

⚠️주의
4.14 버전부터 GameState 클래스가 AGameStateBase와 AGameState로 분리되었습니다. GameStateBase는 기능이 더 적으며, 일부 게임은 기존 GameState 클래스의 모든 기능이 필요하지 않을 수 있습니다.

AGameState 클래스는 서버와 클라이언트 간에 정보를 공유하는 데 가장 중요한 클래스입니다.

GameState는 게임/매치의 현재 상태를 추적하는 데 사용됩니다.
멀티플레이어에서는 연결된 플레이어(APlayerState) 목록을 포함하는 것이 중요합니다.

또한, 모든 클라이언트에 복제되므로 누구나 접근할 수 있습니다.
이로 인해 GameState는 멀티플레이어 게임에서 정보 측면에서 가장 중심적인 클래스 중 하나입니다.

GameMode가 승리에 필요한 킬 수를 정의한다면, GameState는 각 플레이어 및/또는 팀의 현재 킬 수를 추적합니다!

여기에 어떤 정보를 저장할지는 전적으로 개발자에게 달려 있습니다. 점수 배열이나 그룹 및 길드 추적을 위한 커스텀 구조체 배열 등도 저장할 수 있습니다.

Examples and Usage

멀티플레이어 환경에서 AGameState 클래스는 게임의 현재 상태를 추적하는 데 사용되며, 여기에는 플레이어와 각 플레이어의 PlayerState도 포함됩니다.

GameMode는 GameState의 MatchState 관련 함수들이 호출되도록 관리하며, GameState는 클라이언트에서도 이러한 함수들을 사용할 수 있도록 해줍니다.

GameMode에 비해 GameState가 제공하는 기능은 많지 않지만, 클라이언트에 정보를 전달하는 로직을 구현하는 데 충분히 활용할 수 있습니다.

Blueprint Examples

Variables

기본 AGameState 클래스에서 몇 가지 변수를 사용할 수 있습니다. PlayerArray, MatchState, ElapsedTime은 모두 복제(Replicated)되어 클라이언트에서도 접근할 수 있습니다.

AuthorityGameMode는 해당되지 않습니다. GameMode는 서버에만 존재하므로 서버만 접근할 수 있습니다.

PlayerArray 자체는 직접적으로 복제되지 않지만, 각 PlayerState가 복제되며 생성 시 PlayerArray에 자신을 추가합니다. 또한 GameState가 이들을 수집하여 레이스 컨디션(race-condition) 문제를 방지합니다.

아래는 C++에서 PlayerState들이 PlayerArray에 수집되는 방법을 간단히 보여주는 예시입니다.

void APlayerState::PostInitializeComponents()
{
    // […]

    UWorld* World = GetWorld();
    // 이 PlayerState를 게임의 ReplicationInfo에 등록합니다.
    if (World->GameState != NULL)
    {
        World->GameState->AddPlayerState(this);
    }

    // […]
}
void AGameState::PostInitializeComponents()
{
    // […]

    for (TActorIterator<APlayerState> It(World); It; ++It)
    {
        AddPlayerState(*It);
    }
}

void AGameState::AddPlayerState(APlayerState* PlayerState)
{
    if (!PlayerState->bIsInactive)
    {
        PlayerArray.AddUnique(PlayerState);
    }
}

이 모든 과정은 서버와 클라이언트의 PlayerState 및 GameState 인스턴스에서 발생합니다!

Functions

두 팀 'A'와 'B'의 점수를 추적하는 간단한 함수 예시를 들 수 있습니다.
예를 들어, 한 팀이 점수를 획득할 때 호출되는 CustomEvent가 있다고 가정해봅시다.

이 이벤트는 어떤 팀이 점수를 얻었는지 알 수 있도록 boolean 값을 전달합니다.
PlayerState, Team 등 식별에 사용하는 값을 전달할 수도 있습니다.

나중에 “Replication” 챕터에서 오직 서버만 변수를 복제할 수 있다는 규칙을 배우게 되므로, 반드시 서버만 이 이벤트를 호출해야 합니다.

이 이벤트는 다른 클래스(예: 누군가를 처치한 무기)에서 호출되며, 항상 서버에서 발생해야 하므로 별도의 RPC가 필요하지 않습니다.

이러한 변수들과 GameState가 복제되기 때문에, 필요한 다른 클래스에서 이 두 변수를 사용하고 가져올 수 있습니다. 예를 들어, 스코어보드 위젯에 표시하는 경우입니다.

UE C++ Examples

이 작은 예제를 재현하려면 좀 더 많은 코드가 필요하지만, 함수 자체에도 불구하고 복제를 설정하는 데 필요한 코드는 클래스당 한 번만 필요합니다.

// 복제를 위해 반드시 포함해야 합니다.
#include “UnrealNetwork.h”

// Replicated 지정자를 사용하여 변수를 복제하도록 표시
UPROPERTY(Replicated)
int32 TeamAScore;

UPROPERTY(Replicated)
int32 TeamBScore;

// 팀의 점수를 증가시키는 함수
void AddScore(bool bTeamAScored);

이 함수에 대해서는 복제 파트에서 더 자세히 다룹니다!

// 이 함수는 UPROPERTY 매크로의 Replicated 지정자를 통해 필요하며, 해당 매크로에 의해 선언됩니다.
void ATestGameState::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
    Super::GetLifetimeReplicatedProps(OutLifetimeProps);

    DOREPLIFETIME(ATestGameState, TeamAScore);
    DOREPLIFETIME(ATestGameState, TeamBScore);
}
void ATestGameState::AddScore(bool bTeamAScored)
{
    if (bTeamAScored)
    {
        TeamAScore++;
    }
    else
    {
        TeamBScore++;
    }
}
profile
언리얼 엔진 주니어(신입) 개발자 | 소설 쓰는 취준 개발자

0개의 댓글