지금까지 칸나가 총을 맞아도 딱히 반응이 없었다.
이제부터는 실제로 체력이 깎이고, 피격 애니메이션도 재생하도록 할 것이다.
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에 있는 것을 애니메이션 리타게팅으로 가져왔다.
이제 데미지를 입으면 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 지정자를 통해서)
피격 애니메이션이 잘 나오고, 소리도 잘 나왔다.
피격 방향 표시기는 사실 다음 포스팅 내용인데, 미리 만들어둔 것이다.
다음 포스팅은 피격 방향 표시기의 구현에 대해 작성할 것이다.
'언리얼 엔진 5 > 개발 일지' 카테고리의 다른 글
[UE5] 블루아카이브 TPS게임 개발일지 (45) - 피격 방향 표시기 구현 2 (0) | 2024.01.20 |
---|---|
[UE5] 블루아카이브 TPS게임 개발일지 (44) - 피격 방향 표시기 구현 1 (0) | 2024.01.20 |
[UE5] 블루아카이브 TPS게임 개발일지 (42) - 장전 애니메이션 구현 3 (0) | 2024.01.14 |
[UE5] 블루아카이브 TPS게임 개발일지 (41) - 장전 애니메이션 구현 2 (0) | 2024.01.14 |
[UE5] 블루아카이브 TPS게임 개발일지 (40) - 장전 애니메이션 구현 1 (0) | 2024.01.14 |