향상된 입력이란?
원래 언리얼에선 Project Settings에서 Engine > Input 탭에서 Mapping을 통해서 입력을 어떻게 받을지 결정하였다.
이후 SetupPlayerInputComponent 함수를 통해서 어떤 상호작용을 줄지 코딩하는 방법을 사용하였다.
하지만, 복잡한 입력 처리나 런타임 제어 리매핑 등, 보다 고급 입력 기능을 쓰는데 귀찮음이 있었고 이를 쓰기 쉽게 언리얼 5에서 새롭게 나온 Input 시스템이다. (개인적으로는 문자열에 의존성을 덜기 위해서이기도 한 것 같다.)
어떻게 보면 쉬운 로직은 더 귀찮아지긴 했지만, 멀리보면 좋은 기능인 거 같다...
※ 5.1 아래 버전의 주의사항
향상된 입력을 사용하기 위해서는 플러그인을 켜주어야 합니다. (5.1 이상은 Default로 켜져 있습니다)
상단 Edit > Plugins를 누르고, Enhanced Input을 켜주고 재시작을 해주세요.
쓰기 전에 알아야 할 핵심 개념
입력 액션(Input Actions)
입력 액션은 특정 사용자 입력(예: 키보드의 키, 마우스 버튼, 게임패드 버튼)에 이름을 붙여, 이를 프로그램 내에서 쉽게 참조하고 관리할 수 있게 해 줍니다. 입력 액션은 데이터 에셋이라는 점만 다를 뿐, 액션 및 축(Axis) 매핑 이름과 개념적으로 같습니다.
- Digital(bool)은 on 또는 off 스테이트를 갖는 입력에는 부울 액션을 사용해야 합니다.
- Axis 1D 는 직진 후진 같은 컨트롤에 사용합니다.
- Axis 2D 은 게임패드 썸스틱 값 같은 컨트롤에 사용합니다.
- Axis 3D 축을 사용하면 모션 컨트롤러 정보 같은 더 복잡한 데이터를 보유할 수 있습니다.
입력 매핑 컨텍스트 (Input Mapping Contexts)
입력 매핑 컨텍스트는 하나 이상의 입력 액션과 이들 액션에 대응하는 키 또는 버튼 매핑을 그룹화합니다. 이를 통해 개발자는 게임의 다양한 상황(예: 캐릭터 제어, UI 탐색)에 맞게 특정 입력 매핑 세트를 동적으로 활성화하거나 비활성화할 수 있습니다.
입력 매핑 컨텍스트는 여러 계층구조로 이루어져 있습니다.
1. 최상위 레벨: 입력 액션 목록
최상위 레벨에는 다양한 입력 액션들이 목록화되어 있습니다. 입력 액션은 특정 사용자 입력에 대한 반응으로 정의된, 게임 내에서 수행되어야 할 동작들을 의미합니다.
예를 들어, "점프", "발사", "달리기" 등이 입력 액션에 해당할 수 있습니다.
2. 중간 레벨: 사용자 입력 목록
각 입력 액션 아래에는 해당 액션을 트리거할 수 있는 구체적인 사용자 입력들의 목록이 있습니다. 이들은 키보드의 키, 마우스 버튼, 게임패드의 버튼, 이동 축 등 다양한 형태의 사용자 입력 장치로부터 올 수 있습니다.
예를 들어, "점프" 액션은 스페이스바나 게임패드의 특정 버튼으로 트리거 될 수 있습니다.
3. 최하위 레벨: 입력 트리거와 입력 모디파이어
입력 트리거는 특정 조건이 충족될 때 입력 액션이 활성화되도록 정의하는 규칙입니다. 예를 들어, 버튼을 길게 누르는 동안에만 액션이 활성화되도록 설정할 수 있습니다.
반면, 입력 모디파이어는 입력 값의 필터링 방식이나 처리 방식을 결정합니다. 이를 통해 개발자는 원시 입력 값을 변형하거나 조정하여, 특정 입력 액션을 구동하기 위한 제한 사항이나 조건을 설정할 수 있습니다.
이후, 향상된 입력 로컬 플레이어 서브시스템(Enhanced Input Local Player Subsystem)을 통해 이러한 컨텍스트를 하나 이상 로컬 플레이어에게 적용할 수 있으며, 컨텍스트의 우선순위를 지정하여 같은 입력을 사용하려는 여러 액션 간의 충돌을 해결할 수 있습니다. (모디파이어 이후에 설명하도록 하겠습니다.)
※ 트리거는 필요하신 분만 보시면 됩니다.
입력 트리거(Input Triggers)와 트리거 스테이트
특정 조건하에서 입력 액션이 활성화되도록 설정하는 규칙
트리거 스테이트
트리거 스테이트(Trigger State)는시작됨(Started), 진행 중(Ongoing), 트리거됨(Triggered), 완료됨(Completed) 및 취소됨(Canceled) 같이 액션의 현재 스테이트를 나타냅니다. ' Triggered ' 스테이트를 자주 사용할 것입니다. C++과 블루프린트 모두에서 특정 스테이트에 바인딩할 수 있습니다.
- Triggered : 액션이 트리거되었습니다. 즉, 모든 트리거 요구 사항 평가를 완료했다는 뜻입니다. 예를 들어, 사용자가 키를 놓으면 '키 누르고 떼기(Press and Release)' 트리거가 전송됩니다.
- Started : 트리거 평가를 시작한 이벤트가 발생했습니다. 예를 들어, '두 번 탭(Double tap)' 트리거를 처음 누르면 '시작됨' 스테이트가 한 번 호출됩니다.
- Ongoing : 트리거를 여전히 처리 중입니다. 예를 들어, 지정된 기간이 끝나기 전에 사용자가 버튼을 누르고 있는 동안에는 '길게 누르기(Press and hold)' 액션이 진행 중입니다. 이 이벤트는 트리거에 따라 입력 값을 수신하면 작업이 평가되는 동안 틱마다 발동됩니다.
- Completed : 트리거 평가 프로세스가 완료되었습니다.
- Canceled : 트리거링이 취소되었습니다. 예를 들어, 사용자가 '길게 누르기' 액션이 트리거되기 전에 버튼을 놓는 경우가 있습니다.
입력 모디파이어(Input Modifiers)
원시 입력 데이터를 변형하거나 조정하는 규칙입니다. 이를 통해 개발자는 입력의 강도나 형식을 조절할 수 있습니다. 예를 들어, 아날로그 스틱의 움직임에 따라 캐릭터의 이동 속도를 조절하거나, 버튼을 더블 클릭해야만 특정 액션이 발동되도록 설정할 수 있습니다.
자주 사용하는 Swizzle Input Axis Values와 Negate만 알아보도록 하겠습니다.
Swizzle Input Axis Values
- 벡터의 요소를 재배열하는 과정을 말합니다.
- 입력받는 하드웨어의 벡터값을 재배열해서 전달해 줍니다. (Order를 아래 사진처럼 변경할 수 있습니다.
Negate
- 입력 값의 부호를 반전시키는 기능입니다. (축을 선택하여 사용할 수 있습니다.)
- 방향성을 가진 입력에 유용합니다. 예를 들어, 사용자가 조이스틱을 앞으로 밀었을 때 뒤로 움직이게 할 때 사용됩니다.
향상된 입력 적용 하는 방법
1. 향상된 입력 컨텍스트 적용 하는 방법
※ 2. 향상된 입력까지 하셔야 움직임이 적용됩니다!
※ 기본적인 Move와 Look을 구현합니다.
공통 과정
원하는 Input Action과 Input Mapping Context를 생성 후 설정해 줍니다. (이름은 바꾸셔도 됩니다.)
Input Action : IA_Move, IA_Look
nput Mapping Context : IMC_Default
Input Action - Value Type을 Axis 2D로 설정해 줍니다.
아래는 Input Mapping Context입니다. (Jump는 직접 구현해 보세요!)
아래는 Input Mapping Context의 Move Input Action, Look Input Action의 설정입니다.
BluePrint로 적용하는 방법(쉬움)
적용할 Pawn의 BluePrint의 Event Graph를 열어줍니다. 그다음, 아래와 같이 노드를 꺼내서 설정해 주면 됩니다.
Add Mapping Context 노드에 Mapping Context(노란 박스)를 원하는 IMC파일로 설정해 주고 Priority를 설정해 줍니다.(클수록 우선 합니다)
C++로 추가하는 방법 (어려움)
프로젝트의 Build.cs 파일을 찾습니다. (프로젝트명 > Source > 프로젝트명 > 프로젝트명 .Build.cs)
11번째 줄을 수정해 줍니다.
// Before
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore" });
// After
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "EnhancedInput" });
그다음 프로젝트가 존재하는 폴더를 열어줍니다.
(프로젝트 폴더에 우클릭 > Copy path , 이후 파일 탐색기에 붙여 넣기 해줍니다.)
이곳에서 Saved, Binaries, Intermediate를 선택하고 삭제합니다. (프로젝트 실행 시 다시 만들어집니다.)
※ 이후 내용 중 C++ 클래스를 참고할 때 프로젝트명을 변경하시고 사용하셔야 됩니다!
이후에 적용할 캐릭터의 C++ 클래스의 BeginPlay에 아래의 코드를 작성해 줍니다. (내용은 BP와 똑같습니다)
단, 아래의 코드에서 쓰이는 UInputMappingContext와 UEnhancedInputLocalPlayerSubsystem는 include나 전방선언을 해주어야 합니다.
1. 헤더 파일
public:
// 매핑 컨텍스트를 헤더 파일에 프로퍼티로 노출합니다...
UPROPERTY(EditAnywhere, Category = "Input")
class TSoftObjectPtr<class UInputMappingContext> DefaultMappingContext;
2. CPP 파일
#include "InputMappingContext.h"
#include "EnhancedInputSubsystems.h"
...
void AEnhancedInputssssssCharacter::BeginPlay()
{
// Call the base class
Super::BeginPlay();
// Add Input Mapping Context
if (APlayerController *PlayerController = Cast<APlayerController>(Controller))
{
if (UEnhancedInputLocalPlayerSubsystem *Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PlayerController->GetLocalPlayer()))
{
if (!DefaultMappingContext.IsNull())
{
UInputMappingContext *MappingContext = DefaultMappingContext.LoadSynchronous();
Subsystem->AddMappingContext(MappingContext, 0 /*Priority*/);
}
}
}
}
코드를 수정했으니 Ctrl + Alt + F11을 눌러 라이브 코딩이나 컴파일을 한번 해주고 프로젝트를 켜서 수정된 Cpp클래스를 상속받는 BP_캐릭터를 찾는다.
상속 받는 BP_캐릭터를 열어서 원하는 IMC를 설정해 준다. (헤더에서 DefaultMappingContext로 이름 지었다)
향상된 입력 컨텍스트 적용이 완료되었다.
향상된 입력 즉, IA는 적용이 안된 상태이니 여기서 멈추면 안 됩니다! 원하는 이동을 만들어 주세요!
2. 향상된 입력 사용하는 방법
BluePrint로 적용하는 방법(쉬움)
적용할 Pawn의 BluePrint의 Event Graph를 열어주고 아래와 같이 만들어 줍니다.
C++로 추가하는 방법 (어려움)
적용할 Pawn의 해더 파일에서 변수를 (전방) 선언해 주고, 바인딩할 함수를 선언해 줍니다.
Cpp 파일에는 필요한 헤더를 include 해주고, SetupPlayerInputComponent함수 안에서 PlayerInputComponent를 캐스팅하여 EnhancedInputComponent 변수를 만들어 줍니다. 이후 EnhancedInputComponent를 통해서 BindAction() 메서드로 함수들을 묶어주면 됩니다.
1. 헤더 파일
#include "InputActionValue.h"
...
private:
/** Jump Input Action */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true"))
class UInputAction* JumpAction;
/** Move Input Action */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true"))
class UInputAction* MoveAction;
/** Look Input Action */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true"))
class UInputAction* LookAction;
protected:
/** Called for movement input */
void Move(const FInputActionValue& Value);
/** Called for looking input */
void Look(const FInputActionValue& Value);
2. CPP 파일
// CPP 파일
#include "EnhancedInputComponent.h"
...
void AEnhancedInputssssssCharacter::SetupPlayerInputComponent(class UInputComponent *PlayerInputComponent)
{
// Set up action bindings
if (UEnhancedInputComponent *EnhancedInputComponent = CastChecked<UEnhancedInputComponent>(PlayerInputComponent))
{
// Jumping
EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Triggered, this, &ACharacter::Jump);
EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Completed, this, &ACharacter::StopJumping);
// Moving
EnhancedInputComponent->BindAction(MoveAction, ETriggerEvent::Triggered, this, &AEnhancedInputssssssCharacter::Move);
// Looking
EnhancedInputComponent->BindAction(LookAction, ETriggerEvent::Triggered, this, &AEnhancedInputssssssCharacter::Look);
}
}
- CastChecked<>() : Cast와 작동은 같지만, 실패 시 nullptr을 반환하지 않고 프로그램을 종료합니다.
- ETriggerEvent::~ : 트리거 구조체이며 언제 작동할지를 나타냅니다.
- Jump의 경우 이미 구현된 ACharacter::Jump를 사용합니다.
위처럼 연결을 해 주었다면, 연결된 함수를 구현하시면 됩니다.
// CPP 파일
void AEnhancedInputssssssCharacter::Move(const FInputActionValue &Value)
{
// input is a Vector2D
FVector2D MovementVector = Value.Get<FVector2D>();
if (Controller != nullptr)
{
// find out which way is forward
const FRotator Rotation = Controller->GetControlRotation();
const FRotator YawRotation(0, Rotation.Yaw, 0);
// get forward vector
const FVector ForwardDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
// get right vector
const FVector RightDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);
// add movement
AddMovementInput(ForwardDirection, MovementVector.Y);
AddMovementInput(RightDirection, MovementVector.X);
}
}
void AEnhancedInputssssssCharacter::Look(const FInputActionValue &Value)
{
// input is a Vector2D
FVector2D LookAxisVector = Value.Get<FVector2D>();
if (Controller != nullptr)
{
// add yaw and pitch input to controller
AddControllerYawInput(LookAxisVector.X);
AddControllerPitchInput(LookAxisVector.Y);
}
}
코드를 수정했으니 Ctrl + Alt + F11을 눌러 라이브 코딩이나 컴파일을 한번 해주고 프로젝트를 켜서 실행하시면 됩니다.
실행 중 향상된 입력 디버깅하기
실행 중에 키보드에서 '~'를 누르고 " showDebug EnhancedInput "을 입력하면 아래와 같이 눌린 버튼과 여러 정보들을 디버깅할 수 있습니다.
이 글이 언리얼 사용자들에게 많은 도움이 되었으면 합니다.
참고 자료
- 언리얼 공식 문서 - 향상된 입력
- Youtube : Druid Mechanics
- 언리얼 ThirdPerson 프로젝트
'Unreal 공부 > UE5 GameDev' 카테고리의 다른 글
[심플 슈터][구버전] 158. 캐릭터 이동 기능 (0) | 2023.12.18 |
---|---|
[UE5] ToonTank (3) (0) | 2023.11.29 |
[UE5] ToonTank (2) (0) | 2023.11.19 |
[UE5] ToonTank (1) (0) | 2023.11.17 |
[UE5][개념편] ToonTank (0) | 2023.11.17 |