오늘부터는 공격시스템을 만들어 볼 것이다. 

일단 공격에 필요한 애니메이션 리소스는 Mixamo에서 가져다 쓰기로 하자 

 

공격용 애니메이션을 이전에 했던 방식과 같이 Retargeting해준다

오늘은 애니메이션 몽타주를 사용하여 여러 공격 애니메이션이 작동하도록 해주려고 한다. 

이때 애니메이션 몽타주(Animation Montage)는 정해진 순서에 따라 실행되는 애니메이션 사이에서 조건에 맞게 애니메이션 재생을 조절하는 기능으로 연속으로 재생해야 하는 여러 애니메이션 중 일부를 반복하거나 길게 재생하거나, 특정 애니메이션의 생략 혹은 조기 종료 등의 처리를 쉽게 해준다.

 

캐릭터를 기반으로 애니메이션 몽타주를 만들어준 뒤 애니메이션들을 원하는 순서대로 배치하고 

몽타주 섹션을 구분해주자

 

그리고 이제 마우스 왼쪽키를 누르면 이 애니메이션이 작동하도록 기존 애니메이션 그래프에 애니메이션 몽타주의 슬롯기능을 활용하여 그래프 흐름에 넣어주고 이를 코드를 통해 사용할 수 있도록 하자

 

일단 키 액션이 동작하도록 액션 변수를 선언해주고 바인딩 함수도 선언해준 뒤 애니메이션 몽타주 변수를 선언해주자 

 

SlashCharacter.h

UPROPERTY(EditAnywhere, Category = Input)
UInputAction* AttackAction;

void AttackPressed();

/**
	애니메이션 몽타주
*/
UPROPERTY(EditDefaultsOnly,Category=Montages)
UAnimMontage* AttackMontage;

 

SlashCharacter.cpp

void ASlashCharacter::AttackPressed()
{
	UAnimInstance* AnimInstance=GetMesh()->GetAnimInstance();
	if (AnimInstance && AttackMontage)
	{
		AnimInstance->Montage_Play(AttackMontage);
		int32 Selection = FMath::RandRange(0, 2);
		FName SectionName = FName();
		switch (Selection)
		{
		case 0:
			SectionName = FName("Attack1");
			break;
		case 1:
			SectionName = FName("Attack2");
			break;
		case 2:
			SectionName = FName("Attack3");
			break;
		default:
			break;
		}
		AnimInstance->Montage_JumpToSection(SectionName, AttackMontage);
	}
}

 

이렇게 한 뒤 에디터를 끄고 컴파일해주고 블루프린트상에 액션과 애니메이션 변수를 넣어주자 

 

이렇게 해주면 다른 공격 애니메이션이 작동하는 것을 볼 수 있다.

 

이때 공격버튼을 여러번 누르면 이상하게 작동하게 되는데 이를 막기위해 enum class를 통해 캐릭터의 state를 관리해주도록 하자.

Characterstate

enum class EActionState :uint8
{
	EAS_Unoccupied UMETA(DisplayName = "Unoccupied"),		
	EAS_Attacking UMETA(DisplayName = "Attacking")
};

SlashCharacter.h

EActionState ActionState = EActionState::EAS_Unoccupied;

 

SlashCharacter.cpp

void ASlashCharacter::AttackPressed()
{
	if (ActionState == EActionState::EAS_Unoccupied) 
	{
		PlayAttackMontage();
		ActionState = EActionState::EAS_Attacking;
	}
	
}

void ASlashCharacter::PlayAttackMontage()
{
	UAnimInstance* AnimInstance = GetMesh()->GetAnimInstance();
	if (AnimInstance && AttackMontage)
	{
		AnimInstance->Montage_Play(AttackMontage);
		const int32 Selection = FMath::RandRange(0, 2);
		FName SectionName = FName();
		switch (Selection)
		{
		case 0:
			SectionName = FName("Attack1");
			break;
		case 1:
			SectionName = FName("Attack2");
			break;
		case 2:
			SectionName = FName("Attack3");
			break;
		default:
			break;
		}
		AnimInstance->Montage_JumpToSection(SectionName, AttackMontage);
		ActionState = EActionState::EAS_Unoccupied;
	}
}

 

그리고 하나의 공격액션이 끝나면 다른 공격액션도 수행할 수 있도록 해주자 

anim notify를 만들어주고 이 이벤트를 통해 state를 초기화해주자

 

SlashCharacter.cpp

void ASlashCharacter::AttackEnd()
{
	ActionState = EActionState::EAS_Unoccupied;
}

 

그리고 무기를 장착하고 있을 때만 이 애니메이션이 나오도록 하자

SlashCharacter.cpp

void ASlashCharacter::AttackPressed()
{
	const bool bCanAttack = 
		ActionState == EActionState::EAS_Unoccupied && 
		CharacterState != ECharacterState::ECS_Unequipped;
	if (bCanAttack) 
	{
		PlayAttackMontage();
		ActionState = EActionState::EAS_Attacking;
	}
	
}

 

이렇게 하면 무기를 장착하고 있을 때만 공격모션이 나오게 된다.

공격모션이 실행될 때 소리가 같이 들리도록 해보자

 

알맞은 사운드를 다운받고 임폴트 해준 뒤에 메타사운드를 통해 사운드를 설정하고 

사운드 노티파이를 통해 재생되게 하면 된다.

 

이와 같은 방법으로 걸을 때와 점프할 때에도 메타사운드를 만들고 이를 추가해주자

 

