UE5

서버연동 GameMode & GameState

게임 개발 2024. 5. 27. 17:39

 
 

GameMode & GameState

 
멀티 플레이어와 관련하여 매우 중요한 두 가지 클래스는
game mode 와 game state이다.
 
언리얼 공식문서에서도
플레이 중인 게임에 대한 정보를 처리하는 두 가지 주요 클래스는
Game Mode와 Game State라고 언급한다.
 

출저 : 나 ❘ 참고자료 : 언리얼 공식문서

 
 
 


Game Mode

 
가장 자유도가 높은 게임(Even the most open-ended game)이라 할지라도 기본 규칙이 있으며,
이러한 규칙이 게임 모드를 구성한다.
( Even the most open-ended game = 사물, 사람이 제한 없이 자유롭고 유연한 상태,
즉, 플레이어가 자유롭게 행동할 수 있고 다양한 선택을 할 수 있는
게임 자유도가 높은 게임이라 할지라도~ 라고 생각하면 된다.)

가장 기본적인 수준에서 이러한 규칙에는 다음이 포함된다.
 

출저 : 나 ❘ 참고자료 : 언리얼 공식문서

 
 
게임 모드에 들어가는 일반적인 규칙을 나열한 것이다.

  • 참석한 플레이어 및 관중의 수와 허용되는 최대 플레이어 및 관중 수
  • 플레이어가 게임에 입장하는 방법을 몇가지 포함한다.
  • 여기에는 스폰 위치 선택과, 스폰/리스폰 동작이 포함될 수 있다.
  • 게임을 일시정지할 수 있는지의 여부와 게임 일시정지를 처리하는 방법
  • 게임이 영화 모드에서 시작되어야 하는지 여부를 포함하는 레벨 간 전환이다.

 
플레이하는 데 필요한 플레이어 수나 플레이어가 게임에 참가하는 방법과 같은
특정 기본 사항은 여러 유형의 게임에 공통적으로 적용되지만,
개발 중인 특정 게임에 따라 규칙은 무제한으로 달라질 수 있다.
이러한 규칙이 무엇이든 게임 모드는 규칙을 정의하고 구현하도록 설계되었다.
 
현재 게임 모드에 일반적으로 사용되는 기본 클래스는 두 가지가 있다.
AGameModeBase와 AGameMode이다.
 

AGameModeBase

 
필자의 전문 번역가가 아니므로,
번역의 질이 좋지 않음을 감안하여 의역이 들어가 매끄럽지 않기에
공식 문서 원문을 추가로 주석으로 달아 놓겠다.
 
(All Game Modes are subclasses of AGameModeBase,)
모든 게임 모드 클래스는 AGameModeBase 재정의할 수 있도록 설계되었다.
즉 모든 게임모드는 AGameModeBase의 서브 클래스이며, 
(which contains considerable base functionality that can be overridden.)
AGameModeBase 클래스에는 상당한 기본 기능이 포함되어 있고,
언리얼은 이를 재정의 할 수 있도록 하였다.
(Some of the common functions include:)
몇 가지 일반적인 함수로는 다음이 포함된다.
 

출저 : 나 ❘ 참고자료 : 언리얼 공식문서 첨부 표 1

 

출저 : 나 ❘ 참고자료 : 언리얼 공식문서 첨부 표 2
출저 : 나 ❘ 참고자료 : 언리얼 공식문서 첨부 표 3

 

