[ Unreal 5.4.2 ] Character 클래스 이동 구현 (EnhancedInput) 및 게임 모드 설정
테스트 하는 동안 Default Pawn Class를 설정해주기 위해서 임시로Game Mode Base를 상속받은.
TestGameModeBase 스크립트를 생성해 주었습니다.
.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/GameModeBase.h"
#include "TestGameModeBase.generated.h"
/**
*
*/
UCLASS()
class TESTGAME_API ATestGameModeBase : public AGameModeBase
{
GENERATED_BODY()
public:
virtual void BeginPlay() override;
};
//
.cpp
#include "TestGameModeBase.h"
void ATestGameModeBase::BeginPlay()
{
Super::BeginPlay();
if (GEngine)
{
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("Hello, World!"));
}
}
해당 코드가 정상적으로 작동하는지 확인하기 위해서.
BeginPlay를 사용해서 DebugMessage를 사용해서 Text를 출력합니다. - 게임 시작시.
해당 코드를 기반으로 Create Blueprint class based on TestGameModeBase를 눌러서
블루프린트를 생성해줍니다.
이후 Project Settings -> Project 하위 Map & Modes 에서 Default Modes 에 있는 Default GameMode를 해당 블루프린트로 변경해줍니다.
EnhancedInput 을 사용하기 위해서 Input Action 과 Input Mapping Context를 생성해줍니다.
Input Action 5개 ( Forward, Jump, LookX, LookY, Right ) 와 Input Mapping Context 1개를 만들어줍니다.
언리얼 엔진 5 공식 EnhancedInput 사용 방법에서는 Forward 와 Right 를 사용하지 않고 Move 하나로 사용하지만
오류가 생겨서 두개로 분할하여서 사용하였습니다.
IMC_Player를 클릭해서 키들을 Mapping 해줍니다.
Negate 를 사용하는 이유는
Negate 를 사용하지 않는 경우 +를 반환하고
Negate 를 사용하면 - 를 반환하기 떄문에
앞뒤 양옆의 방향에 대한 값을 입력받기 위해서 입니다.
.h
#pragma once
#include "InputActionValue.h"
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "Camera/CameraComponent.h"
#include "Projectile.h"
#include "CharacterController.generated.h"
UCLASS()
class TESTGAME_API ACharacterController : public ACharacter
{
GENERATED_BODY()
public:
// Sets default values for this character's properties
ACharacterController();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
// Called to bind functionality to input
virtual void SetupPlayerInputComponent(class UInputComponent *PlayerInputComponent) override;
private:
// IMC
UPROPERTY(editanywhere, category = input, meta = (allowprivateaccess = true))
class UInputMappingContext *DefaultMappingContext;
// IA_Jump
UPROPERTY(editanywhere, category = input, meta = (allowprivateaccess = true))
class UInputAction *JumpAction;
UPROPERTY(editanywhere, category = input, meta = (allowprivateaccess = true))
class UInputAction *ForwardAction;
UPROPERTY(editanywhere, category = input, meta = (allowprivateaccess = true))
class UInputAction *RightAction;
UPROPERTY(editanywhere, category = input, meta = (allowprivateaccess = true))
class UInputAction *LookXAction;
UPROPERTY(editanywhere, category = input, meta = (allowprivateaccess = true))
class UInputAction *LookYAction;
UPROPERTY(VisibleAnywhere)
UCameraComponent *CameraComponent;
protected:
void Forward(const FInputActionValue &value);
void Right(const FInputActionValue &value);
//void Look(const FInputActionValue &value);
void Jump() override;
void LookX(const FInputActionValue &value);
void LookY(const FInputActionValue &value);
.cpp
#include "CharacterController.h"
#include "EnhancedInputComponent.h"
#include "EnhancedInputSubsystems.h"
#include "GameFramework/Character.h"
#include "Components/CapsuleComponent.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "InputMappingContext.h"
// Sets default values
ACharacterController::ACharacterController()
{
// Set this character to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
static ConstructorHelpers::FObjectFinder<UInputMappingContext> DEFAULT_CONTEXT(TEXT("/Script/EnhancedInput.InputMappingContext'/Game/Input/IMC_Player.IMC_Player'"));
static ConstructorHelpers::FObjectFinder<UInputAction> IA_JUMP(TEXT("/ Script / EnhancedInput.InputAction'/Game/Input/IA_Jump.IA_Jump'"));
static ConstructorHelpers::FObjectFinder<UInputAction> IA_Forward(TEXT("/ Script / EnhancedInput.InputAction'/Game/Input/IA_Forward.IA_Forward'"));
static ConstructorHelpers::FObjectFinder<UInputAction> IA_Right(TEXT("/ Script / EnhancedInput.InputAction'/Game/Input/IA_Right.IA_Right'"));
static ConstructorHelpers::FObjectFinder<UInputAction> IA_LOOKX(TEXT("/ Script / EnhancedInput.InputAction'/Game/Input/IA_LookX.IA_LookX'"));
static ConstructorHelpers::FObjectFinder<UInputAction> IA_LOOKY(TEXT("/ Script / EnhancedInput.InputAction'/Game/Input/IA_LookY.IA_LookY'"));
if (DEFAULT_CONTEXT.Succeeded())
{
DefaultMappingContext = DEFAULT_CONTEXT.Object;
}
if (IA_JUMP.Succeeded())
{
JumpAction = IA_JUMP.Object;
}
if (IA_Forward.Succeeded())
{
// GEngine->AddOnScreenDebugMessage(-1, 3.0f, FColor::Purple, TEXT("Found Forward Action"));
ForwardAction = IA_Forward.Object;
}
if (IA_Right.Succeeded())
{
// GEngine->AddOnScreenDebugMessage(-1, 3.0f, FColor::Purple, TEXT("Found Right Action"));
RightAction = IA_Right.Object;
}
if (IA_LOOKX.Succeeded())
{
LookXAction = IA_LOOKX.Object;
}
if (IA_LOOKY.Succeeded())
{
LookYAction = IA_LOOKY.Object;
}
CameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("PersonCamera"));
CameraComponent->SetupAttachment(GetCapsuleComponent());
CameraComponent->SetRelativeLocation(FVector(-110.0f, 0.0f, 245.0 + BaseEyeHeight));
CameraComponent->SetRelativeRotation(FRotator(0.0, -30.0f, 0.0));
CameraComponent->bUsePawnControlRotation = true;
}
// Called when the game starts or when spawned
void ACharacterController::BeginPlay()
{
Super::BeginPlay();
if (APlayerController *PlayerController = Cast<APlayerController>(GetController()))
{
// ULocalPlayer *LocalPlayer = PlayerController->GetLocalPlayer();
if (UEnhancedInputLocalPlayerSubsystem *SubSystem =
ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PlayerController->GetLocalPlayer()))
{
SubSystem->AddMappingContext(DefaultMappingContext, 0);
}
}
}
// Called every frame
void ACharacterController::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
FVector CurrentVelocity = GetCharacterMovement()->Velocity;
// 이동 속도를 로그로 출력
// UE_LOG(LogTemp, Warning, TEXT("Current Velocity: %s"), *CurrentVelocity.ToString());
// GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Green, *CurrentVelocity.ToString());
}
// Called to bind functionality to input
void ACharacterController::SetupPlayerInputComponent(UInputComponent *PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
if (UEnhancedInputComponent *EnhancedInputComponent = CastChecked<UEnhancedInputComponent>(PlayerInputComponent))
{
EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Triggered, this, &ACharacterController::Jump);
EnhancedInputComponent->BindAction(ForwardAction, ETriggerEvent::Triggered, this, &ACharacterController::Forward);
EnhancedInputComponent->BindAction(RightAction, ETriggerEvent::Triggered, this, &ACharacterController::Right);
EnhancedInputComponent->BindAction(LookXAction, ETriggerEvent::Triggered, this, &ACharacterController::LookX);
EnhancedInputComponent->BindAction(LookYAction, ETriggerEvent::Triggered, this, &ACharacterController::LookY);
}
}
void ACharacterController::Forward(const FInputActionValue &Value)
{
FVector2D MovementVector = Value.Get<FVector2D>();
// GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Green, MovementVector.ToString());
// 플레이어의 현재 방향
const FRotator Rotation = Controller->GetControlRotation();
// Yaw만을 사용해 새로운 회전 생성
const FRotator YawRotation(0.f, Rotation.Yaw, 0.f);
// Yaw회전을 기준으로 전방 방향 계산
const FVector ForwardDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
// 전방 방향과 Y값(전방 이동속도)을 사용하여 이동
AddMovementInput(ForwardDirection, MovementVector.X);
}
void ACharacterController::Right(const FInputActionValue &Value)
{
FVector2D MovementVector = Value.Get<FVector2D>();
// GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Blue, MovementVector.ToString());
// 플레이어의 현재 방향
const FRotator Rotation = Controller->GetControlRotation();
// Yaw만을 사용해 새로운 회전 생성
const FRotator YawRotation(0.f, Rotation.Yaw, 0.f);
// Yaw회전을 기준으로 우측 방향 계산
const FVector RightDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);
// 우측 방향과 X값(우측 이동속도)을 사용하여 이동
AddMovementInput(RightDirection, MovementVector.X);
}
void ACharacterController::LookX(const FInputActionValue &Value)
{
// 회전량
const FVector2D LookVector = Value.Get<FVector2D>();
// X값(수평 회전량)을 사용해 yaw회전 추가 > 수평 시선 회전
AddControllerYawInput(LookVector.X * 0.1f);
}
void ACharacterController::LookY(const FInputActionValue &Value)
{
// 회전량
const FVector2D LookVector = Value.Get<FVector2D>();
// Y값(수직 회전량)을 사용해 pitch회전 추가 > 수직 시선 회전
AddControllerPitchInput(LookVector.X * 0.05f);
}
void ACharacterController::Jump()
{
Super::Jump();
}