언리얼 엔진은 게임플레이 프레임워크 기본 클래스를 제공한다. 개발자는 이 기본 클래스를 상속받아 손쉽게 자신들의 게임에 특화된 클래스를 만들어낼 수 있다. 하지만 이들의 구조를 알지 못하면 언리얼에서 제공되는 클래스의 다양한 기능와 특성들을 온전하게 사용하기 힘들고 캐스팅하는데 애를 먹을 수 있다. 따라서 이들 클래스에 대한 정의와 클래스들 사이의 관계를 알아둘 필요가 있다. 아래 그림은 다이어그램을 통해 대략적으로 이들 클래스의 전반적인 구조와 상속 관계를 그린 것이다.

UObject 는 다른 모든 클래스의 조상 클래스이다.
AActor는 게임 액터, 게임 인포메이션, 플레이어 컨트롤러라는 3개 그룹의 서브클래스를 둔 기본 클래스이다.
UActorComponent는 모든 컴포넌트 클래스의 기본 클래스이다.
UGameInstance는 고유한 게임 인스턴스 매니저 클래스이다. 게임에 특화된 변수, 함수 등을 정의하는 목적으로 확장돼 사용될 수 있다.
언리얼 게임플레이 프레임워크에는 PlayerController, GameModeBase, GameState, GameInstance라는 4개의 클래스가 포함돼 있다. 이 클래스들은 게임 플레이가 수행되는 동안 전반적인 게임 기능과 관련된 다양한 요소를 관리하는 핵심적인 역할을 수행한다.
PlayerController는 엔진의 베이스 C_++ 클래스이다. 게임플레이 컨트롤에 특화돼 사용될 수 있다. 이번에 내가 과제를 진행하면서 만든 JungPlayerController (커스텀 컨트롤러)는 APlayerController를 상속받는 인스턴스로, 플레이어 폰을 제어하기 위해 나만의 입력처리나 기능을 추가한 클래스이다.
게임에 있어서 반드시 사용되어야 하는 클래스는 아니지만 일반적으로 사용하는 것이 권장된다. 게임 플레이 컨트롤에 특화된다는 느낌이 추상적이긴하지만 PlayerController는 플레이어와 제어되는 폰 혹은 캐릭터를 이어주는 보이지 않는 폰이라고 생각하면 된다 PlayerControlle는 플레이어 폰의 로직과 뷰를 분리해서 플레이거가 다양한 폰이나 캐릭터를 좀 더 쉽게 제어할 수 있도록 도와준다.
PlayerController를 사용할 때는 다음과 같은 항목을 고려해야 한다.
플레이어 입력을 수신하고 제어
제어하는 폰이나 캐릭터를 움직이고 회전시키기
제어하는 폰이나 캐릭터의 상태 변경
카메라 뷰 조작
PlayerController는 이처럼 플레이어와 게임 사이에서 상호작용을 관리하는 역할을 한다.
모은 언리얼 엔진 프로젝트에는 GameMode 오브젝트가 필요하다. 이 오브젝트에는 스타팅 레벨, 디폴트 플레이어 폰, 플레이어 컨트롤러와 같은 핵심적인 게임 정보와 설정이 저장돼 있으며, GameModeBase 클래스를 확장해 그 자식 클래스에 더 다양한 게임플레이 정보와 규칙을 추가할 수 있다.
게임을 일시 중지하거나, 시네마틱 영상 재생, 튜토리얼 활성화하는 등의 작업을 가능하게 만드는 플래그
레벨 변환 조건과 프로세스
스폰 지점
멀티플레이어 게임의 경우 게임을 시작할 수 있는 최소 인원
게임을 종료할 수 있는 조건
나는 이번 과제에서는 싱글게임이기 때문에 JungGameState에서 대부분의 게임규칙을 구현했다. 멀티플레이의 환경이라던가 좀 더 복잡한 게임이라면 GameMode에서 해당 규칙들을 관리하는게 적절한 것이다.
GameState 오브젝트는 빠르게 변경되는 게임플레이 정보를 저장하는 데 주로 사용된다.
게임의 진행 상태 관리
토탈 스코어
적 웨이브
미니맵상 적 위치
카운트다운 타이머
간단한 게임 규칙 및 설정 저장
온라인 게임의 경우 GameState 오브젝트는 서버와 클라이언트 양쪽에 모두 존재한다.
약간 GameMode와 분담이 되는 경우가 있는 것 같아 헷갈릴 수 있지만
AGameMode는 서버 전용으로 진행되며 게임의 규칙을 담당하고
AGameState는 서버, 클라이언트 모두 존재하며, 현재 상태를 저장 한다고 볼 수 있다.
간단한 싱글게임의 경우 서버, 클라이언트를 신경쓰지 않아도 된다. 따라서 게임의 규칙을 GameState에서 관리하는 것도 가능한 것이다.
GameInstance 오브젝트는 실행 중인 게임의 인스턴스를 보여주는 하이레벨 매니저라고 할 수 있으며, UGameInstance 클래스를 확장해 로컬 플레이 변수를 자식 클래스에 추가할 수 있다. 온라인 게임의 경우 GameInstance 오브젝트가 클라이언트에만 존재해야 한다.
✅ 게임 실행 중 계속 유지 됨
✅ 레벨 간 데이터 공유 가능
✅ 싱글톤 패턴과 유사
✅ 게임의 전반적인 설정 및 상태 관리