기능/이벤트목적
InitGameInitGame 이벤트는 다른 모드 스크립트(PreInitializeComponents 포함)보다 먼저 호출되며, AGameModeBase의 PreInitializeComponents에서 사용된다.
PreLogin서버에 참가하려고 시도는 플레이어를 수락하거나 거부한다. ErrorMessage를 빈 문자열이 아닌 값으로 설정하면 Login 함수가 실패한다. PreLogin보다 먼저 호출되며, 특히 접속하려는 플레이어가 게임 콘텐츠를 다운로드해야 하는 경우 Login이 호출되기까지 상당한 시간이 걸릴 수 있다.
(해당 설명은 PreLogin과 Login 함수의 역할과 호출 순서,
그리고 플레이어가 서버에 접속할 때 발생할 수 있는 상황을 설명하고 있다.)
PostLogin성공적인 로그인 후에 호출된다. 이 함수는 PlayerController에서 복제된 함수를 호출할 수 있는 첫 번째 안전한 지점이다. OnPostLogin은 블루프린트에서 추가로 로직을 구현할 수 있도록 되어있다. 
HandleStartingNewPlayerPostLogin 또는 시차 없는 이동(seamless travel)후에 호출되며,
블루프린트에서 이를 재정의하여 새로운 플레이어에게 발생하는 일을 변경할 수 있다.
기본적으로, 이 함수는 플레이어를 위한 폰을 생성한다.
RestartPlayer아 함수는 플레이어의 폰을 스폰하기 위해 호출된다.
폰이 스폰될 위치를 지정하려면 RestartPlayerAtPlayerStart 및 RestartPlayerAtTransform 함수도 사용할 수 있다. OnRestartPlayer는 이 함수 구현이 C++ 끝난 후에도 추가로 블루프린트에서 구현할 수 있도록 설계되었다.
SpawnDefaultPawnAtTransform이 함수는 실제로 플레이어의 폰을 스폰하는 함수이며, 블루프린트에서 재정의(overridde) 가능하다.
Logout플레이어가 게임을 떠나거나 파괴될 때 호추된다. OnLogout은 블루프린트 로직을 구현할 수 있도록 설계되었다.

 
 
AGameModeBase의 하위 클래스들은 게임이 제공하는 각 경기 방식, 매치 유형 또는 
게임이 제공하는 툭별한 특별 구역에 대해 AGameModeBase 클래스의 서브클래스를 생성할 수 있다.
게임은 여러 개의 게임 모드가 있을 수 있으므로,
AGameModeBase 클래스의 하위 클래스 역시 여러 개 생성될 수 있다.
그러나 주어진 시간에 하나의 게임 모드만 사용될 수 있다.
게임 모드 액터는 함수를 통해
플레이를 위해 레벨이 초기화될 때마다 인스턴스화된다 UGameEngine::LoadMap().
( A Game Mode Actor : 내 생각에는 적당한 GameMode 기능을 담은 AActor를 말하는 것 같다.
필자는 애셋을 뜯으면서 게임 시스템 중 굵직한 부분을 차지하는 시스템을 추가하기 위해서
(ex. 농사시스템(구조물 설치, 날씨 온도 변화, 인벤토리와의 소통 등등의 다양한 시스템이 들어간 것))
애셋을 뜯어 보았을 때 해당 기능을 하는 AActor를 찾아볼 수 있었다.
물론 당연히 해당 시스템 안에 게임모드도 존재하지만,
이러한 세부 기능을 연결시켜주는 AActor도 따로 있었다.)

 
게임 모드는 멀티플레이어 게임에 참여하는 원격 클라이언트에 복제되지 않는다.
이는 서버에만 존재하므로
로컬 클라이언트는 사용된 스톡 게임 모드 (또는 블루프린트)를 볼 수 있지만
실제 인스턴스에 엑세스하여
해당 변수를 확인하여 게임이 진행됨에 따라 무엇이 변경되었는지 확인할 수 없다.
플레이어에게 현재 게임 모드와 관련된 업데이트된 정보가 필요한 경우
해당 정보는 액터에 저장되어 쉽게 동기화된다.
액테 AGameStateBase 중 하나는 게임 모드와 함께 생성된 다음 모든 원격 클라이언트에 복제된다.
 
 

AGameMode

 
AGameMode 는 AGameModeBase를 상속받은 하위클래스로
멀티플레이어 매치와 레거시 동작(legacy behavior)을 지원하기 위한 추가 기능을 가졌다.
새로 생성된 모든 프로젝트는 기본적으로 AGameModeBase를 디폴트로 사용하지만,
여기서 추가적인 기능을 필요하다면 AGameMode를 상속하여 바꿔줄 수 있다.
AGameMode를 상속하는 경우,  AGameState도 같이 매치해서 상속해줘야 한다.
 
AGameMode는 매치 상태 또는 일반적인 게임 플레이 흐름을 추적하는 상태 기계가 포함되어 있다.
현재 상태를 조회하려면
GetMatchState를 사용하거나
HasMatchStarted, IsMatchInProgress, HasMatchEnd와 같은 래퍼(함수)를 사용할 수 있다.
 
