캐릭터 컨트롤 및 기본 애니메이션 구현하기(1)

주상돈·2025년 1월 23일

TIL

목록 보기
20/53

GameMode 이해하기


1️⃣ GameMode란?

  • GameMode는 게임의 전반적인 규칙과 흐름을 총괄 관리하는, 일종의 컨트롤 타워 역할을 하는 클래스이다.
  • 어떤 Character을 플레이어에게 스폰해줄지, 어떤 PlayerController를 사용할지, 승패 조건이나 점수 계산 방법은 어떻게 설정할지 등 게임 플레이의 핵심 로직을 담당한다.
    • 프로젝트 전역(기본)혹은 레벨별로 필요한 GameMode를 구분해 설정할 수 있다.

2️⃣ GameMode의 주요 기능과 책임

  • 플레이어 캐릭터 - Pawn 클래스 or Character 클래스
  • PlayerController 클래스 - 캐릭터에 빙의
  • 게임 규칙 관리 - 로직(함수)
  • GameState클래스 - 게임 전역 데이터
  • PlayerState클래스 - 개별 캐릭터마다의 데이터

GameMode 클래스 생성하기


1️⃣ GameMode vs GameModeBase

  • GameMode
    • 언리얼에서 제공하는 멀티플레이 기능 (세션, 플레이어 연결 로직 등)을 일부 포함하고 있으며, 싱글 플레이에서도 문제없이 사용할 수 있다.
    • 필요에 따라 GameState, PlayerState 등 연동이 활성화되어 있다
  • GameModeBase
    • 좀 더 단순화된 형태로, 멀티플레이 관련 로직이 거의 포함되지 않는다.
    • 간단한 싱글 플레이 게임 또는 직접 멀티플레이 로직을 구현하고 싶을 때 사용하기도 한다.

2️⃣ C++ 클래스 GameMode 생성

GameMode클래스로 생성하면 cpp,h파일이 각각 생성된다

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/GameMode.h"
#include "SpartaGameMode.generated.h"

UCLASS()
class SPARTAPROJECT_API ASpartaGameMode : public AGameMode
{
	GENERATED_BODY()
};

GameMode.h

#include "SpartaGameMode.h"

GameMode.cpp

3️⃣ GameMode 클래스 적용

  • 직접 만든 C++ GameMode 클래스를 게임에서 활용하려면, 일반적으로 블루프린트 클래스로 한 번 더 감싸는 방식을 권장한다. 이렇게 하면, 에디터에서 세부 파라미터를 수정하기 훨씬 편리하기 때문이다..
  • 언리얼 엔진에서 GameMode를 적용하는 방법은 크게 두 가지이다.
    1. 프로젝트 전역 설정
    2. 레벨 별 설정
  • 만약 프로젝트 전역 설정과 레벨 별 설정이 충돌한다면, 레벨 별 설정 (World Settings)이 우선 적용된다. 즉, 레벨에서 오버라이드된 GameMode가 존재한다면 그 레벨에서는 해당 GameMode를 사용하고, 그렇지 않은 레벨은 전역에 설정된 GameMode를 사용한다.

4️⃣ GameMode 기본 설정 확인

  • Default Pawn Class: 레벨 시작 시 플레이어가 조종하게 될 Pawn (또는 Character) 클래스를 지정한다.
  • HUD Class: 게임 화면에 표시되는 HUD(UI) 요소를 관리하는 클래스를 지정한다.
  • Player Controller Class: 플레이어 입력 처리와 카메라 제어, UI와의 상호작용을 담당하는 클래스를 지정한다.
  • Game State Class / Player State Class: 게임의 전역 상태나 플레이어별 상태를 추적할 때 사용한다.
  • Spectator Class: 관전자 모드에서 사용할 Pawn 클래스를 지정한다. 일반 플레이어와 다른 이동 방식이나 카메라 제어가 필요할 때 활용한다.
    게임을 실행해보면 기본 설정들이 자동 스폰되고 BP_SpartaGameMode (내부적으로 SpartaGameMode)가 GameMode로 동작하는 모습을 볼 수 있다.

Pawn과 Character 클래스 이해하기


