C++와 Unreal Engine으로 3D 게임 개발

C++와 Unreal Engine으로 3D 게임 개발 2-2

jh009 2026. 6. 10. 17:02

2-2. Enhanced Input System을 활용한 입력 매핑 구현하기

1. PlayerController 이해하기

 

PlayerController

사용자가 키보드, 마우스, 게임패드 등에서 입력을 받으면, 그 입력을 해석하여 캐릭터나 다른 오브젝트에게 동작을 명령하는 핵심 클래스

 

언리얼 엔진의 중요한 철학 중 하나 "플레이어 입력은 PlayerController 에서 처리한다"

입력 처리 로직과 실제 캐릭터의 동작 로직을 분리 가능 → 코드를 구조적으로 관리하기가 훨씬 수월해짐

 

입력이 처리되는 기본 흐름

  • 입력 장치로부터 사용자 조작 신호가 들어옴
  • 이 신호는 PlayerController가 받아서 해석
  • PlayerController가 현재 소유 (Possess)하고 있는 Pawn에게 이동, 회전, 공격 등의 구체적인 명령 지시

멀티플레이 환경

각 플레이어마다 개별 PlayerController가 생성 → 여러 사용자의 입력을 충돌 없이 분리하고 관리할 수 있음


PlayerController의 주요 기능

입력 처리

Enhanced Input 시스템 사용 → 액션/축 매핑을 보다 체계적으로 설정 가능

 

카메라 제어 로직

축 입력을 받아 캐릭터의 시점 회전이나 줌 인/아웃 같은 카메라 동작을 수행

 

HUD 및 UI와의 상호작용

언리얼의 UMG (언리얼 모션 그래픽) 기반 UI를 통해 버튼 클릭, 드래그, 터치 등의 이벤트

 

Possess / UnPossess

특정 Pawn에 빙의 (Possess)하여 해당 Pawn을 제어

UnPossess() 함수를 호출하여 Pawn과의 연결을 해제한 뒤, 다른 Pawn으로 바꿔 탈 수도 있음


2. PlayerController C++ 클래스 생성하기

C++ 클래스 APlayerController 생성

Tools → New C++ Class → Common Classes 목록 → Player Controller 선택 or 검색 창에 PlayerController를 입력 후 선택

Class Name : SpartaPlayerController

Class Type : Public

 

SpartaPlayerController.h

 

SpartaPlayerController.cpp


PlayerController를 GameMode에 적용

SpartaPlayerController 실제 사용을 위해 GameMode에 등록해야 함

 

SpartaGameMode.h

 

SpartaGameMode.cpp

 

PlayerControllerClass

  • GameMode가 제공하는 속성
  • 게임 시작 시 사용할 PlayerController 타입을 지정

PlayerController Blueprint 생성

블루프린트로 래핑(wrap)하는 과정

 

Content Browser → SpartaPlayerController 클래스를 우클릭 → Blueprint Class 선택

이름 설정: BP_SpartaPlayerController 

 

BP_SpartaGameMode → Player Controller Class → BP_SpartaPlayerController 설정

 


3. Enhanced Input System의 이해 및 Input Action 설정하기

Enhanced Input System

언리얼 엔진 5에서 Project Settings → Input 시스템을 대체 및 확장을 위해 Enhanced Input 시스템을 제공

 

Input Action (IA)

캐릭터의 이동, 점프, 발사, 줌 등과 같이 특정 동작을 추상화한 단위

 

Input Action (IA) 설정

Value Type

Bool

단순 On/Off 토글 입력에 사용

예시: 점프(스페이스바), 공격(마우스 왼쪽 버튼)

 

Axis1D (1차원 축 값)

단일 축 (-1~1 범위)의 입력에 사용

예시: 게임패드 트리거(가속 페달), 전진/후진(W/S)

 

Axis2D (2차원 축 값)

X, Y 두 축을 동시에 처리할 때 사용

예시: 캐릭터 이동(WASD), 마우스 이동(가로+세로)

 

Axis3D (3차원 축 값)

X, Y, Z 세 축을 동시에 처리

예시: 비행 시뮬레이션에서 3축 제어


IA_Move

Value Type: Axis2D

앞/뒤, 왼/오른쪽 두 방향을 동시에 처리 → Value Type: Axis2D

 

IA_Look

Value Type: Axis2D

가로 (X축, 좌/우), 세로 (Y축, 위/아래) 움직임을 동시에 포함

 

IA_Jump

Value Type: Bool

On/Off로 동작 / Spacebar 키가 눌렸는지 여부만 중요 

 

IA_Sprint

Value Type: Bool

Shift 키 값으로 속도를 늘리거나 줄이고를 설정


