Unreal5 Attack Animation C++

Unreal5 Attack Animation C++

저번 글에서 무기 클래를 생성하고, 장착했습니다.

이번 글에서는 공격 애니메이션을 추가해보려고 합니다.

(이전코드에서 바뀐부분만 추가합니다.)

[프로그래밍/언리얼] - Unreal5 무기 생성 및 장착 C++

  • 애니메이션 구하기.
  • 애니메이션 몽타주 생성 및 설정.
  • 캐릭터에 적용 및 중간결과.
  • Enum을 사용해 문제점 수정.
  • 결과.

애니메이션 구하기.

마켓플레이스, Mixamo 등의 사이트에서 가져오거나 애니메이션을 만들어줍니다.

https://www.mixamo.com/

 

Mixamo

 

www.mixamo.com

가져온 애니메이션을 현재 캐릭터에 맞게 리타겟팅 해줍니다.

사실 이 부분이 제일 오래 걸렸습니다.

한손검 애니메이션 마음에 드는 것이 있어서 가져왔는데 그 스켈레톤에 root대신 bip001이 있어서 그런지 Root motion이 안돼서 한손검 Root motion은 포기하고, Root motion은 쌍칼 애니메이션에만 추가했습니다.

(이 글에는 쌍칼 안 나옴.)

애니메이션 몽타주.

언리얼 엔진에서 Animation montage를 추가해줍니다.

Animation montage에서 Montage Section을 추가합니다.

아래 글에서 Attack1, Attac2이런게 Montage Section입니다.

Montage Section창에서 Clear를 눌러 Section을 분리? 해줍니다.

이렇게 해주면 공격버튼 누름-> Attak_N이 가능해집니다.

애니메이션도 넣고 Section지정도 끝났으니, 이제 이 Animation montage를 실행시키는 코드를 넣으러 가보겠습니다.

SlayerCharacter.h에 InputAction과 Attack함수, Animation montage를 선언해줍니다.

/*Attack*/
class UAnimMontage;

protected:
	//Attack
	UPROPERTY(EditAnywhere, Category = Input)
	UInputAction* AttackAction;
	void Attack();
	void PlayAttackMontage();
private:
	//Attack
	/*Animation montages*/
	UPROPERTY(EditDefaultsOnly, Category = Montages)
	UAnimMontage* AttackMontage;

SlayerCharacter.cpp에서 Attack을 구현해줍니다.

일단은 그냥 확인하려고 Random과 Swich문으로 때려박았습니다.

/*Attack*/
void ASlayerCharacter::Attack()
{
	PlayAttackMontage();
}
void ASlayerCharacter::PlayAttackMontage()
{
	//#include "Animation/AnimInstance.h"
	UAnimInstance* AnimInstance = GetMesh()->GetAnimInstance();
	if (AnimInstance && AttackMontage) {
		AnimInstance->Montage_Play(AttackMontage);
		const int32 Selection = FMath::RandRange(0,AttackMontage->GetNumSections()-1);
		//GEngine->AddOnScreenDebugMessage(1, 2.f, FColor::Cyan, FString::Printf(TEXT("GetSection: %d"), Selection));
		FName SectionName = FName();
		switch (Selection) {
		case 0:
			SectionName = FName("Attack1");
			break;
		case 1:
			SectionName = FName("Attack2");
			break;
		case 2:
			SectionName = FName("Attack3");
			break;
		case 3:
			SectionName = FName("Attack4");
			break;
		case 4:
			SectionName = FName("Attack5");
			break;
		case 5:
			SectionName = FName("Attack6");
			break;
		case 6:
			SectionName = FName("Attack7");
			break;
		case 7:
			SectionName = FName("Attack8");
			break;
		case 8:
			SectionName = FName("Attack9");
			break;
		default:
			break;
		}
		//GEngine->AddOnScreenDebugMessage(2, 2.f, FColor::Red, SectionName.ToString());
		AnimInstance->Montage_JumpToSection(SectionName, AttackMontage);
	}
}
// Called to bind functionality to input
void ASlayerCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);
	//#include "EnhancedInputComponent.h"추가.
	if (UEnhancedInputComponent* EnhancedInputComponent = CastChecked<UEnhancedInputComponent>(PlayerInputComponent)) {
		EnhancedInputComponent->BindAction(MovementAction, ETriggerEvent::Triggered, this, &ASlayerCharacter::Move);
		EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Triggered, this, &ASlayerCharacter::Jump);
		EnhancedInputComponent->BindAction(LookAction, ETriggerEvent::Triggered, this, &ASlayerCharacter::Look);
		EnhancedInputComponent->BindAction(EkeyAction, ETriggerEvent::Triggered, this, &ASlayerCharacter::EkeyPress);
		EnhancedInputComponent->BindAction(AttackAction, ETriggerEvent::Triggered, this, &ASlayerCharacter::Attack);
	}
}

 