앞에서 살펴봤던 게임플레이 프레임워크 클래스 인스턴스를 획득하려면 다음과 같이 해줘야한다.
UWorld* World = GetWorld();
APlayerController* PlayerController = World->GetFirstPlayerController();
AGameModeBase* GameMode = World->GetAuthGameMode();
AGameStateBase* GameState = World->GetGameState();
UGameInstance* GameInstance = World->GetGameInstance();
코드를 살펴보면, 게임플레이 프레임워크 클래스의 인스턴스를 얻기 위해 게임 월드 인스턴스를 먼저 획득 해야 한다는 것을 알 수 있다. 그렇게 해서 월드 포인터가 확보가 되면, 메소드 함수를 호출해 원하는 프레임 워크 클래스 인스턴스를 획득할 수 있다.
또한 예제에서 획득한 모든 변수가 자식 클래스 유형의 포인터가 아니라 기본 클래스 유형의 포인터라는 점도 유의해야 한다. 엔진에서는 게임에 특화된 클래스가 아닌 일반화된 클래스만 다룰 수 있기 때문이다.**
그래서 우리는 기본 클래스 포인터를 자식클래스 포인터로 변환하기 위해 캐스팅 작업을 거친다. 앞서 말했듯이 언리얼 엔진은 범용적인 그래픽 개발 툴로 설계가 되었다. 언리얼은 기본클래스를 상속해 각가의 고유한 게임을 개발할 수 있는 다양한 기능을 제공한다. 기본클래스에 있는 메소드 함수는 베이스 유형의 결과만 반환하며 개발자는 결국 자신들이 만드는 게임에 맞게 정의된 특정한 유형(커스텀 컨트롤러, 커스텀 게임모드 등)으로 결과를 캐스팅해야 한다.
언리얼에서 Cast는 데이터의 유형 변환에 사용되는 안전한 변환 함수이다.
위의 예제에서 내가 언하는 데이터 유형으로 변환을 한다고 해보면 다음과 같다.
AJungPlayerController* JungPlayerController = Cast<AJungPlayerController>(PlayerController);
AJungGameMode* JungGameMode = Cast<AJungGameMode>(GameMode);
AJungGameStateBase* JungGameStateBase = Cast<AJungGameStateBase>(GameState);
AJungGameInstance* JungGameInstance = Cast<AJungGameInstance>(GameInstance);
이런식으로 Cast 템플릿 함수를 통해 기본 클래스 포인터를 여기에 대응하는 자식 클래스 포인터로 변환할 수 있다.
UGameplayStatics는 UObject 기반으로 한 클래스가 아니다. 단순한 정적 함수 모음 클래스이다.
즉, 언리얼의 기본 프레임워크(Actor, Component, GameMode 등)에 포함되지 않으며, 특정 객체를 생성하지 않고도 바로 사용할 수 있는 "HelperClass" 이다.
👉 상속 되지 않는 정적 클래스 (객체 생성 불필요)
👉 게임 플레이와 관련된 여러 기능 제공 (오디오, 월드, 타이머, 충돌, 액터 찾기, 데미지 등)
👉 어디서든 UGameplayStatics::함수명() 형태로 접근 가능
예 :
// 1. 액터 찾기 (특정 클래스의 모든 액터 검사)
TArray<AActor*> FoundActors;
UGameplayStatics::GetAllActorsOfClass(GetWorld(), AMyActor::StaticClass(), FoundActors);
// 2. 사운드 재생 (현재 월드에서 특정 위치에서 사운드 재생)
UGameplayStatics::PlaySoundAtLocation(this, MySoundCue, GetActorLocation());
// 3. 레벨 관리 (OpenLevel, 다음 맵(레벨)로 이동)
UGameplayStatic::OpenLevel(this, "NewLevelName");
// 4. 게임 시간 조작 (게임 속도를 조절)
UGameplayStatics::SetGlobalTimeDilation(GetWorld(), 0.5f); // 게임 속도 50% 감소
// 5. UI 관련 기능 (멀티플레이어 지원 위한 새로운 플레이어 추가)
UGameplayStatics::CreatePlayer(GetWorld(), 1, true);