1️⃣ Pawn 클래스란?

  • Pawn은 플레이어 혹은 AI가 “소유( Possess )”할 수 있는 가장 상위 클래스이다. 즉, 엔진에서 “무언가를 조종한다”라고 할 때 기본이 되는 형태가 Pawn이 된다.
  • Pawn에는 이동 로직이나 충돌 처리, 중력, 네트워크 이동을 위한 기능들이 기본적으로 포함되어 있지 않다.
    • 보행 (걷기, 달리기, 점프 등)에 필요한 시스템 (캡슐 콜리전, 중력, 지형 따라가기)을 모든 단계에서 직접 구현해야 하므로, 사람 캐릭터를 처음부터 Pawn으로 만들기에는 다소 부담이 크다.
  • 이러한 특성 덕분에, 비행기, 드론, 카메라처럼 기존 Character의 이동 방식을 벗어난 특수한 로직을 완전히 자유롭게 구현할 때 유용하다.

2️⃣ Character 클래스란?

  • Character는 Pawn을 상속받아 만들어진 자식 클래스 중 하나로, 기본적으로 UCharacterMovementComponent를 포함하고 있다.
    • 이동, 회전, 점프, 중력, 지형 따라가기, 네트워크 동기화보행형 캐릭터에게 필요한 기능이 이미 구현되어 있어, 사람이 달리고 점프하는 형태의 캐릭터를 쉽게 만들 수 있다.
    • 여기에 미리 정의된 대표적인 함수들(예: MoveForward, MoveRight, Jump)이 존재하므로, 몇 줄의 코드만 추가해도 금방 캐릭터 움직임을 테스트할 수 있다.
  • 캐릭터를 구성하는 전형적인 요소들이 표준화되어 있어, 일반적인 인간형 캐릭터를 만드는 데 최적화되어 있다.
    - 단, 자동차비행기처럼 완전히 다른 이동 방식을 구현할 때는 Character 내부에 탑재된 기능들이 오히려 방해가 될 수 있다. 이런 경우에는 Pawn을 직접 확장해서 사용하는 것을 고려해야 한다.

4. Character 클래스 생성하기


  • 캐릭터 클래스를 생성하면 Character.h,cpp파일이 생성된다. 아래는 필요없는 글자들을 지운 기본 코드이다.
#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "SpartaCharacter.generated.h"

UCLASS()
class SPARTAPROJECT_API ASpartaCharacter : public ACharacter
{
	GENERATED_BODY()

public:
	ACharacter();

protected:
	virtual void BeginPlay() override;
	virtual void Tick(float DeltaTime) override;
	virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
};

Character.h

#include "Character.h"

ASpartaCharacter::ACharacter()
{
	PrimaryActorTick.bCanEverTick = true;
}

void ACharacter::BeginPlay()
{
	Super::BeginPlay();
}

void ACharacter::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);
}

void ACharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);
}

Character.cpp

2️⃣ Character 클래스 구조 살펴보기

  • 아까 위에서 Pawn과 Character클래스 차이를 보여주기위해 잠시 Character클래스 구조를 보여줬었다. 다시 한번보면서 Character클래스를 보자.
  1. CapsuleComponent (Root Component)
  • 캐릭터가 뭔가 다른 것과 충돌하는 범위를 정의하는 콜리전 컴포넌트이다.
  • 캡슐 형태이며,Radius와 Half Height을 조정해 캐릭터의 물리적 크기 설정 가능

    2. ArrowComponent
  • 캐릭터가 어느 방향을 바라보고 있는지를 표시하기 위해 씬에 화살표를 띄워주는 컴포넌트이다.
  • 게임플레이 로직에는 직접적인 영향을 주지 않고, 주로 편집기에서 시각적 디버깅용으로 사용된다.
    3. SkeletalMeshComponent
  • 캐릭터의 3D 모델과 애니메이션을 적용하는 컴포넌트이다.
  • Skeletal Mesh, Anim Blueprint 등을 여기로 할당해 캐릭터의 외형동작을 제어하다.
    4. CharacterMovementComponent
  • 캐릭터의 이동, 점프, 중력, 네트워크 동기화 등 물리적 이동 로직을 담당하는 핵심 컴포넌트이다.
  • 언리얼에서 제공하는 주요 이동 함수 (MoveForward, MoveRight, Jump)가 이미 연결되어 있어, 최소한의 코드만으로 캐릭터 조작을 구현할 수 있다.