4. Input Mapping Context 설정하기

Input Mapping Context (IMC)

여러 개의 IA들을 모아놓은 매핑 설정 파일

게임 진행 중 특정 상황에서 IMC를 활성 (Enable)하거나 비활성 (Disable)하여 입력을 제어 가능

 

Input Mapping Context (IMC) 매핑

 

IA_Move

Swizzle Input Axis Values

  • 입력 축 (Axis)을 변환하거나 재구성하는 기능
  • 언리얼 엔진의 입력 시스템에서는 입력 데이터를 X, Y, Z 축 중 하나로 매핑
  • Swizzle은 이 입력 값이 올바른 축에 맞지 않을 경우, 특정 축으로 재배치 혹은 변환할 수 있게 도와줌

전진/후진

W 키를 누르면 입력 값이 X축에 맞춰 정렬 (X축의 +1 방향에 매핑 → 추가 변환 필요없음 )

S 키의 입력 값도 Swizzle을 통해 X축으로 정렬  (X축의 반대방향 →  Negate 입력 값 뒤집기 X축 -1)

 

좌/우

A 키를 누르면 입력 값이 Y축에 맞춰 정렬 (Y축의 반대방향  Negate 입력 값 뒤집기 Y축 -1)

D 키의 입력값도 Swizzle을 통해 Y축으로 정렬 (Y축의 +1 방향에 매핑 → 추가 변환 필요없음)


IA_Look

Axis2D로 지정한 X축, Y축 값을 게임 내 좌표계에 맞춰 회전에 반영

 

기본적인 마우스의 움직임

Y축 위로 움직임 = 양수

Y축 아래로 움직임 = 음수

 

카메라의 상하 회전 (Pitch)은 엔진의 좌표계

위 = 음수, 아래 = 양수 로 작동하는 경우가 많음 → Y축 값을 Negate Modifier를 통해 반전

  • 위로 움직임 → 음수(-) (카메라 위로)
  • 아래로 움직임 → 양수(+) (카메라 아래로)

X축 (좌우 회전, Yaw) 기본적인 엔진 좌표계와 마우스 움직임의 방향이 일치

  • 오른쪽 이동 → 양수(+)
  • 왼쪽 이동 → 음수(-)


IA_Jump

 

Spacebar 로 키 지정 / 눌렀을 때 On/Off 형태로 동작

별도의 트리거나 모디파이어가 필요x


IA_Sprint

Left Shift 로 키 지정 / 눌렀을 때 On/Off 형태로 동작

별도의 트리거나 모디파이어가 필요x


5. PlayerController에서 IMC 활성화하기

PlayerController 클래스 준비

// SpartaPlayerController.h
#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_Move를 지정할 변수
  UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Input")
  UInputAction* MoveAction;
  
  // IA_Look를 지정할 변수
  UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Input")
  UInputAction* LookAction;
  
  // IA_Jump를 지정할 변수
  UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Input")
  UInputAction* JumpAction;
  
  // IA_Sprint를 지정할 변수
  UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input")
  UInputAction* SprintAction;
};

 

// SpartaPlayerController.cpp
#include "SpartaPlayerController.h"

ASpartaPlayerController::ASpartaPlayerController()
    : InputMappingContext(nullptr),
      MoveAction(nullptr),
      JumpAction(nullptr),
      LookAction(nullptr),
      SprintAction(nullptr)
{
}

 

nullptr 로 초기화하는 이유

  • 블루프린트 상에서 할당을 해줄 것이기 때문에 초기화 
  • 블루프린트에서 값을 할당하지 않은 상태로 게임을 시작하더라도 해당 변수는 안전한 nullptr 값을 가짐

IMC 활성화 코드 작성

// SpartaPlayerController.h
#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();

  UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Input")
  UInputMappingContext* InputMappingContext;
  
  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;

protected:
	// BeginPlay로 IMC를 바로 활성화
	virtual void BeginPlay() override;
};
// SpartaPlayerController.cpp
#include "SpartaPlayerController.h"
// Enhanced Input System의 Local Player Subsystem을 사용하기 위해 포함
#include "EnhancedInputSubsystems.h" 

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 등을 호출하여 입력 매핑을 동적으로 제어할 수 있음

SpartaInputMappingContext

활성화할 IMC

0: 우선순위. 숫자가 낮을수록 높은 우선순위를 가짐


BP_SpartaPlayerController → Details 패널을 확인

 

BP_SpartaPlayerController  → 이벤트 그래프

 

enhanced action events 검색

 

문자열이 출력 테스트

 

게임을 플레이 후 → WASD, Spacebar, 마우스 이동 → 이벤트 노드가 정상적으로 호출되고 문자열 출력