Here are the possible match states :
 

  • EnteringMap의 초기 상태이다. 이 상태는 액터들이 아직 틱을 하지 않았으며,
  • 월드가 완전히 초기화되지 않은 상태이다.
  • 모든 것이 완전히 로드되면 다음 상태로 전환된다.
  • WaitingToStart는 다음 상태이며,
  • 이 상태로 들어갈 때 HandleMatchlsWaitingToStart가 호출된다.
  • 이 상태에서는 액터들이 틱을 하고 있지만,
  • 플레이어들은 아직 스폰되지 않았다.
  • ReadyToStartMatch가 true를 반환하거나 StartMatch가 호출되면 다음 상태로 전환된다.
  • InProgress는 게임의 주요 부분이 진행되는 상태이다.
  • 이 상태에 들어갈 때 HandleMatchHasStarted가 호출되며,
  • 이 함수는 모든 액터에서 BeginPlay를 호출한다.
  • 이 시점에서는 정상적인 게임 플레이가 진행 중이다.
  • ReadyToEndMatch가 true를 반환하거나 EndMatch가 호출되면 다음 상태로 전환된다.
  • WaitingPostMatch는 끝에서 두 번째 상태이며,
  • 이 상태로 들어갈 때 HandleMatchHasEnded가 호출된다.
  • 이 상태에서는 액터들이 여전히 틱하고 있지만,
  • 새로운 플레이어는 참여할 수 없다.
  • 맵이 전환이 시작되면 다음 상태로 전환된다.
  • LeavingMap은 정상적인 흐름에서 마지막 상태이며,
  • 이 상태에 들어갈 때 HandleLeavingMap이 호출된다.
  • 매치는 새로운 맵으로 전환되는 동안 이 상태에 머물며,
  • 이후 다시 EnteringMap 상태로 전환된다.
  • Aborted는 실패 상태이며, AbortMatch를 호출하여 시작할 수 있다.
  • 이는 복구 불가능한 오류가 발생했을 때 설정된다.

 
매치 상태는 거의 항상 InProgress일 것이다.
이 상태에서는 BeginPlay가 호출되고 액터들이 틱을 시작하기 때문이다.
그러나 개별적으로 게임들은 이러한 상태의 동작을 재정의하여
더 복잡한 규칙을 가진 멀티플레이어 게임을 만들 수 있다.
예를 들어, 멀티플레이어 슈팅 게임에서 다른 플레이어가 참가할 때까지
플레이어가 자유롭게 레벨을 돌아다닐 수 있도록 허용할 수있다.
 
 

Game Mode Blueprints

 
게임 모드 클래스에서 파생된 블루프린트를 생성하고
이를 프로젝트나 레벨의 기본 게임모드로 사용할 수 있다.
 
게임 모드에서 파생된 블루프린트는 다음 기본 값을 설정할 수 있다
 

  • Default Pawn Class
  • HUD Class
  • PlayerController Class
  • Spectator Class
  • Game State Class
  • Player State Class

또한 게임 모드의 블루프린트는 매우 유용하다.
블루프린트를 사용하면 코드를 변경하지 않고 변수를 조정할 수 있으며,
따라서 하드 코딩된 애셋 참조를 사용하지 않거나 
엔지니어링 지원 및 코드 변경을 요구하지 않고
단일 게임 모드를 여러 다른 레벨에 맞게 조정하며 적용할 수 있다.
 
 

Setting the Game Mode

 
레벨에 대한 게임 모드를 설정하는 방법에는 여러 가지가 있으며,
여기에서 가장 낮은 우선순위부터 가장 높은 우선순위 순으로 정렬된다.
 

  • DefaultEngine.ini 파일의 /Script/EngineSettings.GameMapsSettings 섹션에 있는
  • GlobalDefaultGameMode 항목을 설정하면
  • 프로젝트 내 모든 맵에 대한 기본 게임 모드가 설정된다.
[/Script/EngineSettings.GameMapsSettings]
GlobalDefaultGameMode="/Script/MyGame.MyGameGameMode"
GlobalDefaultServerGameMode="/Script/MyGame.MyGameGameMode"

 

  • 개별 맵에 대한 프로젝트 설정을 재정을 override하려면,
  • 에디터 월드 설정 탭에서 GameModeOverride를 설정한다. 
출저 : 언리얼 공식 문서

 
 

  • URL을 실행 파일에 전달하여 특정 옵션으로 게임을 로드하도록 강제할 수 있다.
  • game 옵션을 사용하여 게임 모드를 설정할 수 있다.
  • 자세한 내용은 명령 인수(Command-Line Arguments)를 참조하자.
