UE5

[UE5] Enhanced Input System

Honey Badger 2023. 7. 7. 18:12

 

 

 

 

언리얼의 Input 시스템

 

Unreal Input System FlowChart

 

1. 유저가 하드웨어(키보드, 마우스, 패드 등)을 동작시킴.

2. 유저의 인풋과 데이터를 매핑.

 

3. 인풋 컴포넌트가 제일먼저 인풋 처리가 가능한 액터 확인.

4. 그다음 PlayerController 확인. 

5. 레벨 블루프린트 확인.

6. 플레이어가 빙의한 Pawn 확인

 

7. 위의 3~6 과정을 통해 처리된 인풋을 게임 로직에 반영.

 

 

 

 

 

 

입력을 어디서 처리할 것인가?

 

PlayerInput값은 위에서 설명했던 것처럼 크게 4가지 과정에서 처리 가능합니다.

 

1. Input 처리가 가능한 Actor

2. PlayerController

3. Level Blueprint

4. Player가 빙의한 Pawn

 

   기존 언리얼 프로젝트 작업들은 모두 PlayerController에서 입력을 처리해주었는데, 선배님 말씀에 의하면 Pawn에서 처리하는 것이 가장 바람직하다고 합니다. 그 이유는 Pawn이 여러명이 될 수 있는 경우에도 유연하게 대처할 수 있고, 코드가 분산되기 때문에 각각 Pawn의 입력이나 동작을 개별적으로 정의할 수 있어 확장성이 높아지기 때문입니다. 

 

 

 

 

 

 

Enhanced Input 이란?

 

   언리얼4와 다르게 5버전에는 Enhanced Input(향상된 입력 시스템) 이라는 시스템이 정식 도입되었는데, 유저의 Input 설정 변경에 유연하게 대처할 수 있고, 각 설정을 독립적인 에셋으로 만드는 시스템입니다. 오늘은 이를 활용해 입력 시스템의 초안을 작성했습니다. 

 

 

 

  먼저 Enhanced Input의 동작은 네 단계로 세분화되는데 내용은 다음과 같습니다.

  두번째 와 세번째 단계에 있는 입력 매핑 컨텍스트(Input Mapping Context)와 액션(Input Action)을 묶어서 하나의 에셋으로 만들 수 있습니다. 

 

입력 매핑 컨텍스트(Input Mapping Context)
액션(Input Action)

 

 

 

 

 

예제 

 

    코드 작성에 앞서, Enhanced Input 모듈을 추가한다. 

 

 

 

<플레이어 코드>

// Fill out your copyright notice in the Description page of Project Settings.


#include "Character/ABCharacterPlayer.h"
#include "GameFramework/SpringArmComponent.h"
#include "Camera/CameraComponent.h"
#include "EnhancedInputComponent.h"
#include "EnhancedInputSubsystems.h"

AABCharacterPlayer::AABCharacterPlayer()
{
	// Camera
	CameraBoom = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraBoom"));
	CameraBoom->SetupAttachment(RootComponent);
	CameraBoom->TargetArmLength = 400.0f;
	CameraBoom->bUsePawnControlRotation = true;

	Camera = CreateDefaultSubobject<UCameraComponent>(TEXT("Camera"));
	Camera->SetupAttachment(CameraBoom, USpringArmComponent::SocketName);
	Camera->bUsePawnControlRotation = false;

	//Input
	static ConstructorHelpers::FObjectFinder<UInputMappingContext> InputMappingContextRef(TEXT(""));
	if (InputMappingContextRef.Object)
	{
		DefaultMappingContext = InputMappingContextRef.Object;
	}


	static ConstructorHelpers::FObjectFinder<UInputAction> InputActionJumpRef(TEXT(""));
	if (InputActionJumpRef.Object)
	{
		JumpAction = InputActionJumpRef.Object;
	}

	static ConstructorHelpers::FObjectFinder<UInputAction> InputActionMoveRef(TEXT(""));
	if (InputActionMoveRef.Object)
	{
		MoveAction = InputActionMoveRef.Object;
	}

	static ConstructorHelpers::FObjectFinder<UInputAction> InputActionLookRef(TEXT(""));
	if (InputActionLookRef.Object)
	{
		LookAction = InputActionLookRef.Object;
	}

}

void AABCharacterPlayer::Move(const FInputActionValue& Value)
{
	//템플릿이라 꺽쇠구나! 주말동안 코드 분석.
	FVector2D MovementVector = Value.Get<FVector2D>();

	const FRotator Rotation = Controller->GetControlRotation();
	const FRotator YawRotation(0, Rotation.Yaw, 0);

	const FVector ForwardDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
	const FVector RightDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);

	AddMovementInput(ForwardDirection, MovementVector.Y);
	AddMovementInput(RightDirection, MovementVector.X);
}

void AABCharacterPlayer::Look(const FInputActionValue& Value)
{
	FVector2D LookAxisVector = Value.Get<FVector2D>();

	AddControllerYawInput(LookAxisVector.X);
	AddControllerYawInput(LookAxisVector.Y);
}

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

	//InputAction 과 InputMappingContext를 연결.
	UEnhancedInputComponent* EnhancedInputComponent = CastChecked<UEnhancedInputComponent>(PlayerInputComponent);

	//Jump
	EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Triggered, this, &ACharacter::Jump);
	EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Completed, this, &ACharacter::StopJumping);

	//Move
	EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Triggered, this, &AABCharacterPlayer::Move);

	//Look
	EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Triggered, this, &AABCharacterPlayer::Look);
}

 

 

<플레이어 헤더>

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "Character/ABCharacterBase.h"
#include "InputActionValue.h"
#include "ABCharacterPlayer.generated.h"

/**
 * 
 */
UCLASS()
class ARENABATTLE_API AABCharacterPlayer : public AABCharacterBase
{
	GENERATED_BODY()
	
public:
	AABCharacterPlayer();
	
protected:
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, Meta = (AllowPrivateAccess = "true"))
	TObjectPtr<class USpringArmComponent> CameraBoom;

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, Meta = (AllowPrivateAccess = "true"))
	TObjectPtr<class UCameraComponent> Camera;


//Input Section
protected:
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, Meta = (AllowPrivateAccess = "true"))
	TObjectPtr<class UInputMappingContext> DefaultMappingContext;//늘리고싶으면 아래에 또하나 만들면됌.

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, Meta = (AllowPrivateAccess = "true"))
	TObjectPtr<class UInputAction> JumpAction;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, Meta = (AllowPrivateAccess = "true"))
	TObjectPtr<class UInputAction> MoveAction;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, Meta = (AllowPrivateAccess = "true"))
	TObjectPtr<class UInputAction> LookAction;

	void Move(const FInputActionValue& Value);
	void Look(const FInputActionValue& Value);

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