언리얼 엔진 5/개발 일지

[UE5] 블루아카이브 TPS게임 개발일지 (43) - 피격 구현

ciel45 2024. 1. 20. 11:56

지금까지 칸나가 총을 맞아도 딱히 반응이 없었다.

이제부터는 실제로 체력이 깎이고, 피격 애니메이션도 재생하도록 할 것이다.

 

 

KannaCharacter 클래스에도 적에게 달아주었던 UAttributeComponent를 달아주고, 피격 애니메이션에 해당하는 HitMontage와 StunMontage를 추가하였다.

	// KannaCharacter.h 중 일부
    
    UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
	UAttributeComponent* Attributes;
    
    UPROPERTY(EditDefaultsOnly, Category = Montages)
	UAnimMontage* HitMontage;
    
    UPROPERTY(EditDefaultsOnly, Category = Montages)
	UAnimMontage* StunMontage;

 

StunMontage는 HP가 30 이하로 내려가는 데미지를 입었을 때, HitMontage는 그 외 상황에 재생할 것이다.

 

 

 

StunMontage는 믹사모에서 다운받았고, HitMontage는 언리얼 엔진의 AnimStarterPack에 있는 것을 애니메이션 리타게팅으로 가져왔다.

StunMontage
HitMontage. 4개의 섹션 중 하나가 랜덤으로 재생되도록 할 것이다.

 

 

 

이제 데미지를 입으면 Attributes에서는 HP가 깎아주고, 깎이고 남은 HP에 따라 HitMontage 또는 StunMontage를 재생할 것이다.

 

 

 

덤으로 피격 효과음도 전에 다운받았던 MilitaryDarkWeapon 에셋에 있는걸 준비하였다.

사운드 큐로 만들어져 있었다.

 

이를 사용할 수 있도록 KannaCharacter.h에 변수를 추가해주었다.

	UPROPERTY(EditDefaultsOnly, Category = Sound)
	USoundBase* BulletHitSound;

그리고 블루프린트의 디테일 패널에서 에셋을 할당해주었다.

 

 

애니메이션을 재생하고, 피격 효과음을 재생하기 위해서는 TakeDamage함수를 손볼 것이다.

변경 결과는 다음과 같다.

float AKannaCharacter::TakeDamage(float DamageAmount, FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCauser)													
{
	if (Attributes)
	{
		Attributes->ReceiveDamage(DamageAmount);
 
		if (Attributes->GetCurrentHealth() < 30.f)
		{
			UE_LOG(LogTemp, Log, TEXT("체력 위험, 스턴"));
			UAnimInstance* AnimInstance = GetMesh()->GetAnimInstance();
			if (AnimInstance && StunMontage)
			{
				DisableMovement();

				// 타이머 이용해서 1초 후 스턴 풀리도록
				FTimerHandle StunTimerHandle;
				GetWorldTimerManager().SetTimer(StunTimerHandle, this, &AKannaCharacter::EnableMovement, 1.f);

				if (ActionState != EActionState::EAS_Stunned)
				{
					AnimInstance->Montage_Play(StunMontage);
					ActionState = EActionState::EAS_Stunned;
				}
			}
		}
		else
		{
			PlayHitMontage();
		}
	}

	if (BulletHitSound) //피격 효과음
	{
		UGameplayStatics::PlaySound2D(GetWorld(), BulletHitSound);
	}

	return DamageAmount;
}

TakeDamage는 UGameplayStatics::ApplyDamage()에 의해 호출되는데, 이 ApplyDamage는 적들이 쏜 총알인 Projectile.cpp이 칸나와 충돌했을 때 호출한다. 자세한 내용은 다음 포스팅에 담을 것이다.

 

체력의 30% 아래로 내려가는 공격을 받으면 StunMontage가 재생되고, DisableMovement 함수를 통해 캐릭터를 움직일 수 없게된다. 그리고 타이머를 통해 1초 후엔 EnableMovement 함수가 호출되어 다시 캐릭터를 움직일 수 있게 된다.

 

 

Disable/EnableMovement 함수의 내용은 Controller->SetIgnoreMoveInput(false); 뿐이다. 따라서 포스팅에 별도로 적진 않으려고 한다.

 

 

ActionState에 EAS_Stunned를 새로 만들었는데, 스턴 애니메이션이 중복으로 재생되는 것을 막기 위해서이다. 또한 스턴을 별도의 스테이트로 분리하는 것이 논리적으로 더 나을 것 같다는 생각이 들었다.

 

 

 

HitMontage를 재생하는 부분은 PlayHitMontage 함수를 호출하는 것 하나로 끝낸 것을 볼 수 있다.

PlayHitMontage의 내용은 다음과 같다.

void AKannaCharacter::PlayHitMontage()
{
	if (HitMontageSections.Num() <= 0) return;
	const int32 MaxIndex = HitMontageSections.Num() - 1;
	const int32 Index = FMath::RandRange(0, MaxIndex);

	if (HitMontage)
	{
		PlayMontageBySection(HitMontage, HitMontageSections[Index]);
	}
}

// PlayHitMontage에서 호출하는 PlayMontageBySection 함수.
void AKannaCharacter::PlayMontageBySection(UAnimMontage* Montage, const FName& SectionName)
{
	UAnimInstance* AnimInstance = GetMesh()->GetAnimInstance();

	if (AnimInstance && Montage)
	{
		AnimInstance->Montage_Play(Montage);
		AnimInstance->Montage_JumpToSection(SectionName, Montage);
	}
}

 

이전의 AttackMontage와는 다른 방식을 사용한 것을 볼 수 있다. 새로운 섹션을 추가하거나 뺄 때마다 코드를 바꿔야하는 불편함을 줄이고, 그 대신 섹션 목록을 블루프린트에서 관리하기 위해서이다.

 

HitMontageSections는 FName을 담는 TArray 배열이다.

	UPROPERTY(EditAnywhere, Category = Montages)
	TArray<FName> AttackMontageSections; // 이참에 AttackMontage도 방식을 바꿨다.

	UPROPERTY(EditAnywhere, Category = Montages)
	TArray<FName> HitMontageSections;

다시 위의 코드로 돌아가보자면, 배열의 범위 안에서 난수를 발생시켜, 그것을 인덱스로 FName을 받아와 그 FName을 섹션의 이름으로하여 PlayMontageBySection에서 사용하고 있는 것을 볼 수 있다.

 

코드를 수정해야하는 불편함을 블루프린트에서 배열을 수정하는 것으로 해결할 수 있도록 완화해준 것이다.

(UPROPERTY의 EditAnywhere 지정자를 통해서)

우측의 디테일 패널에서 배열의 내용을 볼 수 있다. 손쉽게 섹션명들을 관리할 수 있다.

피격 애니메이션이 잘 나오고, 소리도 잘 나왔다.

 

피격 방향 표시기는 사실 다음 포스팅 내용인데, 미리 만들어둔 것이다.

 

다음 포스팅은 피격 방향 표시기의 구현에 대해 작성할 것이다.