[UE5 C++] 직업 구분 및 Weapon 제작 - 2

LeeTaes·2024년 5월 3일
0

[UE_Project] MysticMaze

목록 보기
8/17

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

  • 이전 포스팅에서 작업한 직업 구분을 토대로 애니메이션 설정하기
  • 무기를 소지 후 장착하고 장착 해제하는 애니메이션 적용하기

전사 애니메이션 설정하기

  • Warrior의 Locomotion 애니메이션을 추가하여 무기를 장착했을 때 Warrior Locomotion을 토대로 애니메이션이 재생되게 만들어보겠습니다.
  • 전사는 이동할 때 사용하는 애니메이션이 초보자와 동일하게 Idle, Walk, Run 3가지입니다.
  • 기존과 동일하게 BlendSpace1D를 만들어줍니다.

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

  • 직업에 따라, 무기 착용 여부에 따라 애니메이션을 블렌딩해줘야 합니다.
  • 애니메이션의 업데이트에 사용할 인터페이스를 선언하고, 애님 인스턴스 클래스에서 해당 변수들을 업데이트 합니다.
// MMAnimationUpdateInterface Header
#include "CoreMinimal.h"
#include "UObject/Interface.h"
#include "GameData/MMEnums.h"
#include "MMAnimationUpdateInterface.generated.h"

// This class does not need to be modified.
UINTERFACE(MinimalAPI)
class UMMAnimationUpdateInterface : public UInterface
{
	GENERATED_BODY()
};

/**
 * 
 */
class MYSTICMAZE_API IMMAnimationUpdateInterface
{
	GENERATED_BODY()

	// Add interface functions to this class. This is the class that will be inherited to implement this interface.
public:
	virtual EClassType GetClassType() = 0;
	virtual bool GetIsGuard() = 0;
	virtual bool GetIsEquip() = 0;
};
// MMPlayerCharacter CPP
// Member Variable
protected:
	FORCEINLINE virtual EClassType GetClassType() override { return ClassType; };
	FORCEINLINE virtual bool GetIsGuard() override { return bIsGuard; }
	FORCEINLINE virtual bool GetIsEquip() override { return bIsEquip; }
// MMPlayerAnimInstance Class

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

	IMMAnimationUpdateInterface* Player = Cast<IMMAnimationUpdateInterface>(TryGetPawnOwner());
	if (Player)
	{
		bIsGuard = Player->GetIsGuard();
		ClassType = Player->GetClassType();
		bIsEquip = Player->GetIsEquip();
	}
}
  • 플레이어 애니메이션 블루프린트 수정하기
    - 무기를 장착하면 Warrior, 해제하면 Beginner Locomotion
    - 직업 타입에 따라 구분할 수 있도록 해줍니다.


무기 장착 / 장착 해제 로직 추가하기

  • IA_ConvertWeapon 이라는 공용 입력 액션을 추가해주도록 합니다.
    - 무기를 장착/장착 해제를 스위치하는 로직을 구현합니다.
// MMPlayerCharacter Header
protected:
	UPROPERTY(VisibleAnywhere, Category = CommonInput, Meta = (AllowPrivateAccess = "true"))
	TObjectPtr<class UInputAction> IA_ConvertWeapon;
    
    // 무기 스왑용 매핑 함수
    void ConvertWeapon();
    
    // 무기 장착 함수
	void DrawWeapon();
	void DrawEnd(class UAnimMontage* Montage, bool IsEnded);
    
    // 무기 해제 함수
	void SheatheWeapon();
	void SheatheEnd(class UAnimMontage* Montage, bool IsEnded);
    
	UPROPERTY(EditAnywhere, Category = Montage, Meta = (AllowPrivateAccess = "true"))
	TMap<EClassType, TObjectPtr<class UAnimMontage>> DrawMontage;

	UPROPERTY(EditAnywhere, Category = Montage, Meta = (AllowPrivateAccess = "true"))
	TMap<EClassType, TObjectPtr<class UAnimMontage>> SheatheMontage;
// MMPlayerCharacter Cpp
void AMMPlayerCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);

	UEnhancedInputComponent* EnhancedInputComponent = CastChecked<UEnhancedInputComponent>(PlayerInputComponent);

	...
    
	EnhancedInputComponent->BindAction(IA_ConvertWeapon, ETriggerEvent::Triggered, this, &AMMPlayerCharacter::ConvertWeapon);
}

void AMMPlayerCharacter::ConvertWeapon()
{
	if (bIsChange) return;

	if (bIsEquip)
	{
		SheatheWeapon();
	}
	else
	{
		DrawWeapon();
	}
}

