PlayerCharacter (사용자 정의 캐릭터 클래스)
PlayerCharacter.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "AlsCharacter.h"
#include "PlayerCharacter.generated.h"
class UPlayerCharacterStatusComponent;
class UCombatComponent;
struct FInputActionValue;
class UAlsCameraComponent;
class UInputMappingContext;
class UInputAction;
UCLASS(AutoExpandCategories = ("Settings|Player Character"))
class CR4S_API APlayerCharacter : public AAlsCharacter
{
GENERATED_BODY()
public:
// Sets default values for this character's properties
APlayerCharacter();
#pragma region Overrides
public:
virtual void DisplayDebug(UCanvas* Canvas, const FDebugDisplayInfo& DisplayInfo, float& Unused, float& VerticalLocation) override;
virtual void NotifyControllerChanged() override;
protected:
virtual void CalcCamera(float DeltaTime, FMinimalViewInfo& ViewInfo) override;
virtual void SetupPlayerInputComponent(UInputComponent* Input) override;
#pragma endregion
#pragma region InputFunction
private:
virtual void Input_OnLookMouse(const FInputActionValue& ActionValue);
virtual void Input_OnLook(const FInputActionValue& ActionValue);
virtual void Input_OnMove(const FInputActionValue& ActionValue);
virtual void Input_OnSprint(const FInputActionValue& ActionValue);
virtual void Input_OnWalk();
virtual void Input_OnCrouch();
virtual void Input_OnJump(const FInputActionValue& ActionValue);
virtual void Input_OnAim(const FInputActionValue& ActionValue);
virtual void Input_OnRagdoll();
virtual void Input_OnRoll();
virtual void Input_OnRotationMode();
virtual void Input_OnViewMode();
virtual void Input_OnSwitchShoulder();
#pragma endregion
#pragma region Components
protected:
UPROPERTY(VisibleDefaultsOnly, BlueprintReadOnly, Category = "Player Character")
TObjectPtr<UAlsCameraComponent> Camera;
#pragma endregion
#pragma region InputActions
protected:
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Settings|Player Character", Meta = (DisplayThumbnail = false))
TObjectPtr<UInputMappingContext> InputMappingContext;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Settings|Player Character", Meta = (DisplayThumbnail = false))
TObjectPtr<UInputAction> LookMouseAction;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Settings|Player Character", Meta = (DisplayThumbnail = false))
TObjectPtr<UInputAction> LookAction;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Settings|Player Character", Meta = (DisplayThumbnail = false))
TObjectPtr<UInputAction> MoveAction;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Settings|Player Character", Meta = (DisplayThumbnail = false))
TObjectPtr<UInputAction> SprintAction;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Settings|Player Character", Meta = (DisplayThumbnail = false))
TObjectPtr<UInputAction> WalkAction;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Settings|Player Character", Meta = (DisplayThumbnail = false))
TObjectPtr<UInputAction> CrouchAction;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Settings|Player Character", Meta = (DisplayThumbnail = false))
TObjectPtr<UInputAction> JumpAction;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Settings|Player Character", Meta = (DisplayThumbnail = false))
TObjectPtr<UInputAction> AimAction;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Settings|Player Character", Meta = (DisplayThumbnail = false))
TObjectPtr<UInputAction> RagdollAction;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Settings|Player Character", Meta = (DisplayThumbnail = false))
TObjectPtr<UInputAction> RollAction;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Settings|Player Character", Meta = (DisplayThumbnail = false))
TObjectPtr<UInputAction> RotationModeAction;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Settings|Player Character", Meta = (DisplayThumbnail = false))
TObjectPtr<UInputAction> ViewModeAction;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Settings|Player Character", Meta = (DisplayThumbnail = false))
TObjectPtr<UInputAction> SwitchShoulderAction;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Settings|Player Character", Meta = (DisplayThumbnail = false))
TObjectPtr<UInputAction> AttackAction;
#pragma endregion
#pragma region LookSettings
protected:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Settings|Player Character", Meta = (ClampMin = 0, ForceUnits = "x"))
float LookUpMouseSensitivity{1.0f};
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Settings|Player Character", Meta = (ClampMin = 0, ForceUnits = "x"))
float LookRightMouseSensitivity{1.0f};
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Settings|Player Character", Meta = (ClampMin = 0, ForceUnits = "deg/s"))
float LookUpRate{90.0f};
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Settings|Player Character", Meta = (ClampMin = 0, ForceUnits = "deg/s"))
float LookRightRate{240.0f};
#pragma endregion
};
PlayerCharacter.cpp
#include "PlayerCharacter.h"
#include "AlsCameraComponent.h"
#include "EnhancedInputComponent.h"
#include "EnhancedInputSubsystems.h"
#include "Character/Components/CombatComponent.h"
#include "Character/Components/PlayerCharacterStatusComponent.h"
#include "Engine/LocalPlayer.h"
#include "GameFramework/PlayerController.h"
#include "Utility/AlsVector.h"
APlayerCharacter::APlayerCharacter()
{
Camera = CreateDefaultSubobject<UAlsCameraComponent>(FName{TEXTVIEW("Camera")});
Camera->SetupAttachment(GetMesh());
Camera->SetRelativeRotation_Direct({0.0f, 90.0f, 0.0f});
}
void APlayerCharacter::NotifyControllerChanged()
{
const auto* PreviousPlayer{Cast<APlayerController>(PreviousController)};
if (IsValid(PreviousPlayer))
{
auto* InputSubsystem{ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PreviousPlayer->GetLocalPlayer())};
if (IsValid(InputSubsystem))
{
InputSubsystem->RemoveMappingContext(InputMappingContext);
}
}
auto* NewPlayer{Cast<APlayerController>(GetController())};
if (IsValid(NewPlayer))
{
NewPlayer->InputYawScale_DEPRECATED = 1.0f;
NewPlayer->InputPitchScale_DEPRECATED = 1.0f;
NewPlayer->InputRollScale_DEPRECATED = 1.0f;
auto* InputSubsystem{ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(NewPlayer->GetLocalPlayer())};
if (IsValid(InputSubsystem))
{
FModifyContextOptions Options;
Options.bNotifyUserSettings = true;
InputSubsystem->AddMappingContext(InputMappingContext, 0, Options);
}
}
Super::NotifyControllerChanged();
}
void APlayerCharacter::CalcCamera(const float DeltaTime, FMinimalViewInfo& ViewInfo)
{
if (Camera->IsActive())
{
Camera->GetViewInfo(ViewInfo);
return;
}
Super::CalcCamera(DeltaTime, ViewInfo);
}
void APlayerCharacter::SetupPlayerInputComponent(UInputComponent* Input)
{
Super::SetupPlayerInputComponent(Input);
auto* EnhancedInput{Cast<UEnhancedInputComponent>(Input)};
if (IsValid(EnhancedInput))
{
EnhancedInput->BindAction(LookMouseAction, ETriggerEvent::Triggered, this, &ThisClass::Input_OnLookMouse);
EnhancedInput->BindAction(LookMouseAction, ETriggerEvent::Canceled, this, &ThisClass::Input_OnLookMouse);
EnhancedInput->BindAction(LookAction, ETriggerEvent::Triggered, this, &ThisClass::Input_OnLook);
EnhancedInput->BindAction(LookAction, ETriggerEvent::Canceled, this, &ThisClass::Input_OnLook);
EnhancedInput->BindAction(MoveAction, ETriggerEvent::Triggered, this, &ThisClass::Input_OnMove);
EnhancedInput->BindAction(MoveAction, ETriggerEvent::Canceled, this, &ThisClass::Input_OnMove);
EnhancedInput->BindAction(SprintAction, ETriggerEvent::Triggered, this, &ThisClass::Input_OnSprint);
EnhancedInput->BindAction(SprintAction, ETriggerEvent::Canceled, this, &ThisClass::Input_OnSprint);
EnhancedInput->BindAction(WalkAction, ETriggerEvent::Triggered, this, &ThisClass::Input_OnWalk);
EnhancedInput->BindAction(CrouchAction, ETriggerEvent::Triggered, this, &ThisClass::Input_OnCrouch);
EnhancedInput->BindAction(JumpAction, ETriggerEvent::Triggered, this, &ThisClass::Input_OnJump);
EnhancedInput->BindAction(JumpAction, ETriggerEvent::Canceled, this, &ThisClass::Input_OnJump);
EnhancedInput->BindAction(AimAction, ETriggerEvent::Triggered, this, &ThisClass::Input_OnAim);
EnhancedInput->BindAction(AimAction, ETriggerEvent::Canceled, this, &ThisClass::Input_OnAim);
EnhancedInput->BindAction(RagdollAction, ETriggerEvent::Triggered, this, &ThisClass::Input_OnRagdoll);
EnhancedInput->BindAction(RollAction, ETriggerEvent::Triggered, this, &ThisClass::Input_OnRoll);
EnhancedInput->BindAction(RotationModeAction, ETriggerEvent::Triggered, this, &ThisClass::Input_OnRotationMode);
EnhancedInput->BindAction(ViewModeAction, ETriggerEvent::Triggered, this, &ThisClass::Input_OnViewMode);
EnhancedInput->BindAction(SwitchShoulderAction, ETriggerEvent::Triggered, this, &ThisClass::Input_OnSwitchShoulder);
}
}
void APlayerCharacter::Input_OnLookMouse(const FInputActionValue& ActionValue)
{
const FVector2f Value{ActionValue.Get<FVector2D>()};
AddControllerPitchInput(Value.Y * LookUpMouseSensitivity);
AddControllerYawInput(Value.X * LookRightMouseSensitivity);
}
void APlayerCharacter::Input_OnLook(const FInputActionValue& ActionValue)
{
const FVector2f Value{ActionValue.Get<FVector2D>()};
AddControllerPitchInput(Value.Y * LookUpRate);
AddControllerYawInput(Value.X * LookRightRate);
}
void APlayerCharacter::Input_OnMove(const FInputActionValue& ActionValue)
{
const auto Value{UAlsVector::ClampMagnitude012D(ActionValue.Get<FVector2D>())};
const auto ForwardDirection{UAlsVector::AngleToDirectionXY(UE_REAL_TO_FLOAT(GetViewState().Rotation.Yaw))};
const auto RightDirection{UAlsVector::PerpendicularCounterClockwiseXY(ForwardDirection)};
AddMovementInput(ForwardDirection * Value.Y + RightDirection * Value.X);
}
void APlayerCharacter::Input_OnSprint(const FInputActionValue& ActionValue)
{
SetDesiredGait(ActionValue.Get<bool>() ? AlsGaitTags::Sprinting : AlsGaitTags::Running);
}
void APlayerCharacter::Input_OnWalk()
{
if (GetDesiredGait() == AlsGaitTags::Walking)
{
SetDesiredGait(AlsGaitTags::Running);
}
else if (GetDesiredGait() == AlsGaitTags::Running)
{
SetDesiredGait(AlsGaitTags::Walking);
}
}
void APlayerCharacter::Input_OnCrouch()
{
if (GetDesiredStance() == AlsStanceTags::Standing)
{
SetDesiredStance(AlsStanceTags::Crouching);
}
else if (GetDesiredStance() == AlsStanceTags::Crouching)
{
SetDesiredStance(AlsStanceTags::Standing);
}
}
void APlayerCharacter::Input_OnJump(const FInputActionValue& ActionValue)
{
if (ActionValue.Get<bool>())
{
if (StopRagdolling())
{
return;
}
if (StartMantlingGrounded())
{
return;
}
if (GetStance() == AlsStanceTags::Crouching)
{
SetDesiredStance(AlsStanceTags::Standing);
return;
}
Jump();
}
else
{
StopJumping();
}
}
void APlayerCharacter::Input_OnAim(const FInputActionValue& ActionValue)
{
SetDesiredAiming(ActionValue.Get<bool>());
}
void APlayerCharacter::Input_OnRagdoll()
{
if (!StopRagdolling())
{
StartRagdolling();
}
}
void APlayerCharacter::Input_OnRoll()
{
static constexpr auto PlayRate{1.3f};
StartRolling(PlayRate);
}
void APlayerCharacter::Input_OnRotationMode()
{
SetDesiredRotationMode(GetDesiredRotationMode() == AlsRotationModeTags::VelocityDirection
? AlsRotationModeTags::ViewDirection
: AlsRotationModeTags::VelocityDirection);
}
void APlayerCharacter::Input_OnViewMode()
{
SetViewMode(GetViewMode() == AlsViewModeTags::ThirdPerson ? AlsViewModeTags::FirstPerson : AlsViewModeTags::ThirdPerson);
}
// ReSharper disable once CppMemberFunctionMayBeConst
void APlayerCharacter::Input_OnSwitchShoulder()
{
Camera->SetRightShoulder(!Camera->IsRightShoulder());
}
void APlayerCharacter::DisplayDebug(UCanvas* Canvas, const FDebugDisplayInfo& DisplayInfo, float& Unused, float& VerticalLocation)
{
if (Camera->IsActive())
{
Camera->DisplayDebug(Canvas, DisplayInfo, VerticalLocation);
}
Super::DisplayDebug(Canvas, DisplayInfo, Unused, VerticalLocation);
}
- 이전 포스팅에서 살펴봤던 예시용 클래스의 코드를 동일하게 작성하여 구성
- 스테이터스, 공격 등을 위한 함수나 컴포넌트들은 추후 추가 구현 예정
BP_Player (PlayerCharacter의 블루프린트)
- 이전에 분석한 B_Als_Character와 동일하게 구성
- VisbileMesh의 경우 리타케팅용으로 추가된 컴포넌트
- 각종 설정 관련 값도 동일하게 설정
결과
- 이동 및 파쿠르 동작 정상 작동
- 오버레이 모드에 따른 물체 소환 및 부착, 애니메이션 변경 정상 적용
참고자료
'프로젝트 > CR4S' 카테고리의 다른 글
Core Reboot:Four Seasons - #6 캐릭터 ALS 적용 (New Skeletal Mesh) (0) | 2025.05.14 |
---|---|
Core Reboot:Four Seasons - #5 StatusComponent 설계 (0) | 2025.05.13 |
Core Reboot:Four Seasons - #3 ALS(Advanced Locomotion System) 분석 (0) | 2025.05.09 |
Core Reboot:Four Seasons - #2 플레이어블 로봇 클래스 설계 (파츠 조합형 로봇) (0) | 2025.05.08 |
Core Reboot:Four Seasons - #1 스테이터스 관리용 데이터 유형 결정 (Data Table vs. Data Asset) (0) | 2025.05.07 |