GAS STUDY(2) - Enhanced Input / Movement Input

Brann Goldbeard·2025년 5월 14일

GAS

목록 보기
2/57
post-thumbnail

이어서 향상된 입력 매핑을 진행해보았다.
이미 여러번 진행해본 만큼 꽤나 익숙하리라 생각된다, 관련 레퍼런스도 유튜브나 LLM을 통해 쉽게 찾아볼 수 있다.

IMC, IA를 생성하고 IMC는 케릭터에 IA는 IMC에 매핑하고, 각 키에 해당되는 함수는 c++에서 작업해준다. AuraPlayerController class를 생성하고 해당 작업을 이어간다. (기존에 게임들 작업할 때는 케릭터에 그냥 맵핑했던걸로 기억하는데, 이번 강의에서는 조금 다른 구조를 가질 예정인듯)

		PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;

		PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore" });
		PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "EnhancedInput" });

		PrivateDependencyModuleNames.AddRange(new string[] {  });

module 추가 해주고..

// Copyright Druid Mechanics

#pragma once

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


class UInputMappingContext;

/**
 * 
 */
UCLASS()
class AURA_API AAuraPlayerController : public APlayerController
{
	GENERATED_BODY()
	
public:
	AAuraPlayerController();
protected:
	virtual void BeginPlay() override;

private:
	UPROPERTY(EditAnywhere, Category="Input")
	TObjectPtr<UInputMappingContext> AuraContext;
};
// Copyright Druid Mechanics


#include "Player/AuraPlayerController.h"

#include "EnhancedInputSubsystems.h"

AAuraPlayerController::AAuraPlayerController()
{
	bReplicates = true;
}

void AAuraPlayerController::BeginPlay()
{
	Super::BeginPlay();
	check(AuraContext); // AuraContext가 Set되지 않았다면 crash

	UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(GetLocalPlayer());
	check(Subsystem); // if 로도 확인가능하지만 check을 이용해 subsystem이 존재하지 않는다면 crash 유도
	Subsystem->AddMappingContext(AuraContext, 0);

	bShowMouseCursor = true;
	DefaultMouseCursor = EMouseCursor::Default;

	FInputModeGameAndUI InputModeData;
	InputModeData.SetLockMouseToViewportBehavior(EMouseLockMode::DoNotLock);
	InputModeData.SetHideCursorDuringCapture(false);
	SetInputMode(InputModeData);
}

이후 MoveAction을 바인딩 해주었다.

public:

	virtual void SetupInputComponent() override;

private:

	UPROPERTY(EditAnywhere, Category="Input")
	TObjectPtr<UInputMappingContext> AuraContext;

	UPROPERTY(EditAnywhere, Category="Input")
	TObjectPtr<UInputAction> MoveAction;

	void Move(const FInputActionValue& InputActionValue);
};
 
void AAuraPlayerController::SetupInputComponent()
{
	Super::SetupInputComponent();

	UEnhancedInputComponent* EnhancedInputComponent = CastChecked<UEnhancedInputComponent>(InputComponent);

	EnhancedInputComponent->BindAction(MoveAction, ETriggerEvent::Triggered, this, &AAuraPlayerController::Move);
}


void AAuraPlayerController::Move(const FInputActionValue& InputActionValue)
{
	const FVector2D InputAxisVector = InputActionValue.Get<FVector2D>();
	const FRotator Rotation = GetControlRotation();
	const FRotator YawRotation(0.f, Rotation.Yaw, 0.f);

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

	if (APawn* ControlledPawn = GetPawn<APawn>())
	{
		ControlledPawn->AddMovementInput(ForwardDirection, InputAxisVector.Y);
		ControlledPawn->AddMovementInput(RightDirection, InputAxisVector.X);
	}
}

항상 const를 사용하는 시점에 대해 헷갈리곤 하는데. 여기 한번 더 정리를 해야 할 것 같다
const의 사전적 의미로는 당연히 상수화 하는것, 변수 자체가 읽기전용이 된다는 것을 외우는 건 아주 쉬운 일이다.
근데 실제로 이런식으로 사용되는걸 보면 좀 어지럽다.
여기서 사용된 예를 들자면

const FRRotator YawRotation(0.f, Rotation.Yaw, 0.f);
* YawRotation은 새로운 FRotator 객체를 생성하면서 초기화된다.
이 때, 이전 줄에서 얻어온 Rotation의 Yaw값만 사용하고, Pitch와 roll은 0으로 설정된다.
* const를 사용한 이유 : YawRotation은 생성될 때 초기값을 가지며, 이 값은 이 코드가 실행되는 '특정 시점'의
Rotation.Yaw값을 기반으로 계산된다. 한 번 초기화된 후에는 이 YawRotation 변수의 값을 변경할 필요가 없으며,
변경해서도 안된다. 따라서 const로 선언하여 의도치 않은 수정을 막는다.

핵심은 const는 변수 자체가 가리키는 값이 초기화 된 후에도 변경될 수 없음을 보장해야한다.
데이터의 원본(입력 액션값, 컨트롤러 회전)은 시간에 따라 동적으로 변화할 수 있다.
다만, const로 선언된 변수는 특정 시점에 그 원본의 값을 복사해 오거나, 특정 값을 기반으로 초기화된 후에는 그 값을 유지한다.

0개의 댓글