적에게 데미지를 주는 기능을 만들었으니, 체력이 0이 되면 죽게 하도록 할 것이다.
사망 애니메이션을 따로 준비하지 않고, 래그돌(Ragdoll) 효과를 사용할 것이다.
혹시 래그돌이라는 단어가 익숙치 않다면, 캐릭터가 힘빠진 듯이 흐느적거리면서 죽는 그것이라고 생각하면 된다.
작업에 앞서, 래그돌을 적용할 캐릭터의 스켈레탈 메시가 Physics Asset을 가지고 있는지 확인해주어야 한다.
스켈레탈 메시를 정상적으로 임포트했다면 자동으로 추가되어있을 것이다.
상단 우측의 Simulate 버튼을 눌러 래그돌을 바로 시험해볼 수도 있다.
우선 Enemy 클래스가 소유하고 있는 Attribute 클래스에 IsDead 함수를 추가하였다. 말 그대로 죽었는지 알려주는 함수이다.
FORCEINLINE bool IsDead() { return CurrentHealth == 0; }
(AttributeComponent.h의 일부. 간단한 함수이므로 인라인 함수로 만들었다.)
그리고 Enemy 클래스의 TakeDamage 함수에서 체력을 깎은 후 IsDead를 호출하여 사망 여부를 확인 후, 사망하였다면 Die 함수를 호출하도록 했다.
float AEnemy::TakeDamage(float DamageAmount, FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCauser)
{
if (Attributes)
{
Attributes->ReceiveDamage(DamageAmount);
if (Attributes->IsDead())
Die(DamageEvent);
}
UE_LOG(LogTemp, Warning, TEXT("Enemy HP: %f"), Attributes->GetCurrentHealth())
return DamageAmount;
}
Die함수의 매개변수로 DamageEvent를 넣고 있는데, 이는 DamageEvent를 안에서 사용할 것이기 때문이다. 자세한 내용은 곧 뒤에서 이어진다.
Die 함수에서는 실제로 적이 움직임을 멈추고 래그돌 상태가 되도록 한다.
void AEnemy::Die(FDamageEvent const& DamageEvent)
{
// 캡슐 콜라이더 비활성화
UCapsuleComponent* Capsule = GetCapsuleComponent();
Capsule->SetCollisionEnabled(ECollisionEnabled::NoCollision);
Capsule->SetCollisionResponseToAllChannels(ECR_Ignore);
GetMesh()->SetCollisionProfileName(TEXT("Ragdoll"));
SetActorEnableCollision(true);
GetMesh()->SetAllBodiesSimulatePhysics(true);
GetMesh()->SetSimulatePhysics(true); // 래그돌을 활성화하는 핵심 함수이다. 사실 이것 하나만 있어도 되긴 한다.
GetMesh()->WakeAllRigidBodies();
GetMesh()->bBlendPhysics = true;
//Movement Component 비활성화
UCharacterMovementComponent* MovementComp = Cast<UCharacterMovementComponent>(GetMovementComponent());
if (MovementComp)
{
MovementComp->StopMovementImmediately();
MovementComp->DisableMovement();
MovementComp->SetComponentTickEnabled(false);
}
//30초 뒤에 시체 사라짐
SetLifeSpan(30.f);
// 총알의 충격을 받는 연출
if (DamageEvent.IsOfType(FPointDamageEvent::ClassID))
{
FPointDamageEvent PointDmg = *((FPointDamageEvent*)(&DamageEvent));
{
GetMesh()->AddImpulseAtLocation(PointDmg.ShotDirection * 10000, PointDmg.HitInfo.ImpactPoint, PointDmg.HitInfo.BoneName);
UE_LOG(LogTemp, Warning, TEXT("Hit Point: %s"), *(PointDmg.HitInfo.BoneName.ToString()));
}
}
}
주석으로 달려있듯이, 래그돌을 재생하는 역할을 맡고있는 핵심 코드는 GetMesh()->SetSimulatePhysics(true); 이다.
이외 GetMesh()의 멤버함수 호출은 단지 래그돌의 사용을 확실하게 하기 위한 것이다.
캡슐 콜라이더를 비활성화하는 이유는, 캡슐 콜라이더가 Physics Asset을 감싸고 있으면 캐릭터가 땅바닥에 자연스럽게 널부러질 수 없기 때문이다.
함수 마지막 부분을 보면, 총을 맞은 방향에 따라 해당 방향으로 충격이 가해지면서 죽도록 하였다. ImpactPoint과 BoneName(맞은 뼈)는 FPointDamageEvent의 HitInfo에, ShotDirection은 FPointDamageEvent 객체 바로 안에 들어있다.
FPointDamageEvent는 지난 시간에 포스팅했던 TakeDamage 함수의 두번째 파라미터인 FDamageEvent로 올 수 있는 것 중 하나이다. 말 그대로 한 포인트에 공격을 입는 경우에 사용하는, 각종 여러 정보를 담고 있는 클래스이다.
이를 이용하여 AddImpulseAtLocation 함수를 통해 맞은 부위에 충격이 가해지는 연출을 넣을 수 있었다.
또한 이를 위해서 파라미터로 DamageEvent를 넣어주어야 했으며, 그 타입은 PointDamageEvent여야 했다.
타입이 PointDamageEvent이기 위해서는, 라인트레이싱으로 공격하는 함수, 즉 Pistol.cpp의 Fire 함수 내부 UGameplayStatics::ApplyDamage를 UGameplayStatics::ApplyPointDamage로 바꿔주어야 했다.
void APistol::Fire(FVector& StartPoint, FVector& Direction)
{
FHitResult HitResult;
FVector EndPoint = StartPoint + Direction * Range;
if (GetWorld())
{
if (GetWorld()->LineTraceSingleByChannel(
HitResult,
StartPoint,
EndPoint,
ECollisionChannel::ECC_Visibility))
{
if (HitResult.GetActor())
{
if (IHitInterface* HitObject = Cast<IHitInterface>(HitResult.GetActor()))
{
HitObject->GetHit();
}
UGameplayStatics::ApplyPointDamage(
HitResult.GetActor(),
Damage,
Direction,
HitResult,
GetInstigator()->GetController(),
this,
UDamageType::StaticClass()
);
}
}
}
ApplyPointDamage를 이용하므로써 Enemy.cpp의 TakeDamage의 2번째 인자로 PointDamageEvent가 들어가, Die 함수로 또 그것을 전달하여 사용할 수 있었던 것이다.
참고로 함수의 시그니쳐는 다음과 같다.
static float ApplyPointDamage
(
AActor * DamagedActor,
float BaseDamage,
const FVector & HitFromDirection,
const FHitResult & HitInfo,
AController * EventInstigator,
AActor * DamageCauser,
TSubclassOf< class UDamageType > DamageTypeClass
)
이렇게 코드를 모두 작성하였다.
그리고 1차 테스트를 진행하였는데, 적이 죽는 모습이 좀 이상했다.
이에 대한 해결은 포스팅을 나눠서 바로 다음 포스팅에 작성할 것이다.
'언리얼 엔진 5 > 개발 일지' 카테고리의 다른 글
[UE5] 블루아카이브 TPS게임 개발일지 (17) - 적 무기 모델링 임포트 (0) | 2023.12.26 |
---|---|
[UE5] 블루아카이브 TPS게임 개발일지 (16) - 적 사망 연출 2 (0) | 2023.12.25 |
[UE5] 블루아카이브 TPS게임 개발일지 (14) - 데미지 판정 구현 (0) | 2023.12.25 |
[UE5] 블루아카이브 TPS게임 개발일지 (13) - HP 시스템 제작 + SRP (2) | 2023.12.23 |
[UE5] 블루아카이브 TPS게임 개발일지 (12) - 크로스헤어 제작 (0) | 2023.12.21 |