본문 바로가기

Unreal Engine 4/C++

<Unreal C++> 50 - Action RPG (Enemy UI)

 

필요한 개념


<Enemy HP UI 만들기>

 

* CStatusComponent에 Health 변수 추가하기

UPROPERTY(EditDefaultsOnly, Category = "Health")
	float MaxHealth = 100.0f;

private:
	float Health;



// CStatusComponent.cpp

// Health에 MaxHealth 넣어주기
Health = MaxHealth;



<위젯 추가하기>

 

* 위젯 2개를 출력할 것이다. 

1. Name 

2. HP

 

-> UserWidget기반 Class를 만들고 이를 기반으로 한 WidgetBP 만들어서 연결해주면 됨

// CUsetWidget_Name.h

// 이름 세팅가능하도록 BP에서 재정의하도록 함
UFUNCTION(BlueprintImplementableEvent)
	void SetNameText(const FString& InName);



* 이 클래스를 기반하여 WB_Name 생성
컨트롤러의 이름과 캐릭터 이름을 모두 출력하도록 작업

그래프 레이아웃을 Desired On Screen으로 실제로 어떻게 나타나는지 확인가능(어느 크키인지 실감 가능)



* WB_Name 이벤트 그래프 구현
Replace : 입력받은 문자열에서 From 문자열과 일칫하는 문자열을 To문자열로 바꿔주는 함수
BP_CEnemy_ 문자를 없애기 위해

 



* UserWidget기반 CUserWidget_Health 클래스도 생성
이 클래스는 HP바 갱신할 클래스

Update()는 BeginPlay할 때 한번 해주고, Enemy가 Hitted 상태일 때, 한번 처리해주면 된다.

 

// CUserWidget_Health.h

// BP에서 재정의 함
UFUNCTION(BlueprintImplementableEvent)
	void Update(float Health, float MaxHealth);



* 이 클래스를 기반한 WB_Health BP 생성 및 Update() 함수 정의



 

* CEnemy에 2개의 WidgetBP 연결해줌

// CEnemy.h

protected:
	UPROPERTY(VisibleDefaultsOnly)
		class UWidgetComponent* NameWidget;

	UPROPERTY(VisibleDefaultsOnly)
		class UWidgetComponent* HealthWidget;



// CEnemy.cpp 생성자

// Widget은 Mesh()에다가 붙이면 된다.
CHelpers::CreateComponent<UWidgetComponent>(this, &NameWidget, "NameWidget", GetMesh());
CHelpers::CreateComponent<UWidgetComponent>(this, &HealthWidget, "HealthWidget", GetMesh());

 

* NameClass 로드

// WidgetClass 설정(TSubClassof UserWidget을 상속받은 애들만 가능)
TSubclassOf<UCUserWidget_Name> nameClass;
CHelpers::GetClass<UCUserWidget_Name>(&nameClass, "WidgetBlueprint'/Game/Widgets/WB_Name.WB_Name_C'");

// SetWidgetClass하면 WidgetComponent에서 이 위젯 객체를 만듬
// 나중에 GetWidgetObject()로 가져올 수 있음
NameWidget->SetWidgetClass(nameClass);
NameWidget->SetRelativeLocation(FVector(0, 0, 240));

// 크기 설정
NameWidget->SetDrawSize(FVector2D(240, 30));

// 화면 공간에 띄울껀지(Screen으로 해서 항상 정면에서 볼 수 있도록 함)
NameWidget->SetWidgetSpace(EWidgetSpace::Screen);

 

 

* HealthClass 로드

TSubclassOf<UCUserWidget_Health> healthClass;
CHelpers::GetClass<UCUserWidget_Health>(&healthClass, "WidgetBlueprint'/Game/Widgets/WB_Health.WB_Health_C'");
HealthWidget->SetWidgetClass(healthClass);
HealthWidget->SetRelativeLocation(FVector(0, 0, 190));
HealthWidget->SetDrawSize(FVector2D(120, 20));
HealthWidget->SetWidgetSpace(EWidgetSpace::Screen);



// CEnemy.cpp BeginPlay

// InitWidget() : 위젯을 초기화시키는 함수
// 안하면 가끔 콜이 안될때가 있음
NameWidget->InitWidget();
// GetUserWidgetObject() : UserWidgetclass로 세팅된 자료형에 대해 자동으로 만들어진 객체 리턴
Cast<UCUserWidget_Name>(NameWidget->GetUserWidgetObject())->SetNameText(GetActorLabel());

HealthWidget->InitWidget();
Cast<UCUserWidget_Health>(HealthWidget->GetUserWidgetObject())->Update(Status->GetHealth(), Status->GetMaxHealth());

 

 

 

CStatusComponent.h 추가된 내용


더보기
#pragma once

...

UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class UONLINE_04_ACTION_API UCStatusComponent : public UActorComponent
{
	GENERATED_BODY()

private:
	UPROPERTY(EditDefaultsOnly, Category = "Health")
		float MaxHealth = 100.0f;

public:
	FORCEINLINE float GetMaxHealth() { return MaxHealth; }
	FORCEINLINE float GetHealth() { return Health; }

public:
	... 
	void AddHealth(float InAmount);
	// Health를 줄이는 것
	void SubHealth(float InAmount);
  

private:
	float Health;	
};

 

 

 

 

 

 

 

CStatusComponent.cpp 추가된 내용


더보기
...


void UCStatusComponent::BeginPlay()
{
	Super::BeginPlay();

	Health = MaxHealth;
}

void UCStatusComponent::AddHealth(float InAmount)
{
	Health += InAmount;

	// Clamp : X가 Max보다 크면 Max로 제한,
	// X가 Min보다 작으면 Min으로 제한
	Health = FMath::Clamp(Health, 0.0f, MaxHealth);
}

void UCStatusComponent::SubHealth(float InAmount)
{
	Health -= InAmount;

	Health = FMath::Clamp(Health, 0.0f, MaxHealth);
}

 

 

 

 

 

CUserWidget_Name.h


더보기
#pragma once

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

UCLASS()
class UONLINE_04_ACTION_API UCUserWidget_Name : public UUserWidget
{
	GENERATED_BODY()

public:
	// 이름 세팅가능하도록 BP에서 재정의하도록 함
	UFUNCTION(BlueprintImplementableEvent)
		void SetNameText(const FString& InName);
};

 

 

 

CUserWidget_Health.h


더보기
#pragma once

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

UCLASS()
class UONLINE_04_ACTION_API UCUserWidget_Health : public UUserWidget
{
	GENERATED_BODY()
	
public:
	// BP에서 재정의 함
	UFUNCTION(BlueprintImplementableEvent)
		void Update(float Health, float MaxHealth);
};

 

 

 

 

 

CEnemy.h 추가된 내용


더보기
#pragma once

...

UCLASS()
class UONLINE_04_ACTION_API ACEnemy : public ACharacter, public IICharacter
{
	GENERATED_BODY()

protected:
	UPROPERTY(VisibleDefaultsOnly)
		class UWidgetComponent* NameWidget;

	UPROPERTY(VisibleDefaultsOnly)
		class UWidgetComponent* HealthWidget;
};

 

 

 

 

 

 

 

CEnemy.cpp 추가된 내용


더보기
...
#include "Components/WidgetComponent.h"
#include "Widgets/CUserWidget_Name.h"
#include "Widgets/CUserWidget_Health.h"


ACEnemy::ACEnemy()
{
	...

	// WidgetClass 설정(TSubClassof UserWidget을 상속받은 애들만 가능)
	TSubclassOf<UCUserWidget_Name> nameClass;
	CHelpers::GetClass<UCUserWidget_Name>(&nameClass, "WidgetBlueprint'/Game/Widgets/WB_Name.WB_Name_C'");
	// SetWidgetClass하면 WidgetComponent에서 이 위젯 객체를 만듬
	// 나중에 GetWidgetObject()로 가져올 수 있음
	NameWidget->SetWidgetClass(nameClass);
	NameWidget->SetRelativeLocation(FVector(0, 0, 240));
	// 크기 설정
	NameWidget->SetDrawSize(FVector2D(240, 30));
	// 화면 공간에 띄울껀지(Screen으로 해서 항상 정면에서 볼 수 있도록 함)
	NameWidget->SetWidgetSpace(EWidgetSpace::Screen);

	TSubclassOf<UCUserWidget_Health> healthClass;
	CHelpers::GetClass<UCUserWidget_Health>(&healthClass, "WidgetBlueprint'/Game/Widgets/WB_Health.WB_Health_C'");
	HealthWidget->SetWidgetClass(healthClass);
	HealthWidget->SetRelativeLocation(FVector(0, 0, 190));
	HealthWidget->SetDrawSize(FVector2D(120, 20));
	HealthWidget->SetWidgetSpace(EWidgetSpace::Screen);
}

void ACEnemy::BeginPlay()
{
	...

	// InitWidget() : 위젯을 초기화시키는 함수
	// 안하면 가끔 콜이 안될때가 있음
	NameWidget->InitWidget();
	// GetUserWidgetObject() : UserWidgetclass로 세팅된 자료형에 대해 자동으로 만들어진 객체 리턴
	Cast<UCUserWidget_Name>(NameWidget->GetUserWidgetObject())->SetNameText(GetActorLabel());

	HealthWidget->InitWidget();
	Cast<UCUserWidget_Health>(HealthWidget->GetUserWidgetObject())->Update(Status->GetHealth(), Status->GetMaxHealth());

	Action->SetUnarmedMode();
}

 

 

 

 

 

결과