스켈레탈 메시


1️⃣ 스켈레탈 메시 (Skeletal Mesh)

  • Skeletal Mesh란?
    - Skeletal Mesh는 내부에 뼈대 (Skeleton)를 갖춘 3D 모델을 의미한다.
    - 이 뼈대 (본, Bone)가 부모-자식 관계로 연결되어 있으며, 본이 움직이면 외형 (Mesh)도 함께 움직이게 된다.
    - 예: 팔, 다리, 머리 등 신체 부위별 본이 존재.
    - 본과 메시가 연동되기 때문에, 애니메이션 (Bone 움직임)에 맞춰 캐릭터가 뛰거나 걷는 동작을 구현할 수 있다.
    - 언리얼 엔진은 물리 엔진과도 연결할 수 있어, Ragdoll(피격 후 쓰러지는) 효과 등 물리 기반 애니메이션 구현도 쉽게 가능하다.

카메라 및 GameMode 설정하기


1️⃣ SpringArm 및 CameraComponent 추가

  • Character.h를 열고 스프링 암(SpringArm)과 카메라(Camera)관련 클래스를 전방 선언하고 프로퍼티를 추가한다.
#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "SpartaCharacter.generated.h"

class USpringArmComponent; // 스프링 암 관련 클래스 헤더
class UCameraComponent; // 카메라 관련 클래스 전방 선언

UCLASS()
class SPARTAPROJECT_API ASpartaCharacter : public ACharacter
{
		GENERATED_BODY()

public:
		ASpartaCharacter();

protected:
	  // 스프링 암 컴포넌트
	  UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Camera")
	  USpringArmComponent* SpringArmComp;
	  // 카메라 컴포넌트
	  UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Camera")
	  UCameraComponent* CameraComp;
	
		virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override; // 이 함수는 이후에 다루게 되니, 우선 삭제하기 않고 둡니다.
};

Character.h

  • VisibleAnywhere, BlueprintReadOnly: 블루프린트에서 보기만 가능하고, C++ 코드 쪽에서만 수정 가능하게 하는 속성이다.
  • Forward Declaration (전방선언)은 헤더 파일의 의존성을 줄이는 좋은 습관이다. (필요할 때만 #include를 추가)
  • SpringArmComp
    • 스프링 암 컴포넌트(카메라 붐). 캐릭터와 카메라 간의 거리를 유지하고, 충돌 시 카메라가 벽 등에 박히지 않도록 위치를 자동 조정해준다.
  • CameraComp
    • 실제로 화면에 표시되는 카메라 컴포넌트입니다. 위치와 회전을 제어하면 게임 뷰가 변경된다.
#include "SpartaCharacter.h"
// 카메라, 스프링 암 실제 구현이 필요한 경우라서 include
#include "Camera/CameraComponent.h"
#include "GameFramework/SpringArmComponent.h"

ASpartaCharacter::ASpartaCharacter()
{
		// Tick 함수는 우선 꺼둡니다.
		PrimaryActorTick.bCanEverTick = false;
		
		// (1) 스프링 암 생성
    SpringArmComp = CreateDefaultSubobject<USpringArmComponent>(TEXT("SpringArm"));
    // 스프링 암을 루트 컴포넌트 (CapsuleComponent)에 부착
    SpringArmComp->SetupAttachment(RootComponent);
    // 캐릭터와 카메라 사이의 거리 기본값 300으로 설정
    SpringArmComp->TargetArmLength = 300.0f;  
    // 컨트롤러 회전에 따라 스프링 암도 회전하도록 설정
    SpringArmComp->bUsePawnControlRotation = true;  

    // (2) 카메라 컴포넌트 생성
    CameraComp = CreateDefaultSubobject<UCameraComponent>(TEXT("Camera"));
    // 스프링 암의 소켓 위치에 카메라를 부착
    CameraComp->SetupAttachment(SpringArmComp, USpringArmComponent::SocketName);
    // 카메라는 스프링 암의 회전을 따르므로 PawnControlRotation은 꺼둠
    CameraComp->bUsePawnControlRotation = false;
}

void ASpartaCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
		Super::SetupPlayerInputComponent(PlayerInputComponent);
}