캐릭터에 적용 및 중간결과.

ABP_yin으로 가서 Slot을 추가합니다.

이게 있어야 animation montage를 사용할 수 있습니다.

BP_SlayerCharacter에 가서 Attack InputAction과 Attack Montage를 추가해줍니다.

Attack Input은 Context에도 Mapping하셔야 합니다.

애니메이션은 잘 나옵니다.

그런데 문제점이 있네요.

마우스를 공격중에 공격을 누르면 이전 동작을 무시합니다.

애니메이션 도중에 움직여집니다.

무기를 장착하지 않아도 공격이 됩니다.

Enum을 사용해 문제점 수정.

Visual studio에 가서 헤더파일을 추가하겠습니다.

이름은 CharacterTypes.h 경로는 public/Character으로 하겠습니다.

Enum타입으로 ECharacterState와 EActionState를 만들었습니다.

무기를 장착했는지, 공격 중인지를 표시합니다.

//CharacterType.h
#pragma once
enum class ECharacterState:uint8 {
	ECS_Unequipped,
	ECS_EquippedOneHandedWeapon,
	ECS_EquippedTwoHandedWeapon
};
enum class EActionState:uint8
{
	EAS_Unoccupied,
	EAS_Attacking
};

SlayerChacacter에서 방금 선언한 Enum을 사용해야 하니 SlayerCharacter.h에 #include "CharacterTypes.h"를 추가하고 아래 코드를 추가합니다.

public에 선언한 SetAttackEndState()함수는 AnimInstance에서 공격의 끝을 알리는 notify가 발생하면 SlayerCharacter에 Private로 선언된 ActionState를 기본값으로 바꿔줄 겁니다.

private:
	//Attack State
	//#include "CharacterTypes.h"
	ECharacterState CharacterState = ECharacterState::ECS_Unequipped;
	//UPROPERTY(BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
	EActionState ActionState = EActionState::EAS_Unoccupied;
public:
	FORCEINLINE EActionState SetAttackEndState(){ return ActionState = EActionState::EAS_Unoccupied; }

이제 SlayerCharacter에 코드를 추가해주겠습니다.

void ASlayerCharacter::Move(const FInputActionValue& value)
{
	//공격중일때 Move작동하지 않도록.
	if (ActionState != EActionState::EAS_Unoccupied) return;
	const FVector2D MovementVector = value.Get<FVector2D>();
	const FRotator Rotation = Controller->GetControlRotation();
	const FRotator YawRotation(0.f, Rotation.Yaw, 0.f);
	const FVector ForwardDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
	AddMovementInput(ForwardDirection, MovementVector.Y);//이동
	const FVector RightDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);
	AddMovementInput(RightDirection, MovementVector.X);//이동
}
void ASlayerCharacter::EkeyPress()
{
	/*Equip*/
	//#include "Item/Weapons/Weapon.h"
	if (AWeapon* Weapon = Cast<AWeapon>(OverlappingItem)) {
		Weapon->Equip(GetMesh(),FName("RightHandSocket"));
		OverlappingItem = nullptr;
		//무기장착.
		CharacterState = ECharacterState::ECS_EquippedOneHandedWeapon;
	}
}

/*Attack*/
void ASlayerCharacter::Attack()
{
	//공격중이 아니고, 무기 장착중.
	if (ActionState == EActionState::EAS_Unoccupied && CharacterState != ECharacterState::ECS_Unequipped) {
		PlayAttackMontage();
		ActionState = EActionState::EAS_Attacking;
	}
}

AnimInstance에서 AttackEnd라는 Notify가 들어오면 SlayerCharacter에 Public으로 선언한 SetAttackEndState()를 실행합니다.

//SlayerAnimInstance.cpp
/*AnimNotify_{Notify Name}(){}*/
void USlayerAnimInstance::AnimNotify_AttackEnd()
{
	SlayerCharacter->SetAttackEndState();
}

마지막으로 montage로 가서 Notify를 생성합니다.

위 함수명에서 보셨다시피 AttackEnd로 Notify를 추가했습니다.

결과.

열심히 마우스 버튼 누르고 있는겁니다.

잘 되네요.

Designed by JB FACTORY