ftftgop3 2024. 4. 28. 14:59

캐릭터 위에 HP 바 UI 위젯 제작

1. 위젯 블루 프린트 생성

2.FullScreen 를 custom 150x50 으로 작업 공간 수정

3.Canvas Panel 제거, Progress Bar 추가 후 Vertical box로 컨트롤 감싸기

4. Spacer 컨트롤를 배치

 

Widget 레이아웃 변경

Vertical box는 세개의 컨트롤을 가지고 레이아웃을 셋팅

각 슬롯을 채우기로 변경 40%, 20%, 40% 영역 배분

Progressbar 채우기 색을 빨간색으로 변경

 

UI 모듈과 빌드 설정

UI를 캐릭터 부착 할 수 있게 UWidgetComponent 제공 

Webget 클래스 사용전에 UI 엔진 모듈을 지정, Projectname.Build.cs 파일 에 UMG 추가

// Fill out your copyright notice in the Description page of Project Settings.

using UnrealBuildTool;

public class ArenaBattle : ModuleRules
{
	public ArenaBattle(ReadOnlyTargetRules Target) : base(Target)
	{
		PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
	
		PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore","UMG"});

		PrivateDependencyModuleNames.AddRange(new string[] {  });
	}
}

 

HPBar를 캐릭터에 부착

캐릭터 위에 HPBar 위치 조정

앞에 생성 한 위젯 블루 프린트 애셋을 WidgetClass로 등록 

Screen 모드 지정, 크기는 150,50 지정

	UCLASS()
class ARENABATTLE_API AABCharacter : public ACharacter
{
	GENERATED_BODY()
    UPROPERTY(VisibleAnywhere, Category = UI)
		class UWidgetComponent* HPBarWidget;
}

AABCharacter::AABCharacter()
{
	HPBarWidget = CreateDefaultSubobject<UWidgetComponent>(TEXT("HPBARWIDGET"));
	HPBarWidget->SetupAttachment(GetMesh());
    
    HPBarWidget->SetRelativeLocation(FVector(0.0f, 0.0f, 180.0f));
	HPBarWidget->SetWidgetSpace(EWidgetSpace::Screen);
	
    static ConstructorHelpers::FClassFinder<UUserWidget> UI_HUD(TEXT("/Game/Book/UI/UI_HPBar.UI_HPBar_C"));

	if (UI_HUD.Succeeded())
	{
		HPBarWidget->SetWidgetClass(UI_HUD.Class);
		HPBarWidget->SetDrawSize(FVector2D(150.0f, 50.f));
		
	}
}

 

UI와 테이터 연동

스탯 변경 시 UI에 전달 해 프로그래스바 변경

UserWidget 상속 받은 새 클래스 생성 후

ABCharacterStatComponent 의 HPchange 델리게이트에 등록 해 구현

KINDA_SMALL_NUMBER 매크로 float 값을 0과 비교 시 오차 범위 내에 판단

 

ABCharacterStatComponent

DECLARE_MULTICAST_DELEGATE(FOnHPchangedDelegate);

UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class ARENABATTLE_API UABCharacterStatComponent : public UActorComponent
{
	GENERATED_BODY()
    public:
    
	void SetHP(float NewHP);
	float GetHPRatio();
	//HP 변경 시 동작 하는 델리게이트
	FOnHPchangedDelegate OnHPChanged;
}

void UABCharacterStatComponent::SetNewLevel(int32 NewLevel)
{
	//월드에서 게임 인스턴스 객체 참조
	auto ABGameInstance = Cast<UABGameInstance>(UGameplayStatics::GetGameInstance(GetWorld()));
	//게임 인스턴스 참조 확인
	ABCHECK(ABGameInstance != nullptr)

	//새로운 레벨에 맞는 현재 스탯 지정
	CurrentStatData = ABGameInstance->GetABCharacterData(NewLevel);
	//데이터 업데이트
	if (CurrentStatData != nullptr)
	{
		Level = NewLevel;
        //HP 값 지정
		SetHP(CurrentStatData->MaxHP);
		CurrentHP = CurrentStatData->MaxHP;
	}
	else
	{
		ABLOG(Error, TEXT("Level (%d) data doesn't exist"), NewLevel);
	}
}

void UABCharacterStatComponent::SetDamage(float NewDamage)
{
	ABCHECK(CurrentStatData != nullptr);
	//최소, 최대 값 지정
	SetHP(FMath::Clamp<float>(CurrentHP - NewDamage, 0.0f, CurrentStatData->MaxHP));
}
void UABCharacterStatComponent::SetHP(float NewHP)
{
	CurrentHP = NewHP;
    //HP 변경 델리게이트 실행
	OnHPChanged.Broadcast();
    
	//KINDA_SMALL_NUMBER 은 float 값을 0과 비교 시 오차 범위 내에 판단
	if (CurrentHP < KINDA_SMALL_NUMBER)
	{
		CurrentHP = 0.0f;
		OnHPIsZero.Broadcast();
	}
}

float UABCharacterStatComponent::GetHPRatio()
{
	//오류 발생 시 오류 문구 출력 후 0.0f 리턴
	ABCHECK(CurrentStatData != nullptr, 0.0f);
	//참 : 0.0f, false : 현재 HP/ 전체 HP 비율 값 리턴
	//0을 분모로 나누면 오류가 발생 미리 예외 처리
	return(CurrentStatData->MaxHP < KINDA_SMALL_NUMBER ? 0.0f : (CurrentHP / CurrentStatData->MaxHP));
}

 

ABCharacterWidgetComponent

HP 변경 될때 프로그래스 바가 변경 될 수 있게 델리게이트에 등록

이번에는 약 포인트를 사용 해 구현 ( 같은 생명 주기일 경우 필요 없지만 학습 용 사용 )

UI 와 캐릭터가 서로 다른 액터 일 경우 약포인트로 사용 하는것 이 좋다

UCLASS()
class ARENABATTLE_API UABCharacterWidget : public UUserWidget
{
	GENERATED_BODY()

public:
	void BindCharacterStat(class UABCharacterStatComponent* NewCharacterStat);

private:
	TWeakObjectPtr<class UABCharacterStatComponent> CurrentCharacterStat;
};

void UABCharacterWidget::BindCharacterStat(class UABCharacterStatComponent* NewCharacterStat)
{
	ABCHECK(NewCharacterStat != nullptr)
	//약포인트에 캐릭터 스탯 지정
	CurrentCharacterStat = NewCharacterStat;

	//델리게이트에 함수 등록
	NewCharacterStat->OnHPChanged.AddLambda([this]() ->void {
	if (CurrentCharacterStat.IsValid())
	{			
		ABLOG(Warning, TEXT("HPRatio : %f"), CurrentCharacterStat->GetHPRatio());
	}

	});
}

 

ABCharacter

캐릭터 컴포넌트와 UI 위젯을 연결

void AABCharacter::BeginPlay()
{
	Super::BeginPlay();
    
	auto CharacterWidget = Cast<UABCharacterWidget>(HPBarWidget->GetUserWidgetObject());
	if (CharacterWidget != nullptr)
		CharacterWidget->BindCharacterStat(CharacterStat);
}

UI_HPBar 블루프린트

그래프 탭 에서 클래스 세팅으로 부모 클래스 ABCharacterWidget으로 변경

4.21 버전 초기화 순서

PostInitializeComponent  -> NativeConstruct -> Beginplay

UI는  NativeConstruct 에 초기화 되기 때문에 이전에 호출 하면 안된다 

그래서 UI는 BeginPlay 에서 초기화 해준다

ABCharacterWidgetComponent

PB_HPbar 위젯 검색 후 해당 위젯의 속성 업데이트

4.21 버전 초기화 순서에 따라  NativeConstruct 함수에서 위젯 내용 업데이트 

UCLASS()
class ARENABATTLE_API UABCharacterWidget : public UUserWidget
{
	GENERATED_BODY()

public:
	void BindCharacterStat(class UABCharacterStatComponent* NewCharacterStat);

protected:
	//Progressbar 초기화
	virtual void NativeConstruct() override;
	//Progressbar percent 값 수정
	void UpdateHPWidget();

private:
	//약 포인트 선언
	TWeakObjectPtr<class UABCharacterStatComponent> CurrentCharacterStat;
    
	UPROPERTY()
	class UProgressBar* HPProgressBar;
};

#include "Components/ProgressBar.h"

void UABCharacterWidget::BindCharacterStat(class UABCharacterStatComponent* NewCharacterStat)
{
	ABCHECK(NewCharacterStat != nullptr)
	//약포인트에 캐릭터 스탯 지정
	CurrentCharacterStat = NewCharacterStat;

	//델리게이트에 함수 등록
	NewCharacterStat->OnHPChanged.AddUObject(this, &UABCharacterWidget::UpdateHPWidget);

	
}

void UABCharacterWidget::NativeConstruct()
{
	Super::NativeConstruct();
	//해당 Widget에서 PB_HPBar 위젯을 찾, UProgressBar 캐스팅 
	HPProgressBar = Cast<UProgressBar>(GetWidgetFromName(TEXT("PB_HPBar")));
	ABCHECK(HPProgressBar != nullptr)
	UpdateHPWidget();
}
void UABCharacterWidget::UpdateHPWidget()
{
	//약 포인트 연결 확인
	if (CurrentCharacterStat.IsValid())
	{
		if (HPProgressBar != nullptr)
		{
			//Percent 값 변경
			HPProgressBar->SetPercent(CurrentCharacterStat->GetHPRatio());
		}
	}
}