[언리얼 C++] 게임 모드
언리얼 엔진 4에는 플레이중인 게임의 정보를 처리하는 게임 모드 클래스가 존재합니다.
큰 제약이 없는 게임일지라도 토대가 되는 규칙들이 있으며, 이러한 규칙들이 게임 모드를 이룹니다.
가장 기본적인 수준에서 이러한 규칙에 포함되는 것은 아래와 같습니다.
- 존재하는 플레이어와 옵저버의 수, 허용된 플레이어와 최대 옵저버 수
- 플레이어가 게임에 들어오는 방식, 스폰 위치 선택 규칙과 기타 스폰/리스폰 동작 포함 가능
- 게임 일시정지 가능 여부, 게임 일시정지 처리 방식
- 레벨간의 전환, 게임의 시네마틱 모드 시작 여부 포함
1. 게임 모드
플레이하는데 필요한 플레이어 수, 그 플레이어가 게임에 참가하는 방식 등의 기본적인 요소들은 여러 유형의 게임에 공통이지만, 어떤 게임을 개발하는지에 따라 무한대의 규칙 변종이 가능합니다. 그 규칙이 무엇이든, 그 정의와 구현은 게임 모드에서 합니다.
현재 게임 모드에 흔히 쓰이는 베이스 클래스는 둘 입니다.
바로 AGameModeBase 클래스와 AGameMode 클래스입니다. AGameModeBase 클래스는 모든 게임 모드의 베이스 클래스로 단순하고 효율적입니다. AGameMode 클래스는 AGameModeBase 클래스의 자손 클래스로 그 경기 상태 개념 구현 방식상 멀티플레이어 슈팅과 같은 표준 게임 유형에 더 적합합니다.
2. AGameModeBase 클래스
모든 게임 모드는 AGameModeBase 클래스의 서브 클래스로 덮어쓸 수 있는 기본 함수들이 많이 들어있습니다.
흔히 쓰이는 함수는 아래와 같습니다.
함수/이벤트 | 용도 |
InitGame | InitGame 은 게임 모드의 초기화 작업을 진행합니다. |
PreLogin | 서버에 접근 시도중인 플레이어를 수락 또는 거부합니다. ErrorMessage 에 공백이 아닌 스트링을 입력하면 Login 함수가 실패하도록 만듭니다. PreLogin 은 Login 전 호출되며, 참가하는 플레이어가 게임 콘텐츠를 다운로드해야 하는 경우 시간이 한참 지나서야 Login 이 호출될 수도 있습니다. |
PostLogin | 로그인 성공 이후 호출됩니다. PlayerController 에서 리플리케이트되는 함수 호출을 하기에 안전한 첫 번째 장소입니다. 블루프린트로 OnPostLogin 을 구현하여 부가 로직을 추가할 수 있습니다. |
HandleStartingNewPlayer | PostLogin 또는 심리스 트래블 이후 호출되며, 블루프린트에서 덮어써서 새 플레이어에게 벌어지는 일을 변경할 수 있습니다. 기본적으로 플레이어에 대한 폰을 생성합니다. |
RestartPlayer | 플레이어의 폰 스폰을 시작하기 위해 호출됩니다. 폰의 스폰 위치를 지정하고자 하는 경우 사용할 수 있는 함수 RestartPlayerAtPlayerStart 와 RestartPlayerAtTransform 도 있습니다. OnRestartPlayer 를 블루프린트에서 구현하여 이 함수 완료 후의 로직을 추가할 수 있습니다. |
SpawnDefaultPawnAtTransform | 플레이어의 폰이 실제 스폰되는 곳으로, 블루프린트에서 덮어쓸 수 있습니다. |
Logout | 플레이어가 게임을 떠나거나 소멸되었을때 호출됩니다. OnLogout 을 구현하여 블루프린트 로직을 짤 수 있습니다. |
게임이 제공하는 경기 포맷, 미션 유형, 특별 지역 각각에 대해 AGameModeBase 클래스의 서브 클래스를 만들 수 있습니다. 게임에는 게임 모드가 몇이든 있을 수 있듯이 AGameModeBase 클래스의 서브 클래스도 그러하지만, 한번에 하나의 게임 모드만 사용할 수 있습니다. 게임 모드 액터는 UGameEngine::LoadMap() 함수를 통해 플레이할 레벨을 초기화시킬때마다 인스턴스를 생성합니다.
게임 모드는 멀티플레이어 게임에 참가하는 원격 클라이언트에 리플리케이트되지 않습니다. 서버에만 존재하므로, 로컬 클라이언트는 원본 게임 모드 클래스를 볼 수 있지만 실제 인스턴스에 접근하여 변수를 확인하고 게임 도중 무엇이 변경되었는가를 확인할 수는 없습니다. 플레이어가 현재 게임 모드 관련해서 업데이트된 정보가 필요한 경우, 그 정보는 AGameStateBase 액터에 저장된 내용을 통해 쉽게 동기화 상태를 유지할 수 있으며, 이 중 하나를 게임 모드와 함께 생성한 다음 모든 원격 클라이언트로 리플리케이트합니다.
3. AGameMode 클래스
AGameMode 클래스는 AGameModeBase 클래스의 서브 클래스로 멀티플레이어 경기 및 기존 작동방식 지원을 위한 부가 함수가 몇 가지 들어있습니다. 새롭게 생성되는 프로젝트는 모두 AGameModeBase 클래스를 기본적으로 사용하지만, 그 부가 함수가 필요한 경우 AGameMode 클래스를 상속하도록 전환할 수 있습니다. AGameMode 클래스를 상속하는 경우, 경기 스테이트 머신도 지원하는 AGameState 클래스에서 게임 스테이트를 상속하기도 해야합니다.
AGameMode 클래스에는 경기 또는 일반 게임플레이 흐름 상태를 기록하는 스테이트 머신이 들어있습니다. 현재 상태를 질의하기 위해서는 GetMatchState() 또는 HasMatchStarted(), IsMatchInProgress(), HasMatchEnded() 와 같은 래퍼(wrapper)를 사용하면 됩니다. 가능한 경기 상태는 아래와 같습니다.
- EnteringMap (맵 진입)은 초기 상태입니다. 액터 틱은 아직 이루어지지 않고 있으며, 월드는 제대로 초기화되지 않은 상태입니다. 모두 완전히 로드되면 다음 상태로 전환될 것입니다.
- WaitingToStart (시작 대기중)가 그 다음 상태로, 여기에 들어갈 때 HandleMatchIsWaitingToStart() 함수가 호출됩니다. 액터 틱은 이루어지지만, 플레이어는 아직 스폰되어있지 않습니다. ReadyToStartMatch() 함수가 true 를 반환하는 경우, 또는 StartMatch() 함수가 호출된 경우 다음 상태로 전환됩니다.
- InProgress (진행중)은 게임의 주요 부분이 일어나는 상태입니다. 여기에 들어갈 때 HandleMatchHasStarted() 함수가 호출되며, 그 후 모든 액터에서 BeginPlay() 함수를 호출합니다. 이 시점에서 일반적인 게임플레이가 진행됩니다. ReadyToEndMatch() 함수가 true 를 반환하거나 EndMatch() 함수가 호출되는 경우 경기가 다음 상태로 전환됩니다.
- WaitingPostMatch (경기 후 대기)는 끝에서 두 번째 상태로, 여기에 들어설 때 HandleMatchHasEnded() 함수가 호출됩니다. 액터 틱은 여전히 일어나지만 새로운 플레이어는 참가할 수 없습니다. 맵 이동이 시작되면 다음 상태로 전환됩니다.
- LeavingMap (맵 떠나기)는 일반적인 흐름의 마지막 상태로, 여기에 들어설 때 HandleLeavingMap() 함수를 호출합니다. 경기가 이 상태에 머물러있다가 맵 전환이 일어나면 EnteringMap (맵 진입) 상태로 돌아갑니다.
- Aborted (중단된)은 실패 상태로, AbortMatch() 함수를 호출하면 시작됩니다. 복구할 수 없는 오류가 발생했을 때 설정됩니다.
경기 상태는 거의 항상 InProgress (진행중) 상태에 머물러있게 되는데, BeginPlay() 함수가 호출되고 액터 틱이 시작되는 곳이기 때문입니다. 하지만 다른 게임에서 보다 복잡한 규칙의 멀티플레이어 게임을 만드는 경우, 예를 들어 멀티플레이어 슈팅 게임에서 다른 플레이어 참가를 기다리는 동안 플레이어가 레벨을 자유롭게 날아다닐 수 있게 하려는 경우, 위와 같은 상태를 덮어쓰면 됩니다.
4. 게임 모드 설정
레벨의 게임 모드 설정을 위한 메서드는 여러가지 있습니다. 우선권이 낮은 것에서부터 높은 것 순으로 나열해 보겠습니다.
- DefaultEngine.ini 파일의 /Script/EngineSettings.GameMapsSettings 섹션에 GlobalDefaultGameMode 항목으로 설정해 주면 프로젝트의 모든 맵에 대한 기본 게임 모드를 설정합니다.
[/Script/EngineSettings.GameMapsSettings] GlobalDefaultGameMode="/Script/MyGame.MyGameGameMode" GlobalDefaultServerGameMode="/Script/MyGame.MyGameGameMode"
- 개별 맵에 대해 프로젝트 세팅을 덮어쓰려면, 에디터에서 World Settings의 Default Game Mode를 설정합니다.
- URL 을 실행파일에 붙여 게임 시작시 로드할 맵을 강제 지정할 수 있습니다. game 옵션을 사용하여 게임 모드를 설정할 수 있습니다.
UE4Editor.exe /Game/Maps/MyMap?game=MyGameMode -game
- 마지막으로 DefaultEngine.ini 파일의 /Script/EngineSettings.GameMapsSettings 섹션에 맵 prefixes와 URL 메서드에 대한 aliases를 설정할 수 있습니다. 설정하면 prefixes가 붙은 모든 맵에 대한 기본 게임 모드를 설정합니다.
[/Script/EngineSettings.GameMapsSettings] +GameModeMapPrefixes=(Name="DM",GameMode="/Script/UnrealTournament.UTDMGameMode") +GameModeClassAliases=(Name="DM",GameMode="/Script/UnrealTournament.UTDMGameMode")