UE4Editor.exe /Game/Maps/MyMap?game=MyGameMode -game

 

  • 마지막으로, 맵 접두사(및 URL 메서드에 대한 별칭)는
  • DefaultEngine.ini파일의 /Script/Engine.WorldSettings/ 섹선에서 설정할 수 있다.
  • 이러한 접두사는 주어진 접두사를 가진 모든 맵에 대해 기본 게임 모드를 설정한다.
[/Script/EngineSettings.GameMapsSettings]
+GameModeMapPrefixes=(Name="DM",GameMode="/Script/UnrealTournament.UTDMGameMode")
+GameModeClassAliases=(Name="DM",GameMode="/Script/UnrealTournament.UTDMGameMode")

 
 
 


 
 

Game State

 
게임에서 규칙과 관련된 이벤트가 발생하면 추적하고
모든 플레이어와 공유해야 하는 경우
해당 정보는 Game State를 통해 저장되고 동기화(synced)된다.
포함되는 정보는 다음과 같다.
 

출저 : 나 ❘ 참고자료 : 언리얼 공식문서

 

  • 게임이 실행된 기간(로컬 플레이어가 참가하기 전의 실행 시간 포함)
  • 각 개별 플레이어가 게임에 참여한 시간 및 해당 플레이어의 현재 상태.
  • 현재 게임 모드의 기본 클래스
  • 게임이 시작되었는지 여부

 
GameState는 클라이언트가 게임 상태를 모니터링할 수 있도록 하는 역할을 담당한다.
개념적으로 GameState는 연결된 모든 클라이언트가 알아아 하며,
특정 플레이어가 아닌 게임 모드에 특화된 정보를 관리해야 한다.
이는 연결된 플레이어 목록, 깃발 뺏기(Capture The Flag)에서 팀 점수,
오픈 월드 게임에서 완료된 미션 등과 같은 게임 전체의 속정을 추적할 수 있도록 한다.
 
GameState는 특정 플레이어가 깃발 뺏기(예시 게임임) 경기에서 팀을 위해
획득한 점수와 같은 플레이어 고유의 정보를 추적하기에 적합하지 않다.
이러한 정보는 플레이어 상태(PlayerState)에서 더 깔끔하게 처리할 수 있기 때문이다.
일반적으로, 게임 상태는 게임 플레이 중에 변경되는 속성을 추적해야 하며
모든 사람이 관련성 있고 볼수 있어야 한다.
게임 모드가 서버에만 존재하는 반면, GameState는 서버에 존재하고
모든 클라이언트에 복제되어 게임이 진행됨에 따라 연결된 모든 GameState를 업데이트 해줘야 한다.
 
 


 
 
해당 프로젝트에서는,
 
게임 모드는 전반적인 게임 규칙을 관리한다.
로그인 로그아웃, 플레이어의 레벨 이동 등을 관리하며,
 
게임 스테이트에서는
클라이언트가 게임 스테이트에 접속하여 해당 정보를 얻어간다.
특정 플레이어의 점수나 승리 횟수등에 대한 정보를 담는 것이 아닌,
게임 상태 정보를 담도록 설계되어 있다.
게임 스테이트에는 플레이어 상태 배열을 포함하며,
플레이어 상태 클래스는 점수 카운트와 같은 플레이어 고유 정보를 유지하도록 설계되었다.
게임 모드는 게임 스테이트에 접근할 수 있다.
해당 array (플레이어 state 배열)의 사이즈 체크를 통해 플레이어의 수를 체크할 수 도 있다.
(see by checking the size of this array player state)
 
 
 
해당 클래스들은 언리얼 내부에 제작되어 있으며
우리가 필요한 함수들을 override 해서 추가적으로 쓸 내용을 첨부하면 된다.
 
아래는 사용된 언리얼 내부 클래스의 예시이다.

 
위 함수들을 통해 LonIn, LogOut 설정을 완료 해준 뒤,
DefultGame.ini 에서 최대 연결될 플레이어 수를 결정해줄 수 있다.

 
 
The case when I used BP GameMode in my project

 
The case when I used C++ GameMode in my project
 

 
 
+ 메뉴 시스템 오류 잡기
델리게이트 꼬인 거 디버깅하면서 잡기
디버깅 매일 꾸준히 하기

+ 플러그인 제작 완료시키면
메인 프로젝트에 이식하기
 
참고 자료
https://docs.unrealengine.com/4.26/en-US/InteractiveExperiences/Framework/GameMode/