void AMMPlayerCharacter::DrawWeapon()
{
	if (CurrentWeapon)
	{
		bIsChange = true;

		UAnimInstance* AnimInstance = GetMesh()->GetAnimInstance();
		if (AnimInstance)
		{
			// 몽타주 재생
			AnimInstance->Montage_Play(DrawMontage[ClassType]);

			// 몽타주 재생 종료 바인딩
			FOnMontageEnded EndDelegate;
			EndDelegate.BindUObject(this, &AMMPlayerCharacter::DrawEnd);

			// DrawMontage가 종료되면 EndDelegate에 연동된 DrawEnd함수 호출
			AnimInstance->Montage_SetEndDelegate(EndDelegate, DrawMontage[ClassType]);
		}
	}
}

void AMMPlayerCharacter::DrawEnd(UAnimMontage* Montage, bool IsEnded)
{
	bIsChange = false;
	bIsEquip = true;
}

void AMMPlayerCharacter::SheatheWeapon()
{
	if (CurrentWeapon)
	{
		bIsChange = true;

		UAnimInstance* AnimInstance = GetMesh()->GetAnimInstance();
		if (AnimInstance)
		{
			// 몽타주 재생
			AnimInstance->Montage_Play(SheatheMontage[ClassType]);

			// 몽타주 재생 종료 바인딩
			FOnMontageEnded EndDelegate;
			EndDelegate.BindUObject(this, &AMMPlayerCharacter::SheatheEnd);

			// SheatheMontage가 종료되면 EndDelegate에 연동된 SheatheEnd함수 호출
			AnimInstance->Montage_SetEndDelegate(EndDelegate, SheatheMontage[ClassType]);
		}
	}
}

void AMMPlayerCharacter::SheatheEnd(UAnimMontage* Montage, bool IsEnded)
{
	bIsChange = false;
	bIsEquip = false;
}

몽타주를 추가하고, AnimNotify 추가하기

  • 플레이어의 등에 부착된 무기를 Draw하며 손 위치로 이동시켜줘야 합니다.
  • 전사 Draw/Sheathe Montage를 추가하고 AnimNotify를 통해 무기의 부착 위치를 변경시켜주도록 하겠습니다.
  • AnimNotify에서 플레이어의 무기에 접근해야 합니다.
  • 플레이어의 무기를 반환하기 위한 인터페이스를 추가해주도록 합니다.
// MMAnimationWeaponInterface Header
UINTERFACE(MinimalAPI)
class UMMAnimationWeaponInterface : public UInterface
{
	GENERATED_BODY()
};

/**
 * 
 */
class MYSTICMAZE_API IMMAnimationWeaponInterface
{
	GENERATED_BODY()

	// Add interface functions to this class. This is the class that will be inherited to implement this interface.
public:
	virtual class AMMWeapon* GetWeapon() = 0;
};
// MMPlayerCharacter Header

// Weapon Section
protected:
	FORCEINLINE virtual class AMMWeapon* GetWeapon() override { return CurrentWeapon; }
  • 무기 장착 몽타주에서 사용할 장착 노티파이, 장착 해제 노티파이 클래스를 생성해주도록 합니다.
// AnimNotify_MMEquipWeapon Cpp
#include "Animation/AnimNotify_MMEquipWeapon.h"
#include "Interface/MMAnimationWeaponInterface.h"
#include "Item/MMWeapon.h"

void UAnimNotify_MMEquipWeapon::Notify(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, const FAnimNotifyEventReference& EventReference)
{
	Super::Notify(MeshComp, Animation, EventReference);

	if (MeshComp)
	{
		// 무기 장착
		IMMAnimationWeaponInterface* WeaponPawn = Cast<IMMAnimationWeaponInterface>(MeshComp->GetOwner());
		if (WeaponPawn)
		{
			WeaponPawn->GetWeapon()->DrawWeapon(MeshComp);
		}
	}
}
// AnimNotify_MMUnEquipWeapon Cpp
#include "Animation/AnimNotify_MMUnEquipWeapon.h"
#include "Interface/MMAnimationWeaponInterface.h"
#include "Item/MMWeapon.h"

void UAnimNotify_MMUnEquipWeapon::Notify(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, const FAnimNotifyEventReference& EventReference)
{
	Super::Notify(MeshComp, Animation, EventReference);

	if (MeshComp)
	{
		// 무기 장착 해제
		IMMAnimationWeaponInterface* WeaponPawn = Cast<IMMAnimationWeaponInterface>(MeshComp->GetOwner());
		if (WeaponPawn)
		{
			WeaponPawn->GetWeapon()->SheatheWeapon(MeshComp);
		}
	}
}

제작한 AnimNotify를 사용해 2개의 몽타주(장착/장착해제)를 생성합니다.



결과

profile
클라이언트 프로그래머 지망생

0개의 댓글