Character.h

  • SpringArmComp->bUsePawnControlRotation = true

    • 플레이어가 마우스를 움직이면 Controller의 회전 값이 변경되고, 이때 스프링 암도 같이 회전하도록 설정한다.
    • 결과적으로 카메라도 따라 회전하여 3인칭 시점을 자연스럽게 구현할 수 있다.
  • CameraComp->bUsePawnControlRotation = false
    - 이미 스프링 암이 회전을 처리하므로, 카메라 자체는 PawnControlRotation을 사용하지 않도록 한다.
    그 후 빌드후 언리얼 에디터로 돌아오면

    SpringArmCompCameraComp가 추가된 것을 확인할 수 있다.

  • 그 후 카메라를 잘 보기좋게 조절하자.

  • 자기가 설정한 Character나 Pawn이 스폰될 첫 위치를 정해주는 PlayerStart이다.

2️⃣ GameMode에서 DefaultPawnClass 설정

  • 이렇게 디폴트 폰 클래스를 내가 만든 Character클래스로 설정해주는 방법하나가 있다.
    또 다른방법으로는 GameMode.h와 cpp파일을 수정해서 바꿀 수도 있다.
#pragma once

#include "CoreMinimal.h"
#include "GameFramework/GameMode.h"
#include "SpartaGameMode.generated.h"

UCLASS()
class SPARTAPROJECT_API ASpartaGameMode : public AGameMode
{
    GENERATED_BODY()

public:
		ASpartaGameMode();
}

GameMode.h

#include "SpartaGameMode.h"
#include "SpartaCharacter.h"

ASpartaGameMode::ASpartaGameMode()
{
    DefaultPawnClass = ASpartaCharacter::StaticClass();
}

GameMode.cpp

  • 이렇게 코드로도 DefaultPawnClass를 설정할 수 있다.

3️⃣ 캐릭터 구현 테스트

  • 잘 생성되는 것을 확인할 수 있다. 이제 키보드와 마우스입력을 받을 수 있게 해보자.

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


1️⃣ PlayerController란?

  • PlayerController는 사용자가 키보드, 마우스, 게임패드 등에서 입력을 받으면, 그 입력을 해석하여 캐릭터나 다른 오브젝트에게 동작을 명령하는 핵심 클래스입니다.
  • 언리얼 엔진의 중요한 철학 중 하나는 “플레이어 입력은 PlayerController에서 처리한다”는 것입니다. 이를 통해 입력 처리 로직과 실제 캐릭터의 동작 로직을 분리할 수 있어, 코드를 구조적으로 관리하기가 훨씬 수월해집니다.
  • 입력이 처리되는 기본 흐름
    1. 키보드, 마우스, 게임패드 등 입력 장치로부터 사용자 조작 신호가 들어옵니다.
    2. 이 신호는 PlayerController가 받아서 해석합니다.
    3. PlayerController가 현재 소유 (Possess)하고 있는 Pawn에게 이동, 회전, 공격 등의 구체적인 명령을 내립니다.
  • 특히 멀티플레이 환경에서는, 각 플레이어마다 개별 PlayerController가 생성되므로, 여러 사용자의 입력을 충돌 없이 분리하고 관리할 수 있습니다.

2️⃣ 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을 소유함으로써 서로 다른 캐릭터 조작이 가능하다.

PlayerController C++ 클래스 생성하기


  • PlayerController 클래스를 선택하여 만들면 PlayerController.cppPlayerController.h 파일이 생성된다. 코드는 아래와 같다.
#pragma once

#include "CoreMinimal.h"
#include "GameFramework/PlayerController.h"
#include "SpartaPlayerController.generated.h"

UCLASS()
class SPARTAPROJECT_API ASpartaPlayerController : public APlayerController
{
	GENERATED_BODY()
};

PlayerController.h

#include "SpartaPlayerController.h"

PlayerController.cpp

2️⃣ PlayerController를 GameMode에 적용

  • 새로만든 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();
};

GameMode.h

#include "SpartaGameMode.h"
#include "SpartaCharacter.h"
#include "SpartaPlayerController.h" // PlayerController 클래스를 사용

