PlayerController는 플레이어의 입력을 처리하고 게임 세계의 폰(Pawn)을 제어하는 언리얼 엔진의 핵심 클래스이다. 플레이어와 게임 세계 간의 다리역할을 하며, 폰을 직접 제어하지 않고 입력을 해석하여 폰에 명령을 전달한다. 멀티플레이어 환경에서는 각 플레이어마다 고유한 PlayerController
가 생성된다.
-> PlayerController는 플레이어 입력을 처리한다.
입력이 처리되는 기본 흐름
플레이어의 입력 처리
-키보드, 마우스, 게임패드 등의 입력을 받아 게임 내 동작으로 변환한다.
카메라 관리
HUD 및 UI와의 상호작용
폰 관리
PlayerController
는 AController
를 상속하며, 추가적인 입력 처리 및 플레이어 중심 로직을 제공한다.
클래스 상속 구조 : AActor
- AController
- APlayerController
주요 컴포넌트 :
PlayerController
가 제어할 폰 객체 1. Possess()
PlayerController
는 Possess()
를 통해 폰과 연결된다.DefaultPawnClass = ASpartaCharacter::StaticClass();
로 기본 Pawn을 지정한거랑은 다른 의미이다.2. UnPossess()
3. SetupInputComponent()
void AMyPlayerController::SetupInputComponent()
{
Super::SetupInputComponent();
InputComponent->BindAxis("MoveForward", this, &AMyPlayerController::MoveForward);
InputComponent->BindAxis("Turn", this, &AMyPlayerController::Turn);
}
4. GetPawn()
APawn* ControlledPawn = GetPawn();
5. PlayerTick()
PlayerController
의 틱 로직을 실행한다.#include "SpartaGameMode.h"
#include "SpartaCharacter.h"
#include "SpartaPlayerController.h"
ASpartaGameMode::ASpartaGameMode()
{
DefaultPawnClass = ASpartaCharacter::StaicClass();
PlayerControllerClass = ASpartaPlayerController::StaticClass();
}
BP_SpartaGameMode에서 DefaultPawnClass를 설정해주었던 것과 마찬가지로 PlayerController를 블루프린트 클래스를 만들어서 BP_SprataGameMode에서 PlayerControllerClass를 설정해주도록 한다.
언리얼 엔진의 Enhanced Input System은 기존의 입력 처리 시스템(Input Mapping System)을 대체하고 확장한 시스템이다. 더욱 유연하고 강력한 입력 관리 기능을 제공한다. 액션 중심의 입력 처리를 지원하며 여러 장치와 상황별 입력 처리를 더 쉽게 구현할 수 있도록 설계 되었다.
InputAction은 입력의 '의미'를 정의하는 추상적인 개념이다.
단순히 키를 누르는 것에 국한되지 않고, 축, 데이터 처리(마우스 이동량) 및 디지털 입력(버튼 클릭)까지 처리할 수 있다.
예시)
IA_Move
: 캐릭터 이동IA_Look
: 카메라 회전IA_Jump
: 점프InputAction
에 전달 된다.IA_Move
InputActiondl 이 활성화되고 이동 데이터가 전달된다.Pawn
은 InputAction
에서 전달된 데이터를 읽고, 실제 게임 동작(로직)을 수행한다.에디터에서 IA_Move(FVector2D), IA_Look(FVector2D), IA_Jump(bool), IA_Sprint(bool)를 만들고 이들을 IMC에서 어떤 키와 연결할지 어느 축과 연결할지 세팅을 해주었다. 이제 이것들을 우리가 이전에 만들었던 PlayerController와 연결을 시켜주어야 한다.
우선 헤더파일에서 우리가 만든 IA파일들과 IMC를 멤버 변수로 선언하고 이를 에디터에서 지정할 수 있도록 리플렉션(UPROPERTY)처리 한다.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/PlayerController.h"
#include "SpartaPlayerController.generated.h"
class UInputMappingContext; // IMC 전방선언
class UInputAction; // IA 전방선언
UCLASS()
class SPARTAPROJECT_API ASpartaPlayerController : public APlayerController
{
GENERATED_BODY()
public:
ASpartaPlayerController();
// 에디터에서 세팅할 IMC
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input")
UInputMappingContext* InputMappingContext;
// 이후로는 IA를 지정할 변수들
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input")
UInputAction* MoveAction;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input")
UInputAction* LookAction;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input")
UInputAction* JumpAction;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input")
UInputAction* SprintAction;
};
#include "SpartaPlayerController.h"
// 초기화 리스트를 사용해서 IMC, IA 변수 초기화
ASpartaPlayerController::ASpartaPlayerController()
: InputMappingContext(nullptr),
MoveAction(nullptr),
LookAction(nullptr),
JumpAction(nullptr),
SprintAction(nullptr) {}
이렇게 하고 ASpartaPlayerController를 상속받는 BP 클래스에서 우리가 만든 에셋들을 할당할 수 있다.
이제 C++ 클래스에서 선언한 변수들에 우리가 만든 에셋들이 연결이 되었으므로 IMC를 활성화하는 코드를 작성하면 된다. Local Player Subsystem을 통해 Input Mapping Context를 활성화하거나 비활성화 할 수 있다.
우선 LocalPlayerSubsystem이 무엇인가에 대해 알아야 한다.
LocalPlayerSubsystem
은 언리얼 엔진의 서브시스템 프레임워크의 일부로, 플레이어의 로컬 입력 및 로직을 관리하는데 사용된다. 특히 IMC와 같은 로컬 입력 관련 설정을 관리하는데 중요한 역할을 한다.
Subsystem 프레임워크는 언리얼 엔진에서 특정 컨텍스트에서 동작하는 전역적이고 재사용 가능한 로직을 구현하는 데 사용된다. 여러 유형이 있다.
Enhanced Input System은 로컬 플레이어 수준에서 작동하는 InputSubsystem을 통해 관리된다.
UEnhancedInputLocalPlayerSubsystem이 바로 이 역할을 담당하며,(즉, Local Player의 Input Subsystem이 UEnhancedInputLocalPlayerSubsystem이다) 로컬 플레이어의 입력 데이터를 기반으로 동작한다.
ULocalPlayer 객체는 게임 실행 시 GameInstance 초기화 과정에서 생성된다. 기본적으로, 각 로컬 플레이어는 고유한 LocalPlayer 객체를 가진다. 그리고 PlayerController와 연결된다.
UlocalPlayer는 연결된 PlayerController를 통해 입력과 뷰포트를 관리한다.
Enhanced Input System은 전체 시스템의 "입력 액션 정의"와 "전체 입력 시스템의 핵심 역할"을 한다
UEnhancedInputLocalPlayerSubsystem은 Local Player의 Input Subsystem이면서 Enhanced Input System의 "하위 시스템"으로, 특정 로컬 플레이어의 입력 처리를 담당한다. 로컬 플레이어의 입력 컨텍스트를 추가하거나 특정 상황에 맞게 동작하도록 제어한다.
virtual void BeginPlay() override;
추가#include "SpartaPlayerController.h"
#include "EnhancedInputSubsystems.h" // Enhanced Input System의 Local Player Subsystem을 사용하기 위해 포함
ASpartaPlayerController::ASpartaPlayerController()
: InputMappingContext(nullptr),
MoveAction(nullptr),
JumpAction(nullptr),
LookAction(nullptr),
SprintAction(nullptr)
{
}
void ASpartaPlayerController::BeginPlay()
{
Super::BeginPlay()
// PlayerController에 연결된 Local Player 객체를 가져옴
if(ULocalPlayer* LocalPlayer = GetLocalPlayer())
{
// Local Player에서 EnhancedInputLocalPlayerSubsystem을 가져옴
if(UEnhancedInputLocalPlayerSubsystem* Subsystem =
Localplayer->GetSubsystem<UEnhancedInputLocalPlayerSubsystem>())
{
if (InputMappingContext)
{
// InputMappingContext이 할당되어 있다면
// Subsystem을 통해 우리가 할당한 IMC 활성화
// 0은 우선순위 Index
Subsystem->AddMappingContext(InputMappingContext, 0);
}
}
}
}
// InputMappingContext는 앞서 선언한 변수
이 로직으로 인해서 IMC에 정의된 IA와 키들이 PlayerController에 적용된다. 하지만 지금은 구현이 되지 않은 상태이므로 캐릭터가 움직이진 않는다.
캐릭터 입력 매핑 활성화시키는것이 되게 어려운 개념이였다. Local Player가 뭔지, Enhanced Input system과 EnhancedInputLocalPlayerSubsystem은 어떻게 다른지 그리고 이 서브시스템을 통해 InputMappingContext를 추가 및 삭제를 할 수 있는지 ... 이제야 조금 가닥이 잡히지만 처음엔 이름도 너무 길고 개념이 너무 추상적일 때도 있고 많아서 힘들었던 것 같다.