Unreal Engine - Enhanced Input System (PlayerController)

2025. 1. 22. 19:05·Unreal Engine

1. PlayerController


PlayerController란

  • 사용자가 키보드, 마우스, 게임 패드 등에서 입력을 받으면 해당 입력을 해석해서 캐릭터나 다른 오브젝트에게 동작을 명령하는 클래스 이다.
  • 이전에 언급한 GameMode에서 지정해줘야할 한 가지 클래스이며,
  • 언리얼 엔진의 철학인 "플레이어의 입력은 PlayerController에서 처리하고, 실제 움직임은 Character가 빙의해서 처리한다" 를 지키기 위해서 존재하며,
  • 코드를 구조적으로 관리하기도 쉬워진다.

PlayerController의 주요 기능

  • 입력 처리
    • 다양한 입력장치(키보드, 마우스 등)의 이벤트 처리
    • 언리얼5에서는 Enhanced Input System을 통해 더욱 개선된 방식으로 처리 가능
    • C++에서는 SetInputComponent() 함수를 오버라이드해서 구현한다.
  • 카메라 제어 로직
    • 캐릭터의 시점 회전이나 줌인, 줌 아웃 등 카메라 동작을 수행
  • HUD및 UI와 상호작용
    • 버튼 클릭, 드래그, 터치 등 이벤트를 받아서 처리할 수 있다.
    • 특정 명령을 UI에서 트리거 시킨 후, PlayerController가 이를 GameMode나 Pawn으로 전달할 수 있다.
  • Possess / Unpossess
    • PlayerController는 특정 Pawn에 Possess 되어서 해당 Pawn을 제어
    • Unpossess() 함수를 통해서 Pawn과의 연결을 해제할 수도 있다.

PlayerController 등록 (C++)

  • 아래와 같은 코드를 통해서 GameMode에 등록시킬 수 있다.
  • StaticClass()는 클래스의 정보를 런타임에 참조할 수 있도록 제공되는 함수이다.
#include "SpartaGameMode.h"
#include "SpartaCharacter.h"
#include "SpartaPlayerController.h"

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

 

 

 

2. Enhanced Input System


Enhanced Input System이란

  • 언리얼5에서 제공하는 input 관리 시스템
  • Input Mapping Context와 Input Action으로 나눠진다.

Input Action (IA)

  • 캐릭터의 특정 동작을 추상화 하는 단위이다. (움직임, 점프, 공격 등)
  • IA에는 다양한 설정이 존재한다. 기본적인 설정들을 살펴보면 다음과 같다
  • Value Type은 기본적으로 설정하지만, 나머지는 좀 더 디테일한 구현을 할 때 사용한다.
    • Value Type -> 어떤 유형의 값을 제공할지
      • Bool : 단순 on/off 입력 (점프, 공격 등)
      • Axis1D : -1 ~ 1 범위의 입력 (전진 후진, 자동차 시뮬레이션 등)
      • Axis2D : x,y 축을 처리 (캐릭터의 이동, 마우스 이동)
      • Axis3D : x,y,z축을 동시에 처리 (비행 시뮬레이션)
    • Trigger -> 언제 입력이 활성화 될지
      • Pressed : 키를 누르는 순간에만 작동
      • Hold : 키를 일정시간 눌렀을 때 작동
      • Released : 키를 뗄 때 작동
    • Modifier -> 입력값을 수정하거나 변환
      • Scale : 입력 값에 일정 배율 곱하기
      • Invert : 입력 값을 반전
      • DeadZone : 임계값보다 작은 값을 무시 (게임 패드)
  • 세팅한 결과
    • IA_Move와 IA_Look은 Axis2D로 설정
    • IA_Jump와 IA_Sprint는 bool로 설정
    • 이번 구현에 있어서는 Value Type만 설정하였다.

InputMappingContext (IMC)

  • IMC는 여러개의 IA를 총괄해서 관리하는 파일이다.
  • IMC 매핑 중 Modifier 설정
    • 스위즐 입력 축 값
      • 입력축을 변환하여, 재구성하는 기능 (이때 X, Y, Z의 우선순위를 선택한다)
    • 부정
      • 입력축 값을 반대로 바꿔주는 기능

에디터 세팅

  • 주의점
    • x축이 앞으로 가는(W) 방향이므로, 정렬을 "XZY"로 바꿔주기
    • Look 에서 마우스를 위로 올리는 것과 카메라의 동작은 반대이므로, "부정" 모디파이어 적용하기

 

C++에서 설정

  • PlayerController 헤더파일
#pragma once

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

class UInputMappingContext;
class UInputAction;

UCLASS()
class SCC_PROJECT_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;

protected:
	virtual void BeginPlay() override;
};
  • PlayerController cpp파일
    • 사용자 (Local Player)를 불러온 후
    • 사용자의 Local Player Subsystem을 가져와서 Input Mapping Context를 등록하는 로직
