[UE5] ApplyDamage

kkado·2024년 4월 9일
0

UE5

목록 보기
33/62
post-thumbnail

언리얼에는 내부적으로 데미지를 줄 수 있는 로직이 존재한다. UGameplayStatics::ApplyDamage 함수를 이용하면 쉽게 데미지 이벤트를 발생시킬 수 있다.

Health Component의 필요성

이전 글에서도 다루었듯이 데미지를 받고 그에 따라 체력이 감소하고, 체력이 감소함에 따라 특정한 행동을 하는 기능은 별도의 액터 컴포넌트에 구현하고 이를 컴포지션하여 사용하는 것이 좋다.

플레이어 또는 AI에 의해 컨트롤되는 폰 뿐만 아니라 구조물에도 데미지 관련 상호작용을 할 수 있어야 하는 경우가 있기 때문이다.

따라서 HealthComponent 라는 이름의 액터컴포넌트를 만들고 이를 타워, 탱크 블루프린트에 부착해 주었다.


Health Component 구성

UGameplayStatics::ApplyDamage 함수에서 콜백 함수로 사용할 함수는 다음과 같이 구성한다.

	UFUNCTION()
	void DamageTaken(AActor* DamagedActor, float Damage, const UDamageType* DamageType, class AController* Instigator, AActor* DamageCauser);
  • DamagedActor는 데미지를 입을 액터이다.
  • Damage는 데미지의 양이다.
  • DamageType은 데미지의 타입이다.
  • Instigator는 데미지를 가한 액터(의 컨트롤러) 이다.
  • DamageCauser는 데미지를 가한 액터이다.

UDamageType 은 데미지의 타입, 원인 등을 지정해서 다양한 타입의 데미지에 따라 다르게 대응할 수 있는 기능을 제공한다. 예를 들어 불에 의한 데미지를 받으면 화상 상태이상을 유발한다던지 하는 상호작용이 가능하다.


ApplyDamage 호출

우리 프로젝트에서 데미지를 가하는 주체는 바로 포탄, 즉 발사체(projectile)이다.
그리고 이전에 이미 hit event를 발생시키는 OnHit 함수를 만든 적이 있다. 발사체가 다른 물체에 부딪히면 -> 데미지를 가한다 라는 자연스러운 플로우에 맞게 OnHit 함수 내부에 ApplyDamage 를 호출하려고 한다.

ApplyDamage 함수 안에 넣을 파라미터 중에서 DamagedActorDamageType 그리고 DamageCauser는 쉽게 구할 수 있다. DamageType은 별다른 값을 지정해주기보다는 StaticClass를 사용하기로 한다.

Static Class는 그 클래스를 나타내는 UClass 객체를 리턴해 준다.

그런데 데미지 정보와 Instigator 는 직접 만들어야 한다.

UPROPERTY(EditAnywhere)
float Damage = 50;

데미지 양 정보를 헤더 파일에 선언하고 'Damage' 라는 이름으로 사용하기로 한다.
그리고 데미지를 가한 컨트롤러 정보인 Instigator가 필요한데, 지금 탱크나 타워가 날려보내는 포탄은 SpawnActor만 해주므로 Owner 정보가 없다. 따라서 누가 발사한 포탄인지 모른다. 그래서 BasePawn.cpp에서 포탄을 스폰할 때 Owner 액터 정보를 발사체에 심어서 날리도록 한다.

그리고 발사체에서 GetOwner() 을 통해 쉽게 오너액터에 접근할 수 있고, GetInstigatorController() 함수를 이용해 Instigator 컨트롤러를 구할 수 있다.

	AController* MyOwnerInstigator = GetOwner()->GetInstigatorController();
	UClass* DamageType = UDamageType::StaticClass();

	if (OtherActor != nullptr && OtherActor != this && OtherActor != GetOwner())
	{
		UGameplayStatics::ApplyDamage(OtherActor, Damage, MyOwnerInstigator, this, DamageType);
		Destroy();
	}

데미지를 가했으면 포탄은 사라져야 하므로 Destroy() 함수를 추가한다.

따라서 만들어진 함수는 위와 같다. 여기서 GetOwner 가 nullptr일 수 있으니 예외처리를 해 주고 나면 완성이다.

void AProjectile::OnHit(UPrimitiveComponent* HitComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit)
{
	if (GetOwner() == nullptr) 
		return;

	AController* MyOwnerInstigator = GetOwner()->GetInstigatorController();
	UClass* DamageType = UDamageType::StaticClass();

	if (OtherActor != nullptr && OtherActor != this && OtherActor != GetOwner())
	{
		UGameplayStatics::ApplyDamage(OtherActor, Damage, MyOwnerInstigator, this, DamageType);
		Destroy();
	}
}

타워가 탱크를 쏴서 충돌할 때마다 DamageTaken 로그가 발생하고 있다.

profile
울면안돼 쫄면안돼 냉면됩니다

0개의 댓글