개요
이번에 팀원들과 사이드 프로젝트로 플레이어와 적의 피격을 구현하기 위해 데미지를 받는 Actor의 TakeDamage를 호출하여 구현하였는데 뭔가 임시로 한 느낌이라 실제로 언리얼에서는 어떤 구조로 Damage를 주고 받는 것을 권장하는지 살펴봤다.
TakeDamage 호출
데미지를 받는 로직을 추가하기 위해 언리얼에서는 TakeDamage 함수를 오버라이드하길 권장하고 있다. TakeDamge는 AAactor에 선언되어있다. FDamageEvent는 데미지에 관한 어떤 정보를 넘겨야되나보다 정도의 감만 왔다. FDamageEvent에 대해서는 이따가 설명,,,
TakeDamage 시그니처
float TakeDamage(float DamageAmount, FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCauser);
먼저 AActor에서의 TakeDamage의 정의의 일부분을 보면 다음을 알 수 있다.
- FPointDamageEvent, FRadiusDamageEvent, AnyDamageEvent에 따라 나눔
- InternalTake[ ]Damage()로 Actor에서 내부적으로 데미지 계산
- ReceivePointDamage(), OnTakePointDamage.BroadCast()로 데미지 받았을때 호출하고 싶은 함수들 호출
- PrimComp->ReceiveComponent로 물리적인 처리
float AActor::TakeDamage(float DamageAmount, FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCauser)
{
float ActualDamage = DamageAmount;
UDamageType const* const DamageTypeCDO = DamageEvent.DamageTypeClass ? DamageEvent.DamageTypeClass->GetDefaultObject<UDamageType>() : GetDefault<UDamageType>();
if (DamageEvent.IsOfType(FPointDamageEvent::ClassID))
{
// point damage event, pass off to helper function
FPointDamageEvent* const PointDamageEvent = (FPointDamageEvent*) &DamageEvent;
ActualDamage = InternalTakePointDamage(ActualDamage, *PointDamageEvent, EventInstigator, DamageCauser);
// K2 notification for this actor
if (ActualDamage != 0.f)
{
ReceivePointDamage(ActualDamage, DamageTypeCDO, PointDamageEvent->HitInfo.ImpactPoint, PointDamageEvent->HitInfo.ImpactNormal, PointDamageEvent->HitInfo.Component.Get(), PointDamageEvent->HitInfo.BoneName, PointDamageEvent->ShotDirection, EventInstigator, DamageCauser, PointDamageEvent->HitInfo);
OnTakePointDamage.Broadcast(this, ActualDamage, EventInstigator, PointDamageEvent->HitInfo.ImpactPoint, PointDamageEvent->HitInfo.Component.Get(), PointDamageEvent->HitInfo.BoneName, PointDamageEvent->ShotDirection, DamageTypeCDO, DamageCauser);
// Notify the component
UPrimitiveComponent* const PrimComp = PointDamageEvent->HitInfo.Component.Get();
if (PrimComp)
{
PrimComp->ReceiveComponentDamage(DamageAmount, DamageEvent, EventInstigator, DamageCauser);
}
}
}
return ActualDamage;
}
여기서 ReceivePointDamage의 시그니처를 보면 BlueprintImplementableEvent 인것을 볼 수 있다.
이는 블루프린트에서 함수를 정의하라는 뜻으로 C++에서는 함수의 정의가 있으면 안된다!
블루프린트에서 데미지를 받았을때 처리를 추가하기 위해 있다.
UFUNCTION(BlueprintImplementableEvent, BlueprintAuthorityOnly, meta=(DisplayName = "PointDamage"), Category="Game|Damage")
ENGINE_API void ReceivePointDamage(float Damage, const class UDamageType* DamageType, FVector HitLocation, FVector HitNormal, class UPrimitiveComponent* HitComponent, FName BoneName, FVector ShotFromDirection, class AController* InstigatedBy, AActor* DamageCauser, const FHitResult& HitInfo);
OnTakePointDamage의 경우 블루프린트에서 해당 델리게이트에 함수를 등록할 수 있지만
c++에서도 등록가능하다.
UPROPERTY(BlueprintAssignable, Category="Game|Damage")
FTakePointDamageSignature OnTakePointDamage;
ApplyDamage
상대 Actor의 TakeDamage를 직접 호출해도 되지만 UGameplayStatic::ApplyDamage를 호출해서 데미지를 처리할 수도 있다.
공격에서 TakeDamage를 직접 호출하는 경우
void UDamageComponent::LineAttack()
{
FHitResult hit;
FCollisionQueryParams Params;
Params.AddIgnoredActor(GetOwner());
FVector SceenCenterLoc;
FVector SceenCenterDir;
GetScreenCenterWorldLocationAndWorldDirection(SceenCenterLoc, SceenCenterDir);
FVector Start = GetOwner()->GetActorLocation();
FVector End = Start + (FVector(SceenCenterDir.X, SceenCenterDir.Y, 0.f) * 1000.f);
bool bResult = GetWorld()->LineTraceSingleByChannel(
hit,
Start,
End,
ECollisionChannel::ECC_GameTraceChannel4,
Params
);
if (bResult)
{
auto* OtherActor = hit.GetActor();
if (Cast<ADamageBox>(OtherActor))
{
FDamageEvent DamageEvent;
AActor* EventCauser = GetOwner();
AController* EventInstigator = Cast<APawn>(EventCauser)->GetController();
OtherActor->TakeDamage(10, DamageEvent, EventInstigator, EventCauser);
}
}
DrawDebugLine(GetWorld(), Start, End, bResult ? FColor::Green : FColor::Red, false, 2.f);
}
여기서 ApplyDamage로 바꾸고 싶으면 TakeDamage 대신 ApplyDamage, ApplyPointDamage, ApplyRadiusDamage를 호출하면 된다.
UGameplayStatics::ApplyDamage(OtherActor, 10.f, EventInstigator, EventCauser, DamageEvent.DamageTypeClass);
UGameplayStatics::ApplyPointDamage(OtherActor, 10.f, Start, hit, EventInstigator, EventCauser, DamageEvent.DamageTypeClass);
Reference
https://www.unrealengine.com/ko/blog/damage-in-ue4
https://mingyu0403.tistory.com/258
[Unreal BP & C++] 엔진에서 제공하는 데미지 프레임워크
참고 문서 www.unrealengine.com/ko/blog/damage-in-ue4 blog.naver.com/destiny9720/220914505079 데미지 프레임워크에 대해서 유니티에는 데미지 프레임워크가 없어서 내 맘대로 구조체를 만들어서 사용했었다. ㅜㅜ
mingyu0403.tistory.com
'Unreal' 카테고리의 다른 글
[Unreal C++]1주차 강의 정리 (0) | 2025.01.20 |
---|---|
[Unreal C++] UFUNCTION (0) | 2025.01.20 |
[Unreal] Collision 채널 (1) | 2025.01.08 |
[Unreal C++] 객체 Class 타입 비교하기 (1) | 2025.01.07 |
[Unreal C++]Unreal 코딩 표준 (0) | 2025.01.06 |