#include "SpartaPlayerController.h"
#include "EnhancedInputSubsystems.h"

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

}

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

	if(ULocalPlayer * LocalPlayer = GetLocalPlayer())
	{
		if (UEnhancedInputLocalPlayerSubsystem* Subsystem 
			= LocalPlayer->GetSubsystem<UEnhancedInputLocalPlayerSubsystem>())
		{
			if (InputMappingContext)
			{
				Subsystem->AddMappingContext(InputMappingContext, 0);
			}
		}
	}
}

 

 

 

3. 실제로 캐릭터 움직이기


Character 클래스에 input action 연결의 개념

  • 캐릭터 클래스의 Input Action 동작의 흐름 
    • PlayerController는 키 입력을 감지한 후,
    • Local Player Subsystem에게 해당 키값이 무슨 뜻인지 물어본다.
    • Local Player Subsystem이 해당 키 값이 무슨 동작인지 알려준다면
    • PlayerController가 Character에게 해당 키 값에 맞는 함수를 호출하도록 명령한다.
    • 마지막으로 Character가 해당 함수를 실행
  • 지금까지 구현한 내용
    • PlayerController를 만든 후 
    • Local Player Subsystem에게 우리가 어떤 IMC를 쓸 것인지 알려주었다.
  • 그렇다면, 다음으로 할 것
    • PlayerController가 어떠한 함수를 호출할 지 바인딩 해주어야 한다!
    • BindAction() 함수를 사용하여, PlayerController와 바인딩을 해주어야 한다.

Character 클래스에 액션 바인딩 추가

  • Character.h 
    • 주의할 점
      • FInputActionValue는 크기가 큰 구조체이므로 각 함수의 인자로 전달할 때, 레퍼런스로 전달해서 복사하지 않도록 하기
      • UFUNCTION을 붙여서 리플렉션 시스템에 등록해주기
      • Jump와 Sprint와 같은 함수는 Start와 Stop으로 나눠서 구현해주는 것이 좋다.
#pragma once

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

class USpringArmComponent;
class UCameraComponent;
struct FInputActionValue;

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

public:
	ASpartaCharacter();

protected:
	virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;

	UFUNCTION()
	void Move(const FInputActionValue& value);
	UFUNCTION()
	void StartJump(const FInputActionValue& value);
	UFUNCTION()
	void StopJump(const FInputActionValue& value);
	UFUNCTION()
	void Look(const FInputActionValue& value);
	UFUNCTION()
	void StartSprint(const FInputActionValue& value);
	UFUNCTION()
	void StopSprint(const FInputActionValue& value);

public:
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Camera")
	USpringArmComponent* SpringArmComp;

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Camera")
	UCameraComponent* CameraComp;

private:
	float NormalSpeed;
	float SprintSpeedMultiplier;
	float SprintSpeed;
};
  • Character.cpp 파일
    • 액션 바인딩을 하는 코드와 각 액션에 해당하는 코드 추가
    • 액션 함수
      • AddMovementInput, Jump, AddControllerYawInput 등 이미 존재하는 함수를 사용했다.
      • 이러한 함수를 쓸 수 있기에 우리가 Character 클래스를 통해 캐릭터를 만드는 것이다.
    • 주의할 점
      • Move 액션 함수에서 Controller의 유효성 체크를 하여, GetActorForwardVector와 같은 함수를 쓸 때 문제가 없도록 해야 한다.
      • Jump나 Sprint의 액션 함수에서는 유효성 체크를 할 필요는 없다. 
        • 그 이유는 위에서 말한 GetActorForwardVector와 같이 Actor의 값을 가져올 때 유효성 체크를 해야 하지만,
        • Jump나 Sprint의 경우 그러한 로직이 없고, 이미 구현된 함수인 Jump()나 StopJumping() 등에서 이미 유효성 체크를 해주기 때문이다.
#include "SpartaCharacter.h"
#include "SpartaPlayerController.h"
#include "EnhancedInputComponent.h"
#include "Camera/CameraComponent.h"
#include "GameFramework/SpringArmComponent.h"
#include "GameFramework/CharacterMovementComponent.h"

