- 플레이어 기본 상태의 애니메이션 구현하기
- AnimInstance 생성
- Basic 상태의 BlendSpace를 만들고 AnimBlueprint 생성하기
- 공용으로 사용할 Dash, Roll 액션 추가하기
- 플레이어 외에 몬스터도 사용할 공용 애님 인스턴스 클래스 제작하기
- 공용 애님 인스턴스를 상속받은 플레이어 전용 클래스 제작하기
플레이어와 몬스터 ABP의 부모가 되는 AnimInstance클래스를 생성합니다.
MMBaseAnimInstance Class
- 움직임 여부(bIsMove), 이동 속도(GroundSpeed) 필요
// MMBaseAnimInstance Header
#pragma once
#include "CoreMinimal.h"
#include "Animation/AnimInstance.h"
#include "MMBaseAnimInstance.generated.h"
/**
*
*/
UCLASS()
class MYSTICMAZE_API UMMBaseAnimInstance : public UAnimInstance
{
GENERATED_BODY()
public:
UMMBaseAnimInstance();
protected:
// Initialize
virtual void NativeInitializeAnimation() override;
// Update
virtual void NativeUpdateAnimation(float DeltaSeconds) override;
// Base Move
protected:
UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
TObjectPtr<class ACharacter> Owner;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
TObjectPtr<class UCharacterMovementComponent> Movement;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
uint8 bIsMove : 1;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
FVector Velocity;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
float GroundSpeed;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
float MovingThreshould;
};
// MMBaseAnimInstance Cpp
#include "Animation/MMBaseAnimInstance.h"
#include "GameFramework/Character.h"
#include "GameFramework/CharacterMovementComponent.h"
UMMBaseAnimInstance::UMMBaseAnimInstance()
{
// 변수 초기화
MovingThreshould = 3.0f;
}
void UMMBaseAnimInstance::NativeInitializeAnimation()
{
Super::NativeInitializeAnimation();
// Owner Actor & CharacterMovement 저장
Owner = Cast<ACharacter>(GetOwningActor());
if (Owner)
{
Movement = Owner->GetCharacterMovement();
}
}
void UMMBaseAnimInstance::NativeUpdateAnimation(float DeltaSeconds)
{
Super::NativeUpdateAnimation(DeltaSeconds);
// 매 프레임 값 갱신
if (Movement)
{
Velocity = Movement->Velocity;
GroundSpeed = Velocity.Size2D();
bIsMove = GroundSpeed > MovingThreshould;
}
}
// MMPlayerAnimInstance Cpp
#include "Animation/MMPlayerAnimInstance.h"
UMMPlayerAnimInstance::UMMPlayerAnimInstance()
{
}
void UMMPlayerAnimInstance::NativeInitializeAnimation()
{
Super::NativeInitializeAnimation();
}
void UMMPlayerAnimInstance::NativeUpdateAnimation(float DeltaSeconds)
{
Super::NativeUpdateAnimation(DeltaSeconds);
}
플레이어는 기본적으로 이동하는 방향을 바라보며 이동할 것입니다.
BlendSpace1D를 생성해 Idle - Walk - Run 애니메이션을 섞어줍니다.
MMPlayerAnimInstance를 상속받은 ABP_Player를 생성합니다.
상속된 변수 표시를 체크하면 C++에서 만든 변수들이 출력됩니다.
스테이트 머신을 새로 생성하고 내부에 위에서 만든 BlendSpace를 넣어주도록 합니다.
BS_Basic으로 들어가서 GroundSpeed 변수를 연결해줍니다.
C++ 코드 상에서 플레이어의 SkeletalMesh가 ABP_Player를 사용하도록 지정합니다.
// AMMPlayerCharacter Cpp
AMMPlayerCharacter::AMMPlayerCharacter()
{
...
// Mesh 설정
{
// Load
static ConstructorHelpers::FObjectFinder<USkeletalMesh> SkeletalMeshRef(TEXT("/Script/Engine.SkeletalMesh'/Game/Pirate/Mesh_UE5/Full/SKM_Pirate_Full_03.SKM_Pirate_Full_03'"));
if (SkeletalMeshRef.Object)
{
// Mesh 설정
GetMesh()->SetSkeletalMesh(SkeletalMeshRef.Object);
GetMesh()->SetRelativeLocationAndRotation(FVector(0.0f, 0.0f, -90.0f), FRotator(0.0f, -90.0f, 0.0f));
// Animation 설정 (애니메이션 블루프린트 사용, 애니메이션 블루프린트 클래스 정보 세팅)
GetMesh()->SetAnimationMode(EAnimationMode::AnimationBlueprint);
static ConstructorHelpers::FClassFinder<UAnimInstance> AnimInstanceClassRef(TEXT("/Script/Engine.AnimBlueprint'/Game/MysticMaze/Player/Animations/ABP_Player.ABP_Player_C'"));
if (AnimInstanceClassRef.Class)
{
GetMesh()->SetAnimInstanceClass(AnimInstanceClassRef.Class);
}
}
}
...
}
- 현재는 이동 시 플레이어는 항상 최대 속도로 이동하도록 설정되어 있습니다.
- [Shift] 키를 데쉬 키로 사용해 걷기/달리기를 구분하도록 하겠습니다.
IMC_BasicPlayer에 매핑해줍니다.
C++ 코드로 Dash 로직을 제어해주도록 하겠습니다.
MMPlayerCharacter Class
- IA_Dash가 Pressed, Completed 될 때 연동될 함수를 지정해주도록 합니다.
- Dash 상태일 때는 공격이 불가능하도록 설정할 것이므로 bIsDash 변수를 추가해 데쉬 상태임을 관리하도록 하겠습니다.
// MMPlayerCharacter Header
// Input Section
protected:
// 공용
void DashStart();
void DashEnd();
...
// 공용
UPROPERTY(VisibleAnywhere, Category = Input, Meta = (AllowPrivateAccess = "true"))
TObjectPtr<class UInputAction> IA_Dash;
...
// Member Variable
protected:
uint8 bIsDash : 1;
// MMPlayerCharacter Cpp
AMMPlayerCharacter::AMMPlayerCharacter()
{
// Input
{
// 공용
static ConstructorHelpers::FObjectFinder<UInputAction>IA_DashRef(TEXT("/Script/EnhancedInput.InputAction'/Game/MysticMaze/Player/Control/InputAction/IA_Dash.IA_Dash'"));
if (IA_DashRef.Object)
{
IA_Dash = IA_DashRef.Object;
}
...
}
// Setting
{
...
// 이동속도 조정
GetCharacterMovement()->MaxWalkSpeed = 230.0f;
}
// Member Variable 초기화
{
bIsDash = false;
}
}
void AMMPlayerCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
UEnhancedInputComponent* EnhancedInputComponent = CastChecked<UEnhancedInputComponent>(PlayerInputComponent);
...
EnhancedInputComponent->BindAction(IA_Dash, ETriggerEvent::Triggered, this, &AMMPlayerCharacter::DashStart);
EnhancedInputComponent->BindAction(IA_Dash, ETriggerEvent::Completed, this, &AMMPlayerCharacter::DashEnd);
}
void AMMPlayerCharacter::DashStart()
{
bIsDash = true;
GetCharacterMovement()->MaxWalkSpeed = 600.0f;
}
void AMMPlayerCharacter::DashEnd()
{
bIsDash = false;
GetCharacterMovement()->MaxWalkSpeed = 230.0f;
}
- 플레이어가 공격을 회피하기 위한 수단인 구르기 동작을 만들어주도록 하겠습니다.
- 이전과 동일하게 IA_Roll이라는 입력 액션을 만들어 IMC_BasicPlayer에 등록한 상태로 진행하도록 하겠습니다.
사용할 애니메이션 몽타주 만들기
애니메이션 블루프린트에서 전체 Body에 적용할 애니메이션입니다.
FullBody Slot을 생성하고 적용시켜주도록 합니다.
추가적으로, 구르기 애니메이션이 끝난 후 해당 위치를 유지하려면 루트모션 활성화를 체크해주면 됩니다.
// MMPlayerCharacter Header
// Input Section
protected:
// 공용
void RollStart();
void RollEnd(class UAnimMontage* Montage, bool IsEnded);
// 공용
UPROPERTY(VisibleAnywhere, Category = Input, Meta = (AllowPrivateAccess = "true"))
TObjectPtr<class UInputAction> IA_Roll;
// Montage
protected:
UPROPERTY(EditAnywhere, Category = Montage, Meta = (AllowPrivateAccess = "true"))
TObjectPtr<class UAnimMontage> RollMontage;
// Member Variable
protected:
uint8 bIsRoll : 1;
void RollEnd(class UAnimMontage* Montage, bool IsEnded);
-> Roll 몽타주의 재생이 끝나면 자동으로 실행될 함수
-> FOnMontageEnded 델리게이트에 연동하기 위한 파라미터 맞춤 함수
// MMPlayerCharacter Cpp
void AMMPlayerCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
UEnhancedInputComponent* EnhancedInputComponent = CastChecked<UEnhancedInputComponent>(PlayerInputComponent);
...
EnhancedInputComponent->BindAction(IA_Roll, ETriggerEvent::Triggered, this, &AMMPlayerCharacter::RollStart);
}
void AMMPlayerCharacter::RollStart()
{
// 구르기 중이면 리턴
if (bIsRoll) return;
// 애님 인스턴스 가져오기
UAnimInstance* AnimInstance = GetMesh()->GetAnimInstance();
if (AnimInstance)
{
// Roll Check (구르기 활성화)
bIsRoll = true;
// 몽타주 재생
AnimInstance->Montage_Play(RollMontage, 1.3f);
// 몽타주 재생 종료 바인딩
FOnMontageEnded EndDelegate;
EndDelegate.BindUObject(this, &AMMPlayerCharacter::RollEnd);
// RollMontage 종료 시 EndDelegate에 연동된 함수 호출
AnimInstance->Montage_SetEndDelegate(EndDelegate, RollMontage);
}
}
void AMMPlayerCharacter::RollEnd(class UAnimMontage* Montage, bool IsEnded)
{
// Roll UnCheck (구르기 비활성화)
bIsRoll = false;
}