3.1-4 플레이어 이동 처리하기
교재와는 다르게 이동을 위한 입력은 EnhancedInput으로 처리하겠습니다.
ThirdPersonCharacter를 임포트할때 이미 입력도 같이 왔습니다. 이걸 사용하겠습니다.
새로운 인핸스드입력은 개별 인풋액션을 조합해서 원하는 Input Mapping Context를 만들수 있습니다. 우클릭후 다음메뉴에서 만들수도 있습니다. 우리는 그냥 있는것 사용하겠습니다.
EnhancedInput을 사용하기 위해 TPSProject.Build.cs에 EnhancedInput 모듈을 추가해주어야 합니다.
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore" , "EnhancedInput" });
키액션과 매핑을 연결해줄 변수를 선언해줍니다. 바인딩할 함수도 선업해주고 이동벡터도 선언해줍니다.
TPSPlayer.h
//헤더추가
#include "InputAction.h"
public:
UPROPERTY(VisibleAnywhere, Category = Camera)
class USpringArmComponent* springArmComp;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera)
class UCameraComponent* tpsCamComp;
UPROPERTY(EditAnywhere, Category = "Input")
UInputMappingContext* PlayerMappingContext;
UPROPERTY(EditAnywhere, Category = "Input")
UInputAction* MoveIA;
UPROPERTY(EditAnywhere, Category = "Input")
UInputAction* LookUpIA;
UPROPERTY(EditAnywhere, Category = "Input")
UInputAction* JumpIA;
UPROPERTY(EditAnywhere, Category = "Input")
UInputAction* FireIA;
public:
void Move(const FInputActionValue& Value);
void LookUp(const FInputActionValue& Value);
void Turn(const FInputActionValue& Value);
void InputJump(const FInputActionValue& Value);
void InputFire(const FInputActionValue& Value);
private:
void Locomotion();
FVector moveDirection;
bool fireReady;
float fireTimerTime;
};
Begin에서 인핸스드인풋을 정의해주고 액션들을 바인딩해주고 정의해줍니다.
ThirdPerson은 마우스입력을 2축으로 입력받아 XY로 Pitch와 Yaw컨트롤을 각각해주고 있어 똑같이 처리하였다. 따라서 Turn은 따로 인풋액션이나 바인딩함수를 만들지 않았다.
Character 클래스를 상속받으면 이동 기능을 제공하는 AddMovementInput 함수가 있다.
해당 함수에 이동방향(moveDirection)을 매개변수로 넘겨준다.
이 때, 이동방향은 플레이어가 바라보는 방향을 감안한 방향으로 이동해야하기 때문에 FTransform의 생성자에 컨트롤러의 회전 값을 넘겨주고, TransformVector를 통해 컨트롤러의 회전을 적용한 방향으로 이동방향을 수정한다.
https://blog.naver.com/abcdxsky/223108086552
[UE5] 플레이어(이동, 회전, 점프, 기본 컴포넌트)
[ 목차 ] 플레이어 플레이어 블루프린트 생성 메쉬 적용 카메라 추가 입력 바인딩 Mapping 이동 플레이어 ...
blog.naver.com
TPSPlayer.cpp
//헤더추가
#include "EnhancedInputSubsystems.h"
#include "EnhancedInputComponent.h"
// Called when the game starts or when spawned
void ATPSPlayer::BeginPlay()
{
Super::BeginPlay();
if (APlayerController* PlayerController = Cast<APlayerController>(GetController()))
{
if (UEnhancedInputLocalPlayerSubsystem* Subsystem
= ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PlayerController->GetLocalPlayer()))
{
Subsystem->AddMappingContext(PlayerMappingContext, 0);
}
}
}
// Called every frame
void ATPSPlayer::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
Locomotion();
}
// Called to bind functionality to input
void ATPSPlayer::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
if (UEnhancedInputComponent* EnhancedInputComponent = CastChecked<UEnhancedInputComponent>(PlayerInputComponent))
{
EnhancedInputComponent->BindAction(MoveIA, ETriggerEvent::Triggered, this, &ATPSPlayer::Move);
EnhancedInputComponent->BindAction(LookUpIA, ETriggerEvent::Triggered, this, &ATPSPlayer::LookUp);
EnhancedInputComponent->BindAction(LookUpIA, ETriggerEvent::Triggered, this, &ATPSPlayer::Turn);
EnhancedInputComponent->BindAction(JumpIA, ETriggerEvent::Triggered, this, &ATPSPlayer::InputJump);
EnhancedInputComponent->BindAction(FireIA, ETriggerEvent::Triggered, this, &ATPSPlayer::InputFire);
}
}
void ATPSPlayer::Locomotion()
{
moveDirection = FTransform(GetControlRotation()).TransformVector(moveDirection);
AddMovementInput(moveDirection);
moveDirection = FVector::ZeroVector; //ZeroVector; == FVector(0,0,0)
}
void ATPSPlayer::Move(const FInputActionValue& Value)
{
const FVector _currentValue = Value.Get<FVector>();
//PRINT_LOG(TEXT("%f %f %f"), _currentValue.X, _currentValue.Y, _currentValue.Z);
if (Controller)
{
moveDirection.Y = _currentValue.X;
moveDirection.X = _currentValue.Y;
}
}
void ATPSPlayer::LookUp(const FInputActionValue& Value)
{
const FVector _currentValue = Value.Get<FVector>();
//PRINT_LOG(TEXT("%f %f %f"), _currentValue.X, _currentValue.Y, _currentValue.Z);
AddControllerPitchInput(_currentValue.Y);
AddControllerYawInput(_currentValue.X);
}
void ATPSPlayer::InputJump(const FInputActionValue& Value)
{
Jump();
}
void ATPSPlayer::InputFire(const FInputActionValue& Value)
{
//if (fireReady)
//{
// UAnimInstance* AnimInstance = GetMesh()->GetAnimInstance();
// if (AnimInstance)
// {
// AnimInstance->Montage_Play(attackAnimMontage);
// }
// fireReady = false;
//}
}
플레이어 회전 처리하기
플레이 해보면 플레이어가 움직이는 곳을 바라 보지 않습니다. 플레이어 회전처리를 하지 않았기 대문입니다.
입력은 Controller가 처리하는데 이걸 springArmComp에서 사용하고 카메라는 자식으로 따라가게 하기위해 언체크 합니다.
Pawn은 Yaw Rotation만 체크해줍니다.
TPSPlayer.cpp 생성자에서 C++로 처리해주면 Default값이 유지 됩니다.
springArmComp->bUsePawnControlRotation = true;
tpsCamComp = CreateDefaultSubobject<UCameraComponent>(TEXT("CameraComp"));
tpsCamComp->SetupAttachment(springArmComp);
tpsCamComp->bUsePawnControlRotation = false;
bUseControllerRotationYaw = true;
Trun(), LookUp() 정의
ia_LookUp, ia_Turn은 나중에 BP_TPSPlayer에서 지정해줘야 합니다.
UPROPERTY(EditDefaultsOnly, Category = "Input")
class UInputAction* ia_LookUp;
UPROPERTY(EditDefaultsOnly, Category = "Input")
class UInputAction* ia_Turn;
private:
FVector direction;
void Turn(const struct FInputActionValue& inputValue);
void LookUp(const struct FInputActionValue& inputValue);
Trun(), LookUp() 구현
void ATPSPlayer::Turn(const FInputActionValue& inputValue)
{
float value = inputValue.Get<float>();
AddControllerYawInput(value);
//PRINT_LOG(TEXT("%f"),value)
}
void ATPSPlayer::LookUp(const FInputActionValue& inputValue)
{
float value = inputValue.Get<float>();
AddControllerPitchInput(value);
//PRINT_LOG(TEXT("%f"), value)
}
void ATPSPlayer::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
auto PlayerInput = CastChecked<UEnhancedInputComponent>(PlayerInputComponent);
if (PlayerInput)
{
//생략
PlayerInput->BindAction(ia_Turn, ETriggerEvent::Triggered, this, &ATPSPlayer::Turn);
PlayerInput->BindAction(ia_LookUp, ETriggerEvent::Triggered, this, &ATPSPlayer::LookUp);
}
}