언리얼 Framework 4대 클래스
- GameMode : 게임의 심판관
- GameState : 게임의 상황판
- PlayerState : 개인 데이터
- GameInstance : 게임의 관리자
GameMode
- 게임 규칙(게임 승리 조건, 플레이어 시작 위치, Default Pawn Class 등)을 정의하는 클래스
- 한 레벨당 하나만 존재
- 서버에서만 동작함
GameModeBase vs GameMode
GameModeBase
- 경량화된 게임 모드 클래스로 가장 기본적인 게임 진행 관리 기능만 제공
- 게임 시작/종료, 플레이어 스폰 등 최소 기능만 제공
- 캐주얼 게임, 퍼즐 게임, RPG 등 매치 로직이 명확하지 않거나 단순한 게임에서 활용
GameMode
GameModeBase를 상속한 확장형 클래스로 다양한 기능을 제공
- 클래식한 FPS/TPS 규칙(팀전, 스코어, 시간, 승리 조건 등) 지원
- 자동 리스폰 시스템, 관전자 모드, 팀 기반 매치 등 더 많은 기능을 기본적으로 제공
- 경쟁 기반 게임(FPS, 배틀로얄 등)에서 활용
조합 규칙
- GameModeBase <-> GameStateBase
- GameMode <-> GameState
- 교차해서 사용할 시에는 에러 발생하기 때문에 상황에 맞게 조합을 맞춰서 사용해야 함
GameState
- 현재 게임의 상태(점수/시간, 팀 정보, 게임 진행 상황 등)를 전체적으로 공유하는 클래스
- 클라이언트와 서버 모두 존재
- GameMode의 상태를 복제해서 모든 플레이어가 알 수 있도록 함
핵심 철학
- 정보 제공자 : 게임 모드에서 관리하면서 보여줄 정보만을 표시
- 읽기 전용 : UI에서 참조만! 직접 수정은 불가!
- GameMode 전용 업데이트 : 다른 곳에서 수정하면 안 됨!
-> 읽기 전용으로 가지고 있는 게임 상태 모음
-> 게임모드에서 해주는 업데이트된 정보를 가지고 있고, 가지고 있는 데이터를 필요한 포맷에 맞게 UI 등에 반환해주는 전광판
PlayerState
- 각 플레이어의 개별 정보(점수, 킬/데스, 유저 이름 등)를 저장하는 클래스
- 클라이언트와 서버 모두 존재
- 죽어도 사라지지 않음(정보 유지)
- 네트워크 멀티플레이에서 핵심적인 클래스
생명 주기 특징
- GameMode, GameState : 레벨 전환 시에 파괴되었다가 새로 생성됨
- PlayerState : 레벨 전환 시에 유지됨.
-> 플레이어의 점수, 이름, 팀 정보 등 게임 중 지속되어야 할 상태 정보를 저장하는 클래스이기 때문.
소유권 구조 및 올바른 접근 방법
GameMode -> PlayerController -> PlayerState
↓ ↓ ↓
권한자 소유자 실제 데이터
- PlayerController가 PlayerState를 가지고 있는데, GameMode가 PlayerController를 가지고 있다. 이 구조라면 레벨 전환 시에는 어떻게 될까?
-> GameMode, PlayerController가 레벨 전환 시 파괴되고 새로 생성되면, PlayerState는 파괴되지 않고 유지되어, GameMode가 PostLogin 시점에 PlayerController를 기존의 PlayerState와 다시 연결한다.
// 잘못된 접근 - 직접 게임 스테이트에서 플레이어 배열에 접근하는 방식
AMyPlayerState* PS = GetWorld()->GetGameState()->PlayerArray[0];
// 올바른 접근 - 플레이어 컨트롤러를 통해 접근
AMyPlayerState* PS = GetPlayerState<AMyPlayerState>();
GameInstance
- 게임 전체(세션 정보, 플레이어 정보)를 아우르는 전역 객체
- 레벨이 바뀌어도 유지됨
- 게임 시작 ~ 종료까지 살아있음.
절대적인 특징
- 게임 전체 생명주기 : 게임 시작부터 게임 종료까지 살아있음
- 레벨 간 데이터 유지 : 레벨 바뀌어도 파괴되지 않고 유지됨
- 레벨 전환 권환 : 오직 GameInstance만 레벨 바꿀 수 있음
Framework 로딩 순서
- 엔진 부팅 단계
1. 엔진 초기화 (UEngine 생성)
2. GameInstance::Init()
- 첫 레벨 로딩 단계
3. World 생성
4. GameMode 생성
5. GameState 생성
6. PlayerController 생성
7. PlayerState 생성
8. Pawn 생성
9. 이후 모든 BeginPlay() 호출 -> 순서 보장 안됨
- 레벨 전환 시
10. 모든 액터의 EndPlay() 호출
11. 기존 World 파괴(GameMode, GameState 사라짐)
12. PlayerState는 새 World로 이주
13. 3~9번 과정 반복 -> 7번 과정에서 PlayerController와 기존 PlayerState 재연결
각 단계에서 할 수 있는 것/없는 것
GameInstance::Init()
- 할 수 있는 것
- 글로벌 설정 로드
- 서브시스템 초기화
- SaveGame 데이터 로드
- 할 수 없는 것
- GetWorld() 호출
- GameMode, PlayerController 접근
- UI 생성
GameMode 생성자
- 할 수 있는 것
- 할 수 없는 것
- GameState 접근
- PlayerController 접근
PostInitializeComponents()
- 할 수 있는 것
- GameState 접근
- 다른 Framework 객체들과 통신
언리얼 설계 사고법
데이터 위치 결정
- 생명주기 : 언제까지 살아있어야 하는가?
- 레벨 끝까지 : GameMode/GameState
- 게임 끝까지 : PlayerState/GameInstance
- 수정 권한 : 누가 바꿀 수 있는가?
- 게임 시스템 : GameMode가 관리
- 플레이어 개인 : PlayerState가 관리
- 참조 대상 : 누가 봐야 하는가
- UI 표시 : GameState/PlayerState
- 내부 로직 : private 멤버
출처: 팀스파르타 내일배움캠프