본문 바로가기
Unreal/Manual

Unreal UDamageType 구현

by Dev_카페인 2024. 1. 2.
반응형

[Unreal/C++] UDamageType 구현

 

언리얼 엔진에서는 데미지를 주고 받을 수 있도록 기능을 제공한다.

 

 

기본적으로 데미지를 주는 것은 Apply Damage 함수들을 사용하고

Apply Damage Hurts the specified actor with generic damage. Target is Gameplay Statics
Apply Point Damage Hurts the specified actor with the specified impact. Target is Gameplay Statics
Apply Radial Damage Hurt locally authoritative actors within the radius. Will only hit components that block the Visibility channel. Target is

데미지를 받는 것은 TakeDamage 함수를 사용한다.

On Take Any Damage On Take Any Damage: Called when the actor is damaged in any way.
On Take Point Damage On Take Point Damage: Called when the actor is damaged by point damage.
On Take Radial Damage On Take Radial Damage: Called when the actor is damaged by radial damage.

 

ApplyDamage 함수를 살펴보면 여러가지 매개변수들이 존재하는데 이들 중 데미지 타입에 대해 어떻게 구현해야 하는지 생각이 필요하다.

UGameplayStatics::ApplyDamage
(
AActor* DamagedActor, //데미지를 받는 액터
float BaseDamage,  // 데미지 값
AController* EventInstigator, // 가해자
AActor* DamageCauser,  // 피해를 입힌 대상
TSubclassOf<UDamageType> DamageTypeClass  // 데미지 타입
)

여러 사이트에서 DamageType에 대한 자료를 찾아보면 생각보다 적은 자료에 놀란다.

아무도 DamageType을 상속받아 구현한 내용을 공개하지 않아서 어떻게 구현해야 하는지 감이 잡히지 않는다.

물론 기본 클래스를 전달하여 아무런 기능도 구현하지 않아도 잘 동작은 되지만, 언리얼 엔진에서 제공하는 프레임워크를 잘 사용할 수 있어야 도움이 된다고 생각한다.

 

 

먼저 언리얼 엔진에서 소개하는 DamageType은 다음과 같다.

이름에서 알 수 있듯이, DamageType (대미지 타입)은 대미지의 근원지와 무관하게 "유형"을 설명하는 데 사용되는 오브젝트입니다. 대미지 근원지가 많고 그 사이에 공통된 함수성이 있었으면 하는 경우 매우 유용한 개념입니다.
이를 쉽게 설명하는 예로 화염 대미지를 들 수 있습니다. 화염 대미지를 입을 때마다 "앗뜨거" 라 외치면서 근처의 물가로 달려가도록 한다고 칩시다. 이러한 행위에 대한 코드를 플레이어를 태울 수 있는 모든 액터( 혹은 탈 수 있는 모든 액터 유형)에 복제하기 보다는, 화염에 대한 대미지 유형(UDamageTypeFire)을 정의한 다음, 거기에 일정 유형의 HandleDamagedCharacter() 함수를 주고, TakeDamage() 호출 체인에서 적절히 호출해 주면 됩니다.

 

데미지 타입 클래스를 확인해 보면 어떻게 구현해야할지 감이 잡힐줄 알았는데 딱히 도움되는 기능은 없고 기본적인 데이터만 들어있을 뿐이다.

 

나는 UDamageType클래스를 상속받아 다른 클래스를 만들어 사용할 것이다.

이 방법이 효율적이고 권장하는 방법은 아니다. 공부를 하며 직감적으로 내가 사용하기 편하도록 틀만 잡아 놓은 것이다.

먼저 DamageType클래스를 상속받아 Base클래스를 만든다.

Base클래스는 전체 기능들을 가지고있는 가상 클래스가 될 것이다.

그리고 열거형을 이용해 타입을 미리 지정해두고 타입에 따라 데미지 이벤트를 달리 사용하는 방법이다.

enum은 데미지 타입이 추가될때마다 증가될 예정이고 현재는 넉백기능을 가진 클래스만 가지고 있다.

// DamageBase.h

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/DamageType.h"
#include "DamageBase.generated.h"

UENUM(BlueprintType)
enum class FDamageType : uint8
{
	None,
	KnockBack,
	Max,
};


UCLASS()
class MAINPROJECT_API UDamageBase : public UDamageType
{
	GENERATED_BODY()
	
public :
	void SetDamageType(FDamageType InDamageType);
	FDamageType GetDamageType();

public :
	virtual float GetKnockBackPower();

protected :
	float KnockBackPower = 0;

private :
	FDamageType DamageType;
};

 

위 Base클래스를 보면 DamageType을 지정해줄 수 있고 virtual함수로 기능을 미리 정의해두었다.

DamageBase클래스를 상속받으면 가상함수를 override해서 사용할 수 있으니 한 객체가 여러 데미지 타입을 받아도 적용이 가능하다.

// KnockBack.h

#pragma once

#include "CoreMinimal.h"
#include "Abilities/DamageType/DamageBase.h"
#include "KnockBack.generated.h"

UCLASS()
class MAINPROJECT_API UKnockBack : public UDamageBase
{
	GENERATED_BODY()
	
public :
	UKnockBack();

public :
	virtual float GetKnockBackPower() override;
};

사용하기에도 편리한 점은 virtual함수로 되어 있기 때문에 override로 구현만 한다면 자식 클래스의 함수가 호출되니 각각 캐스팅해줄 필요도 없다.

DamageType을 사용하기 위해서는 AActor에 있는 TakeDamage함수를 오버라이딩 하고 DamageEvent의 DamageTypeClass를 받아 기본 오브젝트를 받아 사용해야한다.

 

정리하자면

UDamageType을 상속받는 Base클래스를 만들고 모든 기능을 virtual 함수로 구현한다.

그리고 DamageType에 따라 Base클래스를 상속받는 클래스를 생성한다.

DamageType이 Fire라면 Base를 상속받아 Fire클래스를 만들고, 

Base클래스에서는 도트 데미지, 지속시간 변수를 protected로 구현하고 Get,Set 함수를 가상함수로 구현한다.

물론 데미지를 처리하기 위한 GetFireDamage 같은 함수도 만들어 놓는다.

Fire 클래스에서는 GetFireDamage를 Override하고 적절한 처리를 해준다.

이와 같은 처리를 통해 Ice Type도 만들 수 있으며, Ice클래스에서는 Fire데미지에 필요한 가상함수를 구현하지 않아도 된다.

 

 

다시 한 번 말하지만 처리 방법에는 정답이 없다. 이 방법의 효율성은 검증되지 않았고 더 좋은 방법이 있을 것이라 확신한다. 처리 방법에 대해 좋은 의견이 있다면 서슴없이 의견을 내주길 바랍니다.

 

 

 

UE4 에서의 대미지

대미지는 베이스 Actor 클래스에 지원되는 기능이므로, 폭넓게 사용 가능합니다. 이 시스템은 자주 쓰이는 함수성을 쉽게 접근할 수 있도록 해 주어 빠른 결과 확인이 가능하면서도, 확장이 가능

www.unrealengine.com

 

 

Damage

Damage

docs.unrealengine.com

 

반응형