ASpartaGameMode::ASpartaGameMode()
{
		DefaultPawnClass = ASpartaCharacter::StaticClass();
    PlayerControllerClass = ASpartaPlayerController::StaticClass();
}

GameMode.cpp

  • PlayerControllerClass는 GameMode가 제공하는 속성으로, 게임 시작 시 사용할 PlayerController 타입을 지정한다.
  • ASpartaPlayerController::StaticClass()는 언리얼 엔진이 ASpartaPlayerController 클래스의 정보를 런타임에 참조할 수 있도록 제공하는 정적 함수이다.
  • 위 코드를 저장 후 프로젝트 빌드를 완료하면, 언리얼 에디터에서 GameMode가 SpartaPlayerController를 기본 PlayerController로 인식하게 된다.

3️⃣ PlayerController 동작 확인

Enhanced Input System의 이해

1️⃣ Enhanced Input System란?

  • 언리얼 엔진 5에는 이전 버전(UE4 등)에서 사용하던 전역 Project Settings → Input 시스템을 대체하거나 확장하기 위해 Enhanced Input 시스템이 제공된다.
  • Enhanced Input은 입력 설정을 “입력 맵(Input Mapping Context, IMC)”과 “입력 액션(Input Action, IA)”이라는 개념으로 나누어 관리한다.

2️⃣ Input Action (IA)

  • Input Action (IA)
    - Input Action은 캐릭터의 이동, 점프, 발사, 줌 등과 같이 특정 동작을 추상화한 단위이다.
    - 예를 들어 WASD 이동을 담당하는 IA_Move, 스페이스바 점프를 담당하는 IA_Jump, 마우스 회전을 담당하는 IA_Look 등을 만들 수 있다.
  • 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축 제어
  • 트리거 (Trigger)는 입력이 활성화되는 특정 조건을 말한다.
    • Pressed Trigger: 키를 누르는 순간에만 작동.
    • Hold Trigger: 키를 일정 시간 눌렀을 때 작동.
    • Released Trigger: 키를 뗄 때 작동.
  • 모디파이어 (Modifier)는 입력 값을 수정하거나 변환하기 위한 설정이다.
    • Scale : 입력 값에 일정 배율을 곱해줌 (마우스 이동 속도 2배)
    • Invert : 입력 값을 반전 (상하 반전 카메라)
    • Deadzone : 일정 임계값보다 작은 입력은 무시 (게임패드 조이스틱 미세 떨림 방지)

Input Mapping Context


1️⃣ Input Mapping Context (IMC) 생성

  • Input Mapping Context (IMC)
    • IMC는 여러 개의 IA들을 한데 모아놓은 매핑 설정 파일이다.
    • 예를 들어, “플레이어 기본 이동, 점프, 시점 전환”을 하나의 IMC에 넣고, “UI 전용 입력”을 다른 IMC로 분리하는 식으로 관리할 수 있다.
    • 게임 진행 중 특정 상황에서 IMC를 활성 (Enable)하거나 비활성 (Disable)하여 입력을 제어할 수도 있다.