그리고 지금 서 있을 때의 애니메이션에서 두 발이 너무 붙어있기 때문에 이것을 조금 고쳐주도록하자 이게 너무 붙어있는 이유는 IK_foot 애니메이션에서 IK_foot의 위치에 맞게 발의 위치를 조정하고 있기 때문에 원래 발의 위치를 가져와서

IK_foot의 위치를 조정해주어야한다. 

이 애니메이션이 아래와 같이 블루프린트를 설정해주게 되면 

이렇게 바뀌게 된다.

 

이제 무기를 등 뒤에서 장착하고 장착해제하는 애니메이션을 추가해보자 애니메이션은 Mixamo의 axe에서 가져오자

전에 했던 것과 같이 import후 Retargeting 해준다. 그리고 이를 바탕으로 애니메이션 몽타주를 만들어주자 

이때 Equip와 Unequip의 몽타주 섹션을 지정해줘서 코드로 애니메이션 재생을 컨트롤할 수 있게하자

 

이제 코드를 통해 각각의 애니메이션이 실행되도록하자 이때 무기를 들면 그 무기를 변수에 저장해서 장착해제도 가능하도록 하자.

SlashCharacter.h

	void PlayEquipMontage(FName SectionName);
	bool CanDisarm();
	bool CanArm();
    
	UPROPERTY(VisibleAnywhere, Category = Weapon)
	AWeapon* EquippedWeapon;
    
    UPROPERTY(EditDefaultsOnly, Category = Montages)
	UAnimMontage* EquipMontages;

 

SlashCharacter.cpp

void ASlashCharacter::EKeyPressed()
{
	AWeapon* OverlappingWeapon = Cast<AWeapon>(OverlappingItem);
	if (OverlappingWeapon)
	{
		OverlappingWeapon->Equip(GetMesh(), FName("RightHandSocket"));
		CharacterState = ECharacterState::ECS_EquippedOneHandedWeapon;
		OverlappingItem = nullptr;
		EquippedWeapon = OverlappingWeapon;
	}
	else
	{
		if (CanDisarm())
		{
			PlayEquipMontage(FName("Unequip"));
			CharacterState = ECharacterState::ECS_Unequipped;
		}
		else if (CanArm())
		{
			PlayEquipMontage(FName("Equip"));
			CharacterState = ECharacterState::ECS_EquippedOneHandedWeapon;
		}
	}
}

void ASlashCharacter::PlayEquipMontage(FName SectionName)
{
	UAnimInstance* AnimInstance = GetMesh()->GetAnimInstance();
	if (AnimInstance && EquipMontages)
	{
		AnimInstance->Montage_Play(EquipMontages);
		AnimInstance->Montage_JumpToSection(SectionName, EquipMontages);
	}
}


bool ASlashCharacter::CanDisarm()
{
	return ActionState == EActionState::EAS_Unoccupied && 
		CharacterState != ECharacterState::ECS_Unequipped;
}

bool ASlashCharacter::CanArm()
{
	return ActionState == EActionState::EAS_Unoccupied &&
		CharacterState == ECharacterState::ECS_Unequipped &&
		EquippedWeapon;
}

 

이렇게하면 각각의 애니메이션이 e키를 누를때 실행된다.

 

이제 장착해제하고 장착할 때 등에 넣고 등에서 꺼내올 수 있게 하자 이는 소캣을 활용하여 만들 수 있다.

 

 

SlashCharacter.h

	UFUNCTION(BlueprintCallable)
	void Disarm();
    
    
	UFUNCTION(BlueprintCallable)
	void Arm();


SlashCharacter.cpp

void ASlashCharacter::Disarm()
{
	if (EquippedWeapon)
	{
		EquippedWeapon->AttachMeshToSocket(GetMesh(), FName("SpineSocket"));
	}
}

void ASlashCharacter::Arm()
{
	if (EquippedWeapon)
	{
		EquippedWeapon->AttachMeshToSocket(GetMesh(), FName("RightHandSocket"));
	}
}

 

Weapon.cpp

void AWeapon::AttachMeshToSocket(USceneComponent* Inparent, const FName& InSocketName)
{
	FAttachmentTransformRules TransformRules(EAttachmentRule::SnapToTarget, true);
	ItemMesh->AttachToComponent(Inparent, TransformRules, InSocketName);
}

 

이렇게하고 장착해제 애니메이션 몽타주에 Anim Notify를 추가해준 뒤 이 Notify에서 Disarm 함수를 부르도록 하자

 

이렇게 하면 소캣에 넣고 빼는 것이 가능해진다.

 

그리고 달릴 때 E키를 누르면 달리기를 멈추고 동작을 하도록 바꾸자 

일단 애니메이션 몽타주에 notify를 추가해주고 콜백 함수를 만들어준 뒤 블루프린트에서 이 함수를 호출하도록하자

 

SlashCharacter.h

	UFUNCTION(BlueprintCallable)
	void FinishEquipping();

 

SlashCharacter.cpp

if (CanDisarm())
{
	PlayEquipMontage(FName("Unequip"));
	CharacterState = ECharacterState::ECS_Unequipped;
	ActionState = EActionState::EAS_EquippingWeapon;
}
else if (CanArm())
{
	PlayEquipMontage(FName("Equip"));
	CharacterState = ECharacterState::ECS_EquippedOneHandedWeapon;
	ActionState = EActionState::EAS_EquippingWeapon;
}

void ASlashCharacter::FinishEquipping()
{
	ActionState = EActionState::EAS_Unoccupied;
}

 

 

 

이렇게 해주면 달리는 중에  E키를 누르면 멈추고 행동을 한다.

+ Recent posts