[UE5 C++] 플레이어 애니메이션 - Basic, Dash, Roll

LeeTaes·2024년 4월 25일
0

[UE_Project] MysticMaze

목록 보기
4/17

언리얼 엔진을 사용한 RPG 프로젝트 만들기

  • 플레이어 기본 상태의 애니메이션 구현하기
    - 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를 생성합니다.
  • 아직은 추가적인 내용이 필요 없으니 생성만 하고 사용하도록 하겠습니다.
// MMPlayerAnimInstance Cpp
#include "Animation/MMPlayerAnimInstance.h"

UMMPlayerAnimInstance::UMMPlayerAnimInstance()
{
}

void UMMPlayerAnimInstance::NativeInitializeAnimation()
{
	Super::NativeInitializeAnimation();
}

void UMMPlayerAnimInstance::NativeUpdateAnimation(float DeltaSeconds)
{
	Super::NativeUpdateAnimation(DeltaSeconds);
}

BlendSpace 생성하기

플레이어는 기본적으로 이동하는 방향을 바라보며 이동할 것입니다.
BlendSpace1D를 생성해 Idle - Walk - Run 애니메이션을 섞어줍니다.


애니메이션 블루프린트 생성하기

MMPlayerAnimInstance를 상속받은 ABP_Player를 생성합니다.

  • 상속된 변수 표시를 체크하면 C++에서 만든 변수들이 출력됩니다.

  • 스테이트 머신을 새로 생성하고 내부에 위에서 만든 BlendSpace를 넣어주도록 합니다.

  • BS_Basic으로 들어가서 GroundSpeed 변수를 연결해줍니다.


플레이어에게 ABP_Player 연결하기

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);
			}
		}
	}
    
    ...
}
  • BP_Player를 확인해보면 제대로 들어온 것을 확인할 수 있습니다.
  • 실행 결과

플레이어 데쉬 만들기

  • 현재는 이동 시 플레이어는 항상 최대 속도로 이동하도록 설정되어 있습니다.
  • [Shift] 키를 데쉬 키로 사용해 걷기/달리기를 구분하도록 하겠습니다.
  1. InputAction을 생성합니다.
  1. IMC_BasicPlayer에 매핑해줍니다.

  2. C++ 코드로 Dash 로직을 제어해주도록 하겠습니다.

    • [Shift] 키가 눌린 상태 : 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에 등록한 상태로 진행하도록 하겠습니다.
  1. 사용할 애니메이션 몽타주 만들기

    • 애니메이션 블루프린트에서 전체 Body에 적용할 애니메이션입니다.

    • FullBody Slot을 생성하고 적용시켜주도록 합니다.

    • 추가적으로, 구르기 애니메이션이 끝난 후 해당 위치를 유지하려면 루트모션 활성화를 체크해주면 됩니다.

  1. C++ 코드에서 플레이어에게 몽타주를 저장할 슬롯을 만들어주고, 위에서 만든 몽타주를 넣어주도록 합니다.
// 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 델리게이트에 연동하기 위한 파라미터 맞춤 함수

  • 생성한 몽타주를 BP_Player의 RollMontage에 넣어주도록 합니다.
  1. C++ 코드에서 IA_Roll 입력이 들어왔을 때 몽타주를 재생해주도록 합니다.
// 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;
}
  • 결과 확인
profile
클라이언트 프로그래머 지망생

0개의 댓글