2️⃣ Input Mapping Context (IMC) 매핑

  • IA_Move 는 아래와 같이 키를 매핑해준다.

    • 이동 액션(IA_Move)은 W, A, S, D 키를 각각 축 값으로 매핑한다.
    • Swizzle Input Axis Values란?
      • Swizzle Input Axis Values는 입력 축 (Axis)을 변환하거나 재구성하는 기능이다. 언리얼 엔진의 입력 시스템에서는 입력 데이터를 X, Y, Z 축 중 하나로 매핑한다. Swizzle은 이 입력 값이 올바른 축에 맞지 않을 경우, 특정 축으로 재배치하거나 변환할 수 있게 도와준다.
    • 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 방향에 매핑되므로 추가 변환이 필요 없다.
  • IA_Jump 는 아래와 같이 키 설정을 지정해줍니다

    • 점프는 스페이스바를 눌렀을 때 On/Off 형태로 동작한다.
    • 별도의 트리거나 모디파이어가 필요 없으므로, 기본 상태로 둔다.
  • IA_Look 은 아래와 같이 마우스 설정을 지정해줍니다.

    • 마우스 움직임을 통해 Yaw (좌우 회전)과 Pitch (상하 회전) 값을 동시에 전달받습니다.
  • Axis2D로 지정한 X축, Y축 값을 게임 내 좌표계에 맞춰 회전에 반영한다.

  • 기본적으로 마우스 Y축의 움직임은 "위로 움직임 = 양수, 아래로 움직임 = 음수"로 전달된다.

  • 하지만, 카메라의 상하 회전 (Pitch)은 엔진의 좌표계에서 "위로 = 음수, 아래로 = 양수"로 작동하는 경우가 많습니다. 따라서, Y축 값을 Negate Modifier를 통해 반전하여야 한다.

    • 위로 움직임 → 음수(-) (카메라 위로).
    • 아래로 움직임 → 양수(+) (카메라 아래로).
  • X축 (좌우 회전, Yaw)의 경우, 기본적인 엔진 좌표계와 마우스 움직임의 방향이 일치한다.

    • 오른쪽 이동 → 양수(+).
    • 왼쪽 이동 → 음수(-).
  • IA_Sprint 는 아래와 같이 키 설정을 지정해준다.

    • 스프린트는 왼쪽 Shift 키를 눌렀을 때 On/Off 형태로 동작한다.
    • Key를 “Left Shift”로 지정하고 트리거나 모디파이어는 기본 상태로 두고 다시 저장을 한다.

PlayerController에서 IMC 활성화하기


#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_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;
};

PlayerController.h

#include "SpartaPlayerController.h"

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

PlayerController.cpp
UPROPERTY() 이렇게 선언해두면, 언리얼 에디터나 Blueprint에서 해당 멤버 변수에 .uasset 파일 (우리가 만든 IMC, IA 등)을 할당해줄 수 있다.

2️⃣ IMC 활성화 코드 작성

  • 언리얼 5의 Enhanced Input SystemLocal 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)
#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;
};

PlayerController.h

#include "SpartaPlayerController.h"
#include "EnhancedInputSubsystems.h"

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

}

void ASpartaPlayerController::BeginPlay()
{
	Super::BeginPlay();

	//LocalPlayer는 그 플레이어의 입력이나 화면뷰를 관리하는 객체이다. -> 입력을 관리하는 객체이다.(플레이어마다 붙어있음)
	if (ULocalPlayer* LocalPlayer = GetLocalPlayer())
	{
		// Local Player에서 EnhancedInputLocalPlayerSubsystem을 획득
		if (UEnhancedInputLocalPlayerSubsystem* Subsystem =
			LocalPlayer->GetSubsystem<UEnhancedInputLocalPlayerSubsystem>())
		{
			if (InputMappingContext)
			{
				// Subsystem을 통해 우리가 할당한 IMC를 활성화
				// 우선순위(Priority)는 0이 가장 높은 우선순위
				Subsystem->AddMappingContext(InputMappingContext, 0);
			}
		}
	}
	
}

PlayerController.cpp

  • GetLocalPlayer()
    • 현재 PlayerController가 관리하는 Local Player를 반환한다.
  • GetSubsystem<UEnhancedInputLocalPlayerSubsystem>()
    • 해당 Local Player에 부착된 Enhanced Input Subsystem을 반환한다.
    • 이를 통해 AddMappingContextRemoveMappingContext 등을 호출하여 입력 매핑을 동적으로 제어할 수 있다.
  • AddMappingContext()
    • 주어진 IMC를 Subsystem에 추가하여 입력 매핑을 활성화한다.
    • SpartaInputMappingContext: 활성화할 IMC.
    • 0: 우선순위. 낮을수록 높은 우선순위를 가진다.
    • 이 함수를 여러 번 호출해 여러 IMC를 활성화할 수도 있다.
      • 우선순위를 달리 부여해, 특정 IMC가 다른 IMC보다 우선순위가 높도록 설정할 수도 있다.

3️⃣ IMC 적용 확인 및 테스트

  • 그 후 잘 연결되었는지 확인하기위해 BP_PlayerController Blueprint를 열어 이벤트 그래프창에서

    와 같이 만들어주자.
  • 그 후 실행을해서 확인을해보면 마우스를 돌리거나 w, a, s, d 키를 누르거나 space를 누르거나 shift키를 누르면 각각에 맞는 이벤트가 발생함을 알 수 있다.

0개의 댓글