카테고리:

Unreal C++ - 데미지 출력

언리얼에서 피격 부분에 데미지를 출력하는 방법을 기록한 포스트입니다.
Unreal데미지출력

데미지를 출력하기 위한 UCUserWidget_Damage 클래스를 생성하고 이를 상속받는 Widget Blueprint를 생성합니다.

//CUserWidget_Damage.h 파일
#pragma once

#include "CoreMinimal.h"
#include "Blueprint/UserWidget.h"
#include "CUserWidget_Damage.generated.h"

UCLASS()
class CPP_PORTFOLIO_API UCUserWidget_Damage : public UUserWidget
{
	GENERATED_BODY()

public:
	FORCEINLINE void SetDamage(int InDamage) { Damage = InDamage; }

	UFUNCTION(BlueprintCallable)
		int GetDamage();

private:
	int Damage = 0;
};

//CUserWidget_Damage.cpp 파일
#include "Widgets/CUserWidget_Damage.h"

int UCUserWidget_Damage::GetDamage()
{
	return Damage;
}

Unreal데미지위젯

텍스트의 값을 GetDamage로 가져온 Damage값과 Bind하여 출력 시 밑에서 입력한 데미지로 출력하도록 설정합니다.

이 후 기존 코드의 데미지를 입는 부분에 데미지를 출력하는 함수를 추가합니다.

//몬스터에게 피해를 적용하는 메서드
void UCHitComponent_Monster::Hit(ACharacter* InAttacker, const FHitData& InHitData, EPhysicalSurface InBodyPart, FHitResult SweepResult)
{
	UCMonsterStateComponent* state = Cast<UCMonsterStateComponent>(OwnerCharacter->GetComponentByClass(UCMonsterStateComponent::StaticClass()));
	UCMonsterBehaviorComponent* behavior = Cast<UCMonsterBehaviorComponent>(OwnerCharacter->GetComponentByClass(UCMonsterBehaviorComponent::StaticClass()));

	HP = FMath::Clamp<float>(HP - InHitData.Damage, 0, MaxHP);

	//데미지 출력 추가 부분
	{
			APlayerController* controller = Cast<APlayerController>(InAttacker->GetController());

			FVector screenPosition;
			controller->ProjectWorldLocationToScreenWithDistance(OwnerCharacter->GetActorLocation(), screenPosition, true);

			PrintDamage(InAttacker, InHitData.Damage, screenPosition.X, screenPosition.Y, screenPosition.Z);
	}

	if (FMath::IsNearlyZero(HP))
	{
		Dead();
		return;
	}

	//TODO: sound and effects
	if (BodyPartHP.Contains(TEnumAsByte<EPhysicalSurface>(InBodyPart)))
	{
		BodyPartHP[InBodyPart].DamageTaken += InHitData.Damage;

		if (BodyPartHP[InBodyPart].DamageTaken >= BodyPartHP[InBodyPart].BreakDamage)
		{
			BodyPartHP[InBodyPart].DamageTaken -= BodyPartHP[InBodyPart].BreakDamage;

			if (Montages[InBodyPart] != nullptr && BodyPartHP[InBodyPart].BreakCount > 0)
			{
				BodyPartHP[InBodyPart].BreakCount--;

				if (!!state)
				{
					state->SetHitState();
				}

				if(!!behavior)
				{
					behavior->SetHitMode();
				}



				OwnerCharacter->PlayAnimMontage(Montages[InBodyPart]);
			}
		}
	}
}
// 몬스터 피격을 관리하는 컴포넌트의 데미지 출력 메서드
void UCHitComponent_Monster::PrintDamage(ACharacter* InAttacker, int InDamage, int x, int y, int distance)
{
	APlayerController* controller = Cast<APlayerController>(InAttacker->GetController());

	if (controller == nullptr)
		return;

	UCUserWidget_Damage* widget = Cast<UCUserWidget_Damage>(CreateWidget(controller, DamageWidget));

	distance = FMath::Clamp(distance, 0, 1000);
	float scale = 0.8f + (0.6 * (1 - float(distance) / 1000));

	widget->SetDamage(InDamage);
	widget->SetRenderTranslation(FVector2D(x, y));
	widget->SetRenderScale(FVector2D(scale, scale));
	widget->AddToViewport();
}

공격자(Player)의 controller를 가져와 controller의 ProjectWorldLocationToScreenWithDistance함수를 사용하여 WVP연산을 통해 나타낸 몬스터의 위치를 역연산합니다.

역연산을 통해 나온 몬스터의 위치의 Screen에서의 좌표와 controller와 떨어져 있는 거리를 통해 출력될 데미지 Widget의 크기를 결정한다.