favicon Jay Lee DevLog

728x90
300x250

저번글에서는 간단하게 HP Bar를 추가해보았다.

오늘은 좀더 나아가서 위젯 블루프린트를 만들고,

소스를 통해 피격시 HP 감소,

몬스터 네임 설정 등 몇몇 부분을 추가해보려 한다.

 

먼저, UserWidget c++ 클래스 추가.

이후, 이전 글에서 만든 위젯의 부모를 방금 만든 C++ 클래스로 변경.

이제 MyUserWidget.h에 아래 소스 추가.

protected:
	virtual void NativeConstruct() override;

private:
	UPROPERTY()
	class UProgressBar* HPProgressBar;

	float fCurrentHP;
public:
	void SetHP(float HP);

MyUserWidget.cpp에 아래 소스 추가.

#include "Components/ProgressBar.h"

void UMyUserWidget::NativeConstruct()
{
	Super::NativeConstruct();

	HPProgressBar = Cast<UProgressBar>(GetWidgetFromName(TEXT("HPBar")));
	if (HPProgressBar)
	{
		fCurrentHP = 100.0f;
		HPProgressBar->SetPercent(fCurrentHP);
	}
}

void UMyUserWidget::SetHP(float HP)
{
	fCurrentHP = HP;
	HPProgressBar->SetPercent(HP);
}

여기서 HPProgressBar = Cast<UProgressBar>(GetWidgetFromName(TEXT("HPBar")));

해당 라인의 TEXT()안에는 

내가 만들었던 위젯 블루프린트의 HPBar의 이름을 넣어주면 된다.

SetHP같은 경우에는 대강 보면 알겠지만, SetPercent를 호출하여,

프로그래스 바의 퍼센트를 설정해주는 부분이다.

 

같은 방식으로 이름만 달리하여,

Name에 관련된 C++클래스도 추가해준다.

추가해준 이후에는 네임 블루프린트의 부모클래스 역시 바꿔준다.

이후 소스로 돌아가,

MyUserWidgetName.h에 아래 소스 추가.

protected:
	virtual void NativeConstruct() override;

private:
	UPROPERTY()
	class UTextBlock* NameTextBox;

	FText NameText;

public:
	void SetMonsterName(FText str);

MyUserWidgetName.cpp에 아래 소스 추가.

#include "Components/TextBlock.h"

void UMyUserWidgetName::NativeConstruct()
{
	Super::NativeConstruct();

	NameTextBox = Cast<UTextBlock>(GetWidgetFromName(TEXT("TB_Name")));
	if (NameTextBox)
	{
		NameTextBox->SetText(NameText);
	}
}


void UMyUserWidgetName::SetMonsterName(FText str)
{
	NameText = str;
}

여기까지 해두면, 일단 HP와 이름을 설정할 수 있는 준비는 끝났다.

이제 MyCharacter로 돌아와 실제로 사용하는 소스를 추가해 준다.

먼저 MyCharacter.h

public:
...
	UPROPERTY(VisibleAnywhere, Category = UI)
	class UWidgetComponent* NameWidget;
	
private:
...
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = UI, Meta = (AllowPrivateAccess = true))
	float NameHeight;
	
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = UI, Meta = (AllowPrivateAccess = true))
	FText MonsterName;

MyCharacter.cpp

AMyCharacter::AMyCharacter()
{
	NameWidget = CreateDefaultSubobject<UWidgetComponent>(TEXT("NameWIDGET"));
	NameWidget->SetupAttachment(GetMesh());
	NameWidget->SetWidgetSpace(EWidgetSpace::Screen); // Screen은 2D, World는 3D
	static ConstructorHelpers::FClassFinder<UUserWidget> UI_NAME(TEXT("WidgetBlueprint'/Game/BluePrint/UI/BP_NameWidget.BP_NameWidget'")); // BP 참조할때는 _C 붙여줘야 한다.
	if (UI_NAME.Succeeded())
	{
		NameWidget->SetWidgetClass(UI_NAME.Class);
		NameWidget->SetDrawSize(FVector2D(70.0f, 20.0f));
	}
}

void AMyCharacter::BeginPlay()
{
	if (NameWidget)
	{
		NameWidget->SetRelativeLocation(FVector(0.0f, 0.0f, NameHeight));
		auto MonsterNameWidget2 = Cast<UMyUserWidgetName>(NameWidget->GetUserWidgetObject());
		if (MonsterNameWidget2)
			MonsterNameWidget2->SetMonsterName(MonsterName); // 이러면 안되는데 그 이유는 순서가 BeginPlay->NativeConstruct 순서이기 때문이다.
	}								 // 때문에 Widget 소스에서 멤버로 해당 값을 받아 저장하고 있다가 NativeConstruct에서 바꿔주는 형태로 해야한다.
}

위의 소스는 이전 글에서 처럼 그냥 위젯 사이즈 설정하고,

위치 속성 추가해주고, 

이름 설정할 수 있게끔 해주는 소스이다.

추가로 SetMonsterName쪽에 주석처리 해둔 부분은 중요하니 한번 읽어두는게 좋다.

NativaConstruct는 MyUserWidgetName.cpp에 있다.

 

이제 HP Bar의 Percent에 대한 처리를 할건데,

데미지 부분에 방어력, 현재HP, Max HP, Damage 부분을 MyCharacter.h에 추가.

public:
...
	float CurrentHP;
	
private:
...
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Stat, Meta = (AllowPrivateAccess = true))
	float Defense;
	
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Stat, Meta = (AllowPrivateAccess = true))
	float MaxHP;
	
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Stat, Meta = (AllowPrivateAccess = true))
	float CurrentAttackDamage;
	
	float DefaultDamage = 50;

이제 MyCharacter.cpp에 소스 추가.

먼저 기존의 TakeDamage에 50의 값을 넣어줬었는데, 이 부분을 사용자가 셋팅한 값 CurrentAttackDamage로 변경.

void AMyCharacter::AttackHitNotifyL()
{
	...
	HitResult.GetActor()->TakeDamage(CurrentAttackDamage, DamageEvent, GetController(), this);
}

 

추가로 TakeDamage 부분에,

Damage 계산 식과 현재 HP Percent 설정하는 부분을 추가 해준다.

주석처리 되어 있는 DamageWidget은 스스로 구현해보자.

방법은 위와 똑같다.

float AMyCharacter::TakeDamage(float DamageAmount, FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCauser)
{
	float fDamage = DamageAmount - Defense;
	CurrentHP -= fDamage;
	if (MaxHP > 0)
	{
		float HPRate = CurrentHP / MaxHP;
		if (HPRate < 0)
			HPRate = 0.0f;
		UE_LOG(LogTemp, Warning, TEXT("CurrentHP : %f, HP Rate : %f"), CurrentHP, HPRate);

		auto HPWidget = Cast<UMyUserWidget>(HPBarWidget->GetUserWidgetObject());
		if (HPWidget)
			HPWidget->SetHP(HPRate);

		/*auto DamageWidget2 = Cast<UMyUserWidgetDamage>(DamageWidget->GetUserWidgetObject());
		if (DamageWidget2)
			DamageWidget2->SetDamageText(fDamage);*/
	}
}

갑자기 난이도가 올라간것 같은데,

원래 하나씩 하려고 했는데 다 엮여있다보니 난이도가 올라간 것 같다.

하지만 실질적으로 보면 같은 작업 반복이기 때문에, 그리 어렵지 않을거다.

 

이제 값들을 셋팅해주고, 플레이해보면

 

위와같이 HP Bar와 네임이 들어가는 걸 볼 수 있다.

그런데 보니깐 한방에 HP가 다 깍여서 확인해보니,

Current HP 초기 값이 없었다..

Current HP는 BeginPlay에서 MaxHP로 해주면 될 것 같다.

void AMyCharacter::BeginPlay()
{
	...
	CurrentHP = MaxHP;
}

이제 다시 확인해보면,

확인해볼라고 봤더니 Name도 셋팅이 안되는게 확인됐다..

Name은 Widget이랑 소스랑 다른 컴포넌트를 바라보게끔 만들어놔서 그런거였다.

위와 같이 바꿔주면 해결된다.

이제 다시 플레이해보면,

 

이렇게 공격마다 HP가 깍이는 모습을 확인할 수 있다.

여기서 더 나아가서 HP가 0이 되면 죽는 모습을 추가한다던지

할 수도 있을 것 같다.

이건 따로 글로 정리하진 않고 이전에 해놨기 때문에,

글 작성 이후에 추가해놓을거다.

if (CurrentHP <= 0.0f)
{
	MyAnim->SetDead();
}

이것만 TakeDamage쪽에 추가해두면 된다.

 

다음 글에서는

이제 AI를 추가해보려 한다.

728x90
반응형

+ Recent posts

/ /

Contact

📧 dlwjdwls60@naver.com


블로그에 내용이 있으면 해당 글을 보여주며, 없으면 내용이 복사된 채로 ChatGPT로 연결됩니다.