ASpartaCharacter::ASpartaCharacter()
	:NormalSpeed(600.f)
	,SprintSpeedMultiplier(1.5f)
	,SprintSpeed(NormalSpeed * SprintSpeedMultiplier)
{
	PrimaryActorTick.bCanEverTick = false;

	SpringArmComp = CreateDefaultSubobject<USpringArmComponent>(TEXT("SpringArm"));
	SpringArmComp->SetupAttachment(RootComponent);
	SpringArmComp->TargetArmLength = 300.f;
	SpringArmComp->bUsePawnControlRotation = true; //카메라 회전 세팅

	CameraComp = CreateDefaultSubobject<UCameraComponent>(TEXT("Camera"));
	CameraComp->SetupAttachment(SpringArmComp, USpringArmComponent::SocketName); //끝부분에 붙이기
	CameraComp->bUsePawnControlRotation = false;

	GetCharacterMovement()->MaxWalkSpeed = NormalSpeed;
}


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

	if (UEnhancedInputComponent* EnhancedInput = Cast<UEnhancedInputComponent>(PlayerInputComponent))
	{
		if (ASpartaPlayerController* PlayerController = Cast<ASpartaPlayerController>(GetController()))
		{
			if (PlayerController->MoveAction)
			{
				EnhancedInput->BindAction(
					PlayerController->MoveAction
					, ETriggerEvent::Triggered
					, this
					, &ASpartaCharacter::Move);
			}

			if (PlayerController->JumpAction)
			{
				EnhancedInput->BindAction(
					PlayerController->JumpAction
					, ETriggerEvent::Triggered
					, this
					, &ASpartaCharacter::StartJump);

				EnhancedInput->BindAction(
					PlayerController->JumpAction
					, ETriggerEvent::Completed
					, this
					, &ASpartaCharacter::StopJump);
			}

			if (PlayerController->LookAction)
			{
				EnhancedInput->BindAction(
					PlayerController->LookAction
					, ETriggerEvent::Triggered
					, this
					, &ASpartaCharacter::Look);
			}

			if (PlayerController->SprintAction)
			{
				EnhancedInput->BindAction(
					PlayerController->SprintAction
					, ETriggerEvent::Triggered
					, this
					, &ASpartaCharacter::StartSprint);

				EnhancedInput->BindAction(
					PlayerController->SprintAction
					, ETriggerEvent::Completed
					, this
					, &ASpartaCharacter::StopSprint);
			}
		}
	}
}

void ASpartaCharacter::Move(const FInputActionValue& value)
{
	if (!Controller)
		return;

	const FVector2D MoveInput = value.Get<FVector2D>();

	if (!FMath::IsNearlyZero(MoveInput.X))
	{
		AddMovementInput(GetActorForwardVector(), MoveInput.X);
	}

	if (!FMath::IsNearlyZero(MoveInput.Y))
	{
		AddMovementInput(GetActorRightVector(), MoveInput.Y);
	}
}

void ASpartaCharacter::StartJump(const FInputActionValue& value)
{
	if (value.Get<bool>())
	{
		Jump();
	}
}

void ASpartaCharacter::StopJump(const FInputActionValue& value)
{
	if (!value.Get<bool>())
	{
		StopJumping();
	}
}

void ASpartaCharacter::Look(const FInputActionValue& value)
{
	FVector2D LookInput = value.Get<FVector2D>();

	AddControllerYawInput(LookInput.X);
	AddControllerPitchInput(LookInput.Y);
}

void ASpartaCharacter::StartSprint(const FInputActionValue& value)
{
	if (GetCharacterMovement())
	{
		GetCharacterMovement()->MaxWalkSpeed = SprintSpeed;
	}
}

void ASpartaCharacter::StopSprint(const FInputActionValue& value)
{
	if (GetCharacterMovement())
	{
		GetCharacterMovement()->MaxWalkSpeed = NormalSpeed;
	}
}

'Unreal Engine' 카테고리의 다른 글

UE5 Issues : Additive Animation (animation sequence 색깔)  (0) 2025.01.26
Unreal Engine - 캐릭터 만들기  (0) 2025.01.24
Unreal Engine - GameMode, Pawn, Character  (0) 2025.01.22
Unreal Engine - 클래스와 변수의 리플렉션 (매크로 지정자 위주)  (0) 2025.01.22
Unreal Engine - Actor의 생성과 응용  (0) 2025.01.21
'Unreal Engine' 카테고리의 다른 글
  • UE5 Issues : Additive Animation (animation sequence 색깔)
  • Unreal Engine - 캐릭터 만들기
  • Unreal Engine - GameMode, Pawn, Character
  • Unreal Engine - 클래스와 변수의 리플렉션 (매크로 지정자 위주)
gbleem
gbleem
gbleem 님의 블로그 입니다.
  • gbleem
    gbleem 님의 블로그
    gbleem
  • 전체
    오늘
    어제
    • 분류 전체보기 (184)
      • Unreal Engine (73)
      • C++ (19)
      • 알고리즘(코딩테스트) (27)
      • TIL (60)
      • CS (4)
      • 툴 (1)
  • 블로그 메뉴

    • 홈
    • 카테고리
  • 링크

    • 과제용 깃허브
    • 깃허브
    • velog
  • 공지사항

  • 인기 글

  • 태그

    BFS
    템플릿
    gamestate
    매크로 지정자
    C++
    addonscreendebugmessage
    map을 vector로 복사
    싱글턴
    cin함수
    character animation
    blend pose
    motion matching
    Vector
    const
    actor 클래스
    DP
    additive animation
    상속
    enhanced input system
    applydamage
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
gbleem
Unreal Engine - Enhanced Input System (PlayerController)
상단으로

티스토리툴바