본문 바로가기
개발이야기/언리얼 c++

언리얼 C++: EnhancedInput 시스템이란?

by oddsilk 2024. 6. 18.

언리얼 엔진 5의 Enhanced Input 시스템은 기존의 입력 시스템(Input System)을 대체하여 보다 유연하고 강력한 입력 처리를 제공합니다. 이 시스템은 다양한 입력 디바이스를 통합 관리하고, 더 복잡한 입력 매핑과 처리 로직을 지원합니다. 

Enhanced Input의  주요 개념

Input Mapping Contexts:

  • 입력 맵핑 컨텍스트는 여러 입력 액션(Input Actions)을 그룹화하는 역할을 합니다.
  • 특정 상황이나 상태에서 활성화될 수 있으며, 여러 컨텍스트를 겹쳐 사용할 수 있습니다.
  • 예를 들어, 걷기 상태와 운전 상태에 따라 다른 입력 맵핑 컨텍스트를 사용할 수 있습니다.

Input Actions:

  • 입력 액션은 단일 입력 이벤트(키 누르기, 마우스 클릭 등)를 나타냅니다.
  • 디지털 입력(키보드, 버튼)과 아날로그 입력(조이스틱, 트리거) 모두 지원합니다.
  • 트리거 조건(Pressed, Released, Held 등)과 같은 다양한 입력 조건을 설정할 수 있습니다.

Input Modifiers:

  • 입력 값에 변형을 가하는 역할을 합니다.
  • 예를 들어, 축소, 확대, 반전 등의 변형을 적용할 수 있습니다.

Input Triggers:

  • 입력 이벤트가 트리거될 조건을 정의합니다.
  • 예를 들어, 버튼이 눌렸을 때, 버튼이 일정 시간 동안 눌려있을 때 등의 조건을 설정할 수 있습니다.

 

위와같은 개념을 기반으로 언리얼 엔진의 Enhanced Input 시스템은 더 유연하고 강력한 입력 처리를 가능하게 합니다.

이를 통해 개발자는 다양한 입력 장치와 복잡한 입력 시나리오를 보다 쉽게 관리할 수 있습니다.

 

Enhanced Input 시스템은 Input Mapping Context, Input Action, Input Modifiers, Input Triggers와 같은 개념을 통해 입력을 구조화하고, 블루프린트나 C++ 코드에서 이를 활용하여 게임 내 입력 로직을 구현할 수 있습니다.

 

설정 및 사용 방법

콘텐츠 브라우저에서 우클릭을 통해 다음과 같은 에셋들을 생성할 수 있습니다. 

 

Input Mapping Context 생성:

  • 입력 맵핑 컨텍스트를 생성하여, 해당 컨텍스트 내에서 사용할 입력 액션을 정의합니다.
  • 컨텍스트는 주로 에디터의 콘텐츠 브라우저에서 생성할 수 있습니다.

Input Action 생성:

  • 각 입력 액션을 생성하고, 입력 장치 및 입력 유형을 정의합니다.
  • 예를 들어, "MoveForward" 입력 액션을 생성하고, 키보드의 "W" 키와 매핑할 수 있습니다.

Input Mapping Context에 Input Action 추가:

  • 생성한 입력 액션을 입력 맵핑 컨텍스트에 추가합니다.
  • 각 액션에 대해 트리거 조건과 모디파이어를 설정할 수 있습니다.

C++ 또는 블루프린트에서 입력 처리:

  • 생성한 입력 맵핑 컨텍스트와 입력 액션을 사용하여, 캐릭터나 플레이어 컨트롤러 클래스에서 입력 처리를 구현합니다.

 

 

예제: PlayerController에서 입력받기

Input Mapping Context 및 Input Action 생성:

  • 콘텐츠 브라우저에서 "Input Mapping Context"와 "Input Action" 에셋을 생성합니다.
  • 각 Input Action에 대해 키 매핑을 설정합니다.

PlayerController 설정:

  • 블루프린트 또는 C++ 코드에서 플레이어 컨트롤러 클래스를 설정하고, 생성된 입력 맵핑 컨텍스트를 활성화합니다.

Input 이벤트 처리:

  • 플레이어 컨트롤러나 캐릭터 블루프린트에서 입력 이벤트를 처리합니다.
  • 블루프린트에서 Enhanced Input Component를 사용하여 입력 액션에 대한 콜백을 설정합니다.
void AMyPlayerController::SetupInputComponent()
{
    Super::SetupInputComponent();

    if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(GetLocalPlayer()))
    {
        // Add the mapping context
        Subsystem->AddMappingContext(DefaultMappingContext, 0);
    }

    // Bind input actions
    InputComponent->BindAction(MoveAction, ETriggerEvent::Triggered, this, &AMyPlayerController::Move);
}

void AMyPlayerController::Move(const FInputActionValue& Value)
{
    FVector2D MovementVector = Value.Get<FVector2D>();
    // Movement logic here
}

 

 

 

 

 

 

예제: Character클래스 에서 입력받고 InputValue ( (ex)1D float Axis)를 사용하여 처리해보기 

Enhanced Input 시스템에서 1D float Axis 값을 사용하여 애니메이션에 활용하려면, 입력 액션을 BindAction에 바인딩할 때 적절한 파라미터를 전달해야 합니다. 특히, float 값을 전달할 때 BindAction 호출 시에 값이 제대로 전달되는지 확인해야 합니다.

 

EnhancedInputComponent의 BindAction 메서드는 IInputActionInstance를 매개변수로 사용하므로, 이를 활용하여 축 값을 얻을 수 있습니다.

 

 

 

 

먼저 헤더파일에서 해야할 기본적인 것들이 있습니다

 

  • #include "InputActionValue.h"를 추가하여 FInputActionValue를 사용할 수 있도록 합니다.
  • 바인딩을 하기위해 사용할 TriggerGraspAnimRight 함수의 매개변수를 const FInputActionValue&로 변경합니다.

 

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "InputActionValue.h"
#include "VRCharacter.generated.h"

UCLASS()
class MYPROJECT_API AVRCharacter : public ACharacter
{
    GENERATED_BODY()

public:
    AVRCharacter();

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

public:
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
    USkeletalMeshComponent* HandMesh;

    // Function to handle grasp animation
    void TriggerGraspAnimRight(const FInputActionValue& Value);
};

 

 

다음으로, 소스 파일에서 함수 정의를 수정하고 입력 액션을 바인딩합니다. 

  • TriggerGraspAnimRight 함수에서 Value.Get<float>()를 사용하여 float 값을 추출합니다.
  • SetupPlayerInputComponent 함수에서 EnhancedInputComponent를 캐스팅하여 입력 액션을 바인딩합니다.
#include "VRCharacter.h"
#include "EnhancedInputComponent.h"
#include "EnhancedInputSubsystems.h"
#include "VRHandAnimInstance.h"

AVRCharacter::AVRCharacter()
{
    // Set this character to call Tick() every frame.
    PrimaryActorTick.bCanEverTick = true;

    // Initialize the HandMesh component
    HandMesh = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("HandMesh"));
    HandMesh->SetupAttachment(RootComponent);
}

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

    if (UEnhancedInputComponent* EnhancedInputComponent = Cast<UEnhancedInputComponent>(PlayerInputComponent))
    {
        // Bind the input action
        EnhancedInputComponent->BindAction(IAGraspAnimRight, ETriggerEvent::Triggered, this, &AVRCharacter::TriggerGraspAnimRight);
    }
}

void AVRCharacter::TriggerGraspAnimRight(const FInputActionValue& Value)
{
    float InAxisValue = Value.Get<float>(); // Extract the float value from the input action

    UVRHandAnimInstance* HandAnimInstance = Cast<UVRHandAnimInstance>(HandMesh->GetAnimInstance());
    if (HandAnimInstance)
    {
        HandAnimInstance->SetGrapPoseAlpha(InAxisValue);
        UE_LOG(LogTemp, Warning, TEXT("GrapPoseAlpha: %f"), HandAnimInstance->GetGrapPoseAlpha());
    }
}