언리얼 엔진의 Actor 클래스에는 데미지를 받는 함수가 기본적으로 정의되어있다.
https://docs.unrealengine.com/5.3/en-US/API/Runtime/Engine/GameFramework/AActor/TakeDamage/
그 시그니쳐는 이러하다.
virtual float TakeDamage
(
float DamageAmount,
struct FDamageEvent const & DamageEvent,
class AController * EventInstigator,
AActor * DamageCauser
)
액터가 데미지를 받게 하고자 한다면, TakeDamage 함수를 오버라이딩하여 사용하면 된다.
파라미터를 하나씩 살펴보자면,
- DamageAmount: 문자 그대로 받는 데미지의 양이다.
- DamageEvent: 데미지에 대한 추가적인 정보를 가지고 있는 구조체이다.
- EventInstigator: 데미지를 준 컨트롤러이다. 캐릭터가 적에게 데미지를 주었다면, 캐릭터의 컨트롤러를 의미한다.
- instigator는 '선동자'라는 의미를 가지고 있다.
- DamageCauser: 데미지를 준 액터 자체이다. 총알이 적에게 박혀 데미지를 주었다면, 그 총알을 의미한다.
그리고 float을 리턴하도록 되어있는데, DamageAmount를 리턴하도록 하는 것이 보통이다.
데미지를 받는 함수가 있다면, 데미지를 주는 함수도 있어야 할 것이다.
UGameplayStatics::ApplyDamage 함수가 바로 TakeDamage가 호출되도록 하는 함수이다.
https://docs.unrealengine.com/5.3/en-US/API/Runtime/Engine/Kismet/UGameplayStatics/ApplyDamage/
static float ApplyDamage
(
AActor * DamagedActor,
float BaseDamage,
AController * EventInstigator,
AActor * DamageCauser,
TSubclassOf< class UDamageType > DamageTypeClass
)
대부분의 파라미터는 TakeDamage에서와 그 의미가 유사하거나, 비슷한 맥락이다.
다만 마지막에 TSubclassOf<class UDamageType>이 들어가는데, 이는 UDamageType을 상속하여 커스터마이징한 데미지타입을 넣어줄 수 있음을 의미한다.
예를 들면, 지속적으로 데미지를 주는 도트데미지 등을 만들 수 있다.
굳이 특별한 데미지타입을 사용할 것이 아니라면 UDamageType::StaticClass()를 넣어주면 된다.
이제 코드를 작성할 것이다.
Pistol.cpp의 Fire함수에서 적을 맞췄을 때 로그만 출력하는 대신 실제로 ApplyDamage를 호출하도록 하였다.
void APistol::Fire(FVector& StartPoint, FVector& Direction)
{
//UE_LOG(LogTemp, Warning, TEXT("PISTOL FIRE")); //로그에 출력
FHitResult HitResult;
FVector EndPoint = StartPoint + Direction * Range;
if (GetWorld())
{
//DrawDebugLine(GetWorld(), StartPoint, EndPoint, FColor::Red, true, -1.f, 0, 0.5f);
if (GetWorld()->LineTraceSingleByChannel(
HitResult,
StartPoint,
EndPoint,
ECollisionChannel::ECC_Visibility))
{
if (HitResult.GetActor())
{
//UE_LOG(LogTemp, Warning, TEXT("%s"), *(HitResult.GetActor())->GetName());
if (IHitInterface* HitObject = Cast<IHitInterface>(HitResult.GetActor()))
{
HitObject->GetHit();
}
UGameplayStatics::ApplyDamage(
HitResult.GetActor(),
Damage,
GetInstigator()->GetController(),
this,
UDamageType::StaticClass()
);
}
}
}
}
중간의 IHitInterface 사용 코드는 추후에 명중의 효과를 표현하기 위한 것으로, 당장은 사용하지 않고 있다. 인터페이스를 사용한 이유는 적 외에 사물을 명중시켰을 때도 각각에 맞는 효과를 표현하기 위해서이다.
현재 눈여겨볼 것은 ApplyDamage 부분이다.
2번째 파라미터인 Damage는 미리 만들어둔 float 타입 인스턴스 변수로, 현재 35.0f가 들어가있다.
3번째 파라미터에서는 GetInstigator()를 사용한 것을 볼 수 있다.
GetInstigator()는 Pawn 포인터를 리턴하는 함수이다.
그러면 Pistol 액터의 Instigator가 언제 Set 되었냐 하면, 캐릭터의 SwitchWeapon 함수 내부에서이다.
void AKannaCharacter::SwitchWeapon()
{
if(ActionState != EActionState::EAS_Neutral) return;
if (CharacterState == ECharacterState::ECS_ArmedWithPistol) // 권총 -> 맨손
{
CharacterState = ECharacterState::ECS_Unarmed;
CurrentWeapon = nullptr;
}
else if (CharacterState == ECharacterState::ECS_Unarmed) //맨손 -> 권총
{
CharacterState = ECharacterState::ECS_ArmedWithPistol;
CurrentWeapon = WeaponList[0];
}
if(CurrentWeapon)
{
CurrentWeapon->SetOwner(this);
CurrentWeapon->SetInstigator(this);
}
SetNeutralStateSpeed(); // 중립 상태일 때의 걷기 속도 조정
}
무기를 장착할 때 Instigator를 캐릭터로 할당해두었다.
다시 위의 ApplyDamage를 보면, 파라미터로 넘겨줄 것은 Controller 포인터이므로 GetController()를 호출해주면 된다.
이제 ApplyDamage 함수가 완성되었으니, Enemy.cpp로 이동하여 TakeDamage 함수를 오버라이딩할 차례이다.
float AEnemy::TakeDamage(float DamageAmount, FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCauser)
{
if (Attributes)
{
Attributes->ReceiveDamage(DamageAmount);
}
UE_LOG(LogTemp, Warning, TEXT("Enemy HP: %f"), Attributes->GetCurrentHealth())
return DamageAmount;
}
앞서 Attribute에 멤버변수로 CurrentHealth, MaxHealth를 만들어두었으므로, 이를 활용하는 함수인 ReceiveDamage()를 만들어 그것을 호출하도록 하였다.
Enemy에서 직접 Attribute->CurrentHealth 등으로 변수에 접근하는 방법도 있지만, 의존성을 낮추기 위하여 별도의 함수를 만든 것이다.
그리고 테스트를 위해 UE_LOG 매크로도 달아두었다.
ReceiveDamage 함수는 다음과 같다.
void UAttributeComponent::ReceiveDamage(float Damage)
{
CurrentHealth = FMath::Clamp(CurrentHealth-Damage, 0.f, 100.f);
}
체력이 마이너스가 되지 않기를 바라므로 Clamp 함수를 사용하였다.
다음은 테스트영상이다. 하단 로그에 주목해보자.
'언리얼 엔진 5 > 개발 일지' 카테고리의 다른 글
[UE5] 블루아카이브 TPS게임 개발일지 (16) - 적 사망 연출 2 (0) | 2023.12.25 |
---|---|
[UE5] 블루아카이브 TPS게임 개발일지 (15) - 적 사망 연출 (0) | 2023.12.25 |
[UE5] 블루아카이브 TPS게임 개발일지 (13) - HP 시스템 제작 + SRP (2) | 2023.12.23 |
[UE5] 블루아카이브 TPS게임 개발일지 (12) - 크로스헤어 제작 (0) | 2023.12.21 |
[UE5] 블루아카이브 TPS게임 개발일지 (11) - 라인 트레이싱 (0) | 2023.12.19 |