PlayerController란?
- PlayerController는 사용자가 키보드, 마우스, 게임패드 등에서 입력을 받으면, 그 입력을 해석하여 캐릭터나 다른 오브젝트에게 동작을 명령하는 핵심 클래스입니다.
- 언리얼 엔진의 중요한 철학 중 하나는 “플레이어 입력은 PlayerController에서 처리한다”는 것입니다. 이를 통해 입력 처리 로직과 실제 캐릭터의 동작 로직을 분리할 수 있어, 코드를 구조적으로 관리하기가 훨씬 수월해집니다.
- 입력이 처리되는 기본 흐름
- 키보드, 마우스, 게임패드 등 입력 장치로부터 사용자 조작 신호가 들어옵니다.
- 이 신호는 PlayerController가 받아서 해석합니다.
- PlayerController가 현재 소유 (Possess)하고 있는 Pawn에게 이동, 회전, 공격 등의 구체적인 명령을 내립니다.
- 특히 멀티플레이 환경에서는, 각 플레이어마다 개별 PlayerController가 생성되므로, 여러 사용자의 입력을 충돌 없이 분리하고 관리할 수 있습니다.
PlayerController의 주요 기능
- 입력 처리
- 키보드, 마우스, 게임패드, 터치 등 다양한 입력 장치의 이벤트를 처리합니다.
- 언리얼 엔진 5에서 제공하는 Enhanced Input 시스템을 사용하면, 액션/축 매핑을 보다 체계적으로 설정할 수 있습니다.
- C++에서는 SetupInputComponent() 함수를 오버라이드하여, 블루프린트에서는 이벤트 그래프를 통해 입력 로직을 구현합니다.
- 카메라 제어 로직
- 마우스나 게임패드의 축 입력을 받아 캐릭터의 시점 회전이나 줌 인/아웃 같은 카메라 동작을 수행할 수 있습니다.
- HUD 및 UI와의 상호작용
- 언리얼의 UMG (언리얼 모션 그래픽) 기반 UI를 통해 버튼 클릭, 드래그, 터치 등의 이벤트를 PlayerController에서 받을 수 있습니다.
- 예를 들어 인벤토리 열기, 스킬 사용 등의 명령을 UI에서 트리거하면 PlayerController가 이를 해석해 Pawn 또는 GameMode 등 다른 시스템으로 전달할 수 있습니다.
- Possess / UnPossess
- PlayerController는 특정 Pawn에 “빙의 (Possess)”하여 해당 Pawn을 제어합니다.
- 필요할 때 UnPossess() 함수를 호출하여 Pawn과의 연결을 해제한 뒤, 다른 Pawn으로 바꿔 탈 수도 있습니다.
- 멀티플레이 시 각 플레이어마다 고유의 PlayerController가 있고, 이 컨트롤러가 특정 Pawn을 소유함으로써 서로 다른 캐릭터 조작이 가능합니다.
C++ 클래스 APlayerController 생성
언리얼 엔진에서 커스텀 PlayerController를 만들기 위해서는, 보통 PlayerController 클래스를 상속받은 후 필요한 기능을 추가·확장합니다.
- 에디터 상단 메뉴에서 Tools → New C++ Class를 선택
- Common Classes 목록에서 Player Controller를 선택하거나, 검색 창에 PlayerController를 입력해 해당 클래스를 선택
- 클래스 이름을 지정하고, Class Type은 Public으로 설정한 뒤 Create Class
PlayerController를 GameMode에 적용
헤더
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/GameMode.h"
#include "SpartaGameMode.generated.h"
UCLASS()
class SPARTAPROJECT_API ASpartaGameMode : public AGameMode
{
GENERATED_BODY()
public:
ASpartaGameMode();
};
cpp
#include "SpartaGameMode.h"
#include "SpartaCharacter.h"
#include "SpartaPlayerController.h" // PlayerController 클래스를 사용
ASpartaGameMode::ASpartaGameMode()
{
DefaultPawnClass = ASpartaCharacter::StaticClass();
PlayerControllerClass = ASpartaPlayerController::StaticClass();
}
- PlayerControllerClass는 GameMode가 제공하는 속성으로, 게임 시작 시 사용할 PlayerController 타입을 지정합니다.
- ASpartaPlayerController::StaticClass()는 언리얼 엔진이 ASpartaPlayerController 클래스의 정보를 런타임에 참조할 수 있도록 제공하는 정적 함수입니다.
- 위 코드를 저장 후 프로젝트 빌드를 완료하면, 언리얼 에디터에서 GameMode가 SpartaPlayerController를 기본 PlayerController로 인식하게 됩니다.
블루프린트로 랩핑해주기
게임 모드로 가서 default pawn class를 블루프린트로 감싼 플레이어 컨트롤러로 지정하기

Enhanced Input System
1. input action(IA) - 추상적인 행동(일종의 전선들)
- 특정 동작을 추상화하는 추상 단위
점프 -> IA_Jump -> Jump 함수
마우스 회전 -> IA_Look -> Look 함수
이동 -> IA_Move
2. input mapping system(IMC) - (스위치 같은 것)
- IA들을 총괄해서 관리하는...
ex. a, b, c라는 스위치가 있으면 a는 점프, b는 마우스 회전 등등으로 설정하는 것
버튼을 바꾸고 싶으면 a, b, c 같은 imc들만 바꾸고 점프를 구현한 IA_Jump의 내부 로직같은 것들은 안 바꿔도 돼서 편함
그리고 캐릭터를 움직이다가 자동차 같은 거에 탐
그럼 이제 자동차의 입력으로 바뀔 텐데 그럴 때 IMC를 갈아끼우면 됨.
자동차 전용 IMC로!
그때그때 상황에 따라 어떤 걸 활성화 해줄지만 if문으로 켜주면 됨!
IA 파일을 더블 클릭해 열면 아래와 같은 주요 속성을 설정할 수 있습니다:
- Value Type은 Input Action (IA)이 입력 동작을 발생시킬 때, 어떤 유형의 값을 제공할지 결정하는 옵션입니다.
- Bool (참/거짓)
- 단순 On/Off 토글 입력에 사용됩니다.
- 예) 점프(스페이스바), 공격(마우스 왼쪽 버튼)
- Axis1D (1차원 축 값)
- 단일 축 (-1~1 범위)의 입력에 사용됩니다.
- 예) 게임패드 트리거(가속 페달), 전진/후진(W/S)
- Axis2D (2차원 축 값)
- X, Y 두 축을 동시에 처리할 때 사용됩니다.
- 예) 캐릭터 이동(WASD), 마우스 이동(가로+세로)
- Axis3D (3차원 축 값)
- X, Y, Z 세 축을 동시에 처리합니다.
- 예) 비행 시뮬레이션에서 3축 제어
- Bool (참/거짓)
- 트리거 (Trigger)는 입력이 활성화되는 특정 조건을 말합니다.
- Pressed Trigger: 키를 누르는 순간에만 작동.
- Hold Trigger: 키를 일정 시간 눌렀을 때 작동.
- Released Trigger: 키를 뗄 때 작동.
- 모디파이어 (Modifier)는 입력 값을 수정하거나 변환하기 위한 설정입니다.
- Scale : 입력 값에 일정 배율을 곱해줌 (마우스 이동 속도 2배)
- Swizzle Input Axis Values : 입력 값의 축을 재구성 해주는 것 (XYZ축을 YXZ축으로 변경한다던지...)
- Invert : 입력 값을 반전 (상하 반전 카메라)
- Deadzone : 일정 임계값보다 작은 입력은 무시 (게임패드 조이스틱 미세 떨림 방지)
Input Action (IA) 생성과 설정
- 캐릭터 행동
1. 이동 WASD (IA_Move)

인풋 폴더 생성 -> 우클릭 -> 위와 같이 만들기 -> IA_Move로 이름 생성

- 이동은 일반적으로 "앞/뒤"와 "왼/오른쪽" 두 방향을 동시에 처리하므로, Value Type은 Axis2D로 설정합니다.
- IA_Move는 항상 활성화된 상태에서 WASD 입력만으로 동작해야 하므로 트리거나 모디파이어가 필요하지 않습니다. 예를 들어, "Shift 키를 눌러야만 이동 가능" 같은 조건이 없다면 생략해도 됩니다.
2. 점프 (IA_Jump)
스페이스바를 누르면 점프하는 간단한 방식이므로, Value Type을 Bool로 설정합니다. 점프 동작은 단순히 "On/Off"로 동작하기 때문에 키가 눌렸는지 여부만 중요하며, 추가적인 수치나 축 값이 필요하지 않습니다.
3. 스프린트 (shift 누르면 캐릭터가 빨리 달림) (IA_Sprint)
스프린트 또한 단순히 Shift 키 값으로 속도를 늘리거나 줄이고를 설정해주므로, value type을 bool로 (On/Off) 로 설정하고 저장해줍니다.
4. 마우스 회전 (IA_Look)
마우스 입력은 항상 가로 (X축, 좌/우)와 세로 (Y축, 위/아래) 움직임을 동시에 포함합니다. 따라서 Value Type을 Axis2D로 설정해줍니다.
Input Mapping Context (IMC) 생성과 매

- Content Browser의 Inputs 폴더에서 우클릭 → Input → Input Mapping Context를 선택합니다. 생성된 파일의 이름을 지정합니다.
- 생성된 IMC_ 에셋을 더블 클릭하여 열고, Mappings 목록에 앞서 만든 IA_Move, IA_Jump, IA_Look 등을 각각 추가합니다.

- W 키 (전진)
- W 키를 누르면 입력 값이 X축 (앞뒤 방향)에 맞춰 정렬됩니다.
- 전진은 X축 +1 방향이므로 추가적인 변환은 필요 없습니다.

- S 키 (후진)
- S 키의 입력 값도 Swizzle을 통해 X축으로 정렬됩니다.
- 후진은 전진 (W)의 반대 방향이므로 Negate를 사용해 입력 값을 뒤집습니다. (X축 +1 → X축 -1)

- A 키 (왼쪽 이동)
- A 키를 누르면 입력 값이 Y축 (좌우 방향)에 맞춰 정렬됩니다.
- A키는 오른쪽 D 이동의 반대 방향이므로 Negate를 사용해 Y축의 값을 반전합니다. (Y축 +1 → Y축 -1)

- D 키 (오른쪽 이동)
- D 키의 입력값도 Swizzle을 통해 Y축으로 정렬됩니다.
- D 키는 Y축 (좌우 방향)의 +1 방향에 매핑되므로 추가 변환이 필요 없습니다.

- 점프는 스페이스바를 눌렀을 때 On/Off 형태로 동작합니다.
- 별도의 트리거나 모디파이어가 필요 없으므로, 기본 상태로 둡니다.

- 마우스 움직임을 통해 Yaw (좌우 회전)과 Pitch (상하 회전) 값을 동시에 전달받습니다.
- Axis2D로 지정한 X축, Y축 값을 게임 내 좌표계에 맞춰 회전에 반영합니다.
- 기본적으로 마우스 Y축의 움직임은 "위로 움직임 = 양수, 아래로 움직임 = 음수"로 전달됩니다.
- 하지만, 카메라의 상하 회전 (Pitch)은 엔진의 좌표계에서 "위로 = 음수, 아래로 = 양수"로 작동하는 경우가 많습니다. 따라서, Y축 값을 Negate Modifier를 통해 반전하여야 합니다.
- 위로 움직임 → 음수(-) (카메라 위로).
- 아래로 움직임 → 양수(+) (카메라 아래로).
- X축 (좌우 회전, Yaw)의 경우, 기본적인 엔진 좌표계와 마우스 움직임의 방향이 일치합니다.
- 오른쪽 이동 → 양수(+).
- 왼쪽 이동 → 음수(-).

- 스프린트는 왼쪽 Shift 키를 눌렀을 때 On/Off 형태로 동작합니다.
- Key를 “Left Shift”로 지정하고 트리거나 모디파이어는 기본 상태로 두고 다시 저장을 합니다.
PlayerController에서 IMC 활성화하기
<player controller 헤더>
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/PlayerController.h"
#include "SpartaPlayerController.generated.h"
class UInputMappingContext; // IMC 관련 전방 선언
class UInputAction; // IA 관련 전방 선언
UCLASS()
class 솔루션이름_API A플레이어컨트롤러이름 : public APlayerController
{
GENERATED_BODY()
public:
A플레이어컨트롤러이름();
// 에디터에서 세팅할 IMC
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Input")
UInputMappingContext* InputMappingContext;
// IA_Move를 지정할 변수
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Input")
UInputAction* MoveAction;
// IA_Jump를 지정할 변수
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Input")
UInputAction* JumpAction;
// IA_Look를 지정할 변수
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Input")
UInputAction* LookAction;
// IA_Sprint를 지정할 변수
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input")
UInputAction* SprintAction;
};
<cpp 파일>
#include "SpartaPlayerController.h"
ASpartaPlayerController::ASpartaPlayerController()
: InputMappingContext(nullptr),
MoveAction(nullptr),
JumpAction(nullptr),
LookAction(nullptr),
SprintAction(nullptr)
{
}
-> 어차피 블루프린트에서 초기화 해줄 거기 때문에, 코드에선 널포인터(nullptr) 처리를 해줌!
아무것도 할당 안 하는 것보단 널포인터 해주는 게 더 안전함.

-> 에셋 할당!
IMC 활성화 코드 작성
- 이번에는 C++에서 실제로 IMC를 활성화하는 코드를 작성해보겠습니다. 언리얼 5의 Enhanced Input System은 Local Player Subsystem을 통해 Input Mapping Context를 활성화하거나 비활성화합니다.
- Local Player Subsystem이란?
- 게임이 실행되면 언리얼은 각 플레이어를 표현하기 위해 Local Player 객체를 생성합니다.
- 싱글플레이어 상황에서는 하나의 Local Player
- 로컬 멀티플레이 (하나의 화면에서 여러 명이 플레이)라면 플레이어 수만큼 Local Player가 생성됩니다.
- UEnhancedInputLocalPlayerSubsystem 은 Local Player에 부착되어, 해당 플레이어가 사용할 입력 매핑 (IMC)을 관리합니다.
- 이를 통해, 플레이 중에 동적으로 다른 IMC를 추가·제거하여 입력 모드를 전환할 수 있습니다.
- 예) 전투 중 (IMC_Character) → UI창 열림 (IMC_UI) → 전투 종료 후 다시 (IMC_Character)
- 게임이 실행되면 언리얼은 각 플레이어를 표현하기 위해 Local Player 객체를 생성합니다.
- ASpartaPlayerController 클래스에서 BeginPlay() 함수를 오버라이드하여, Blueprint에서 지정해둔 IMC를 활성화하는 코드를 추가하겠습니다.
<헤더 파일>
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/PlayerController.h"
#include "SpartaPlayerController.generated.h"
class UInputMappingContext;
class UInputAction;
UCLASS()
class SPARTAPROJECT_API ASpartaPlayerController : public APlayerController
{
GENERATED_BODY()
public:
ASpartaPlayerController();
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input")
UInputMappingContext* InputMappingContext;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input")
UInputAction* MoveAction;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input")
UInputAction* JumpAction;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input")
UInputAction* LookAction;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input")
UInputAction* SprintAction;
virtual void BeginPlay() override;
};
<cpp 파일>
#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)
{
// Subsystem을 통해 우리가 할당한 IMC를 활성화
// 우선순위(Priority)는 0이 가장 높은 우선순위
Subsystem->AddMappingContext(InputMappingContext, 0);
}
}
}
}
- GetLocalPlayer()
- 현재 PlayerController가 관리하는 Local Player를 반환합니다.
- GetSubsystem<UEnhancedInputLocalPlayerSubsystem>()
- 해당 Local Player에 부착된 Enhanced Input Subsystem을 반환합니다.
- 이를 통해 AddMappingContext나 RemoveMappingContext 등을 호출하여 입력 매핑을 동적으로 제어할 수 있습니다.
- AddMappingContext()
- 주어진 IMC를 Subsystem에 추가하여 입력 매핑을 활성화합니다.
- SpartaInputMappingContext: 활성화할 IMC.
- 0: 우선순위. 낮을수록 높은 우선순위를 가집니다.
- 이 함수를 여러 번 호출해 여러 IMC를 활성화할 수도 있습니다.
- 우선순위를 달리 부여해, 특정 IMC가 다른 IMC보다 우선순위가 높도록 설정할 수도 있습니다.
이 로직이 실행되면, IMC에 정의된 모든 IA와 키 매핑이 이 PlayerController에 적용됩니다.
IMC 적용 확인 및 테스트
블루프린트 위쪽에 open full blueprint editor로 들어가서 event graph 열기!

이 IA 이벤트들을 Blueprint 상에서 연결하여 간단한 테스트를 해볼 수 있습니다. 예를 들어 Print String 노드를 붙여서, 키나 마우스를 움직일 때마다 문자열이 출력되는지 확인할 수 있습니다.

게임을 플레이하고 직접 WASD, Spacebar, 마우스 이동 등을 해보면, 해당 이벤트 노드가 정상적으로 호출되며 문자열이 출력되는 것을 볼 수 있습니다.
'학습 > Unreal' 카테고리의 다른 글
| 언리얼 UObject 동작 원리 정리 (CDO, Reflection, GC, Serialization) (0) | 2026.04.27 |
|---|---|
| [실습] Pawn 클래스 3D캐릭터 만들기 (0) | 2026.04.16 |
| Character 클래스를 활용한 캐릭터 구현하기 (0) | 2026.04.15 |
| [실습] Actor 회전과 이동 기능 구현 (0) | 2026.04.14 |
| C++ 클래스와 리플렉션 시스템 활용하기 (0) | 2026.04.10 |
