본문 바로가기

언리얼C++게임개발/04.게임플레이 프레임워크

[이득우UE프로그래밍] 게임플레이 프레임워크 - 게임모드

게임의 규칙을 관리하는 게임 모드와 플레이어가 조종하는 액터인 폰에 대해 알아보자.

프로젝트 세팅의 Selected GameMode 를 펼쳐보면, Default Pawn Class 항목에서 게임에 입장한 플레이어에게 주어질 조종할 수 있는 액터, 폰의 타입을 지정할 수 있다.

일단 실습을 위해 Save Current Level As로 step2로 저장한다 그럼 Tab이 Step2로 바뀐다.

Default GameMode가 ArenaBattleGame모드로 지정되어 있다는건 이미 어딘가 만들어져 있지만 연결해서 사용하기 불편하다. 언리얼이 초기에 만든 파일들은 건드리지 말고 확장해 사용하던지 직접 만들어서 연결해주는 편이 좋다.

언리얼 에디터에서 각각 GameModeBase상속받은  ABGameMode,, Pawn 을 상속받은 ABPawn C++클래스를 생성하자.

Public폴더에서 생성확인되고

블루프린트아이콘을 클릭해 GameMode Override를 ABGameModeBase로 지정해준다. 이러면 방금만든 게임모드가 step2 레벨에만 적용된다. 그래서 step2 레벨을 만든것이다.

오른쪽 디테일패널 WorldSettings에서도 변경및 확인가능하다.

여기까지만 해서 플레이 해보면 마네킹이 사라지고 일인칭 시점에서 탐험하게 된다.

F8을 누르면 마우스커서가 나타나는데 아웃라이너에서 DefaultPawn이  확인된다.

DefaultPawn을 방금만든 ABPawn으로 교체할것이다.

VS로 이동해 ABGameMode.h이동해 생성자를 추가해준다.

#pragma once

#include "EngineMinimal.h" //CoreMinimal.h 대신사용
#include "GameFramework/GameModeBase.h"
#include "ABGameModeBase.generated.h"

/**
AABGameModeBase
 */
UCLASS()
class ARENABATTLE_API AABGameModeBase : public AGameModeBase
{
	GENERATED_BODY()
	AABGameModeBase();  //생성자 선언
};

ABGameModeBase.h에서 생성자를 구현해준다.  AABPawn이 디폴트PawnClass가 되게 해준다

StaticClass()는 ABPawn의 자동으로 생성된 언리얼 오브젝트 클래스 정보를 자동으로 가져옴

#include "ABGameModeBase.h"
#include "ABPawn.h"
//AABGameModeBase() 생성자
AABGameModeBase::AABGameModeBase()
{
	DefaultPawnClass = AABPawn::StaticClass();  //디폴트PawnClass지정
}

VS빌딩후 언리얼이 리로딩후 디테일옆 월드세팅의 Selected GameMode를 펼쳐보면 ABPawn이 DefaultPawnClass로 잘 설정되어 있다. 문제는 플레이 해보면 움직이질 않는다. 이걸 해결하자

 

플레이어의 입장 - 플레이어 컨트롤러

플레이어가 입장하면 게임 모드는 플레이어에게 폰을 배정해 준다. 이때, 게임 모드는 플레이어 컨트롤러(PlayerController) 라는 특별한 액터도 함께 배정한다.

플레이어 컨트롤러는 게임 세계에서 현실 세계의 플레이어를 대변하는 무형의 액터이다. 플레이어가 입장할 때 배정되며 폰을 조종하는 역할을 맡는다. 배정된 플레이어 컨트롤러는 변경할 수 없다.

폰은 플레이어컨트롤러에 조종당하는 아바타다. 입력컨트롤이 되는 액터이다.

플레이어는 플레이어 컨트롤러를 통해 폰을 조정하고 새로운 폰을 선택할 수 있다.

언리얼 엔진에서 플레이어가 플레이어 컨트롤러를 통해 폰을 조종하는 행위를 빙의(Possess) 라고 한다.

플레이 버튼을 눌러 플레이어가 입장하면 게임 모드에 의해 다음과 같은 순서로 액터들이 생성되고 게임 플레이를 위한 설정이 갖춰진다.

  1. 플레이어 컨트롤러의 생성
  2. 플레이어 폰의 생성
  3. 플레이어 컨트롤러가 플레이어 폰을 빙의
  4. 게임의 시작

PlayerController를 부모로하는 ABPlayerController C++Class를 만들어보자.

VS로 가서 ABGameModeBase.cpp에 PlayerControllerClass를 지정해준다. 

#include "ABGameModeBase.h"
#include "ABPlayerController.h"
#include "ABPawn.h"
//AABGameModeBase() 생성자
AABGameModeBase::AABGameModeBase()
{
	DefaultPawnClass = AABPawn::StaticClass();  //디폴트PawnClass지정
	PlayerControllerClass = AABPlayerController::StaticClass(); //디폴트PlayerController지정
}

VS 빌드후 언리얼에디터 자동로딩시킨다. 월드세팅을 확인해본다.

 

플레이어가 로그인을 완료하면 게임 모드의 PostLogin 이벤트 함수가 호출되는데, 이 함수 내부에서 플레이어가 조종할 폰을 생성하고 플레이어 컨트롤러가 해당 폰에 빙의하는 작업이 이뤄진다. 폰과 플레이어 컨트롤러가 생성되는 시점은 각 액터의 PostInitializationComponents 함수로 파악할 수 있고, 빙의를 진행하는 시점은 플레이어 컨트롤러의 Possess, 폰의 PossessedBy 함수로 파악할 수 있다.

이제 로그를 찍은 코드를 보고 출력 결과를 파악해 보자.

 

ABGameModeBase.h 에 PostLogin()을 선언해준

...
// 추가
virtual void PostLogin(APlayerController* NewPlayer) override;
...

 

ABGameModeBase.cpp 에  코드를 구현해준다

void AABGameModeBase::PostLogin(APlayerController* NewPlayer)
{
	UE_LOG(LogTemp, Warning, TEXT("Post Login Begin"));
	Super::PostLogin(NewPlayer);
	UE_LOG(LogTemp, Warning, TEXT("Post Login End"));
}

교재의 possess()는 V4.22이후로 final로 override가 안된다 OnPossess()로 수정해야한다

ABPlayerController.h

#include "EngineMinimal.h"
...
virtual void PostInitializeComponents() override;
//virtual void Possess(APawn* aPawn) override; V4.22이후 아래로 바귐
virtual void OnPossess(APawn* aPawn) override;
virtual void OnUnPossess() override;

 

ABPlayerController.cpp

여기도 OnPossess로 변경해준다.  문제는 Super::OnPossess도 함께 바꿔야 한다

void AABPlayerController::PostInitializeComponents()
{
	Super::PostInitializeComponents();
	ABLOG_S(Warning);
}

void AABPlayerController::OnPossess(APawn* InPawn)
{
	ABLOG_S(Warning);
	Super::OnPossess( InPawn);
}

void AABPlayerController::OnUnPossess()
{
	ABLOG_S(Warning);
	Super::OnUnPossess();
}

void AABPlayerController::OnPossess(APawn* aPawn) V4.22이후로 지원하지 않는다.
{
	ABLOG_S(Warning);
	Super::Possess(aPawn);
}

 

ABPawn.h

#include "ArenaBattle.h"
...
virtual void PostInitializeComponents() override;
virtual void PossessedBy(AController* NewController) override;

 

ABPawn.cpp

void AABPawn::PostInitializeComponents()
{
	Super::PostInitializeComponents();
	ABLOG_S(Warning);
}

void AABPawn::PossessedBy(AController* NewController)
{
	ABLOG_S(Warning);
	Super::PossessedBy(NewController);
}

 

로그를 보고 순서를 확인해 보자.

 

플레이 해보면 언리얼에디터가 멈춘다. 일단은 GameMode에서 PlayerControllerClass를 ABPlayerController에서 원래 PlayerController로 돌려주었더니 해결되었다. OnPossess로 바꾼게 문제인듯하다. 나중에 체크해보겠다

Auto Possess Player

폰의 Auto Possess Player 옵션을 사용하면 게임 모드는 ABPawn 액터를 생성하지 않고, 레벨에 배치된 해당 폰의 액터에 빙의하라는 명령을 내리게 만들 수 있다. (Player 0은 로컬 플레이어를 가리킴)

 

만약 C++ 로 생성된 액터가 아닌 블루프린트로 생성된 에셋를 사용하고자 한다면, 애셋 경로에 ' _C ' 를 붙여서 사용하면 된다. 다음은 기본 ThirdPersonCharacter 블루프린트를 사용하는 예제 코드이다.

static ConstructorHelpers::FClassFinder<APawn> BP_PAWN_C(TEXT("/Game/ThirdPersonCPP/Blueprints/ThirdPersonCharacter.ThirdPersonCharacter_C"));
if (BP_PAWN_C.Succeeded())
{
    DefaultPawnClass = BP_PAWN_C.Class;
}

이 예제는 블루프린트의 클래스 정보도 C++에서 가져 올 수 있다는 것을 학습하기 위한것으로 앞으로 예제의 마네킹은 더 이상 사용하지 않을것이므로 주석처리한거나 입력하지 않아도 된다.