저번에 만들었던 체력을 이제 UI를 통해 가시적으로 보이게 만들 것이다
우클릭 후 유저 인터페이스(UI) -> 위젯 블루프린트를 생성하자 이름은 UI_HPBar
로 해주었다
상단에 프로그래스 바를 끌어와 생성해주고, 우측 상단에 사이즈를 커스텀 -> 넓이 150, 높이 50 으로 설정해준다
이름은 PB_HPBar
로 지정했다
프로그래스 바를 우클릭 후 Vertical Box 로 Wrap 해주고, 위 아래에 각각 Spacer를 넣어준다
우측의 디테일창에서 각각의 비율을 40 : 20 : 40 으로 설정해준다
이후 프로그래스 바는 배경 초록색, progress 색은 빨강, 진행 방향은 우측에서 좌측으로 설정해준다
위젯 에셋을 추가하기 위해서는 빌드 파일에서 UMG 를 추가해주어야 한다
프로젝트의 Source
폴더안에 있는 파일명.build.cs 파일에서 UMG 를 추가해준다
완료 되었다면, ABCharacter 클래스에 오브젝트를 생성해주자
ABCharacter.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "ArenaBattle.h"
#include "GameFramework/Character.h"
#include "ABCharacter.generated.h"
...
public:
...
UPROPERTY(VIsibleAnywhere, Category = Stat)
class UABCharacterStatComponent* CharacterStat;
UPROPERTY(VisibleAnywhere, Category = Camera)
USpringArmComponent* SpringArm;
UPROPERTY(VisibleAnywhere, Category = Camera)
UCameraComponent* Camera;
UPROPERTY(VisibleAnywhere, Category = UI)
class UWidgetComponent* HPBarWidget;
...
ABCharacter.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "ABCharacter.h"
#include "ABAnimInstance.h"
#include "DrawDebugHelpers.h"
#include "ABWeapon.h"
#include "ABCharacterStatComponent.h"
#include "Components/WidgetComponent.h"
// Sets default values
AABCharacter::AABCharacter()
{
// Set this character to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
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.0f));
}
...
}
이제 위젯 에셋을 데이터 값과 연동을 시키겠다
UserWidget
을 상속받는 클래스 ABCharacterWidget
을 만들자
해당 클래스에 전달할 정보는 ABCharacterStatComponent 에서 가져올 것이다
델리게이트를 만들어 연동시키도록 하겠다
_ABCharacterStatComponent.h_
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "ArenaBattle.h"
#include "Components/ActorComponent.h"
#include "ABCharacterStatComponent.generated.h"
DECLARE_MULTICAST_DELEGATE(FOnHPIsZeroDelegate);
DECLARE_MULTICAST_DELEGATE(FOnHPChangedDelegate);
UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent))
class ARENABATTLE_API UABCharacterStatComponent : public UActorComponent
{
GENERATED_BODY()
public:
// Sets default values for this component's properties
UABCharacterStatComponent();
protected:
// Called when the game starts
virtual void BeginPlay() override;
virtual void InitializeComponent() override;
public:
void SetNewLevel(int32 NewLevel);
void SetDamage(float NewDamage);
void SetHP(float NewHP);
float GetAttack();
float GetHPRatio();
FOnHPIsZeroDelegate OnHPIsZero;
FOnHPChangedDelegate OnHPChanged;
...
}
ABCharacterStatComponent.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "ABCharacterStatComponent.h"
#include "ABGameInstance.h"
...
// Called when the game starts
void UABCharacterStatComponent::BeginPlay()
{
Super::BeginPlay();
SetNewLevel(Level);
}
...
void UABCharacterStatComponent::SetNewLevel(int32 NewLevel)
{
auto ABGameInstance = Cast<UABGameInstance>(UGameplayStatics::GetGameInstance(GetWorld()));
CurrentStatData = ABGameInstance->GetABCharacterData(NewLevel);
if (nullptr != CurrentStatData)
{
Level = NewLevel;
SetHP(CurrentStatData->MaxHP);
}
}
void UABCharacterStatComponent::SetDamage(float NewDamage)
{
SetHP(FMath::Clamp<float>(CurrentHP - NewDamage, 0.0f, CurrentStatData->MaxHP));
}
void UABCharacterStatComponent::SetHP(float NewHP)
{
CurrentHP = NewHP;
OnHPChanged.Broadcast();
if (CurrentHP < KINDA_SMALL_NUMBER)
{
CurrentHP = 0.0f;
OnHPIsZero.Broadcast();
}
}
float UABCharacterStatComponent::GetAttack()
{
return CurrentStatData->Attack;
}
float UABCharacterStatComponent::GetHPRatio()
{
return (CurrentHP < KINDA_SMALL_NUMBER ? 0.0f : (CurrentHP / CurrentStatData->MaxHP));
}
이때 사용한
KINDA_SMALL_NUMBER
는 언리얼에서 제공하는 매크로 엡실론이다
이제 ABCharacterWidget 클래스를 정의해준다
ABCharacterWidget.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "ArenaBattle.h"
#include "Blueprint/UserWidget.h"
#include "ABCharacterWidget.generated.h"
/**
*
*/
UCLASS()
class ARENABATTLE_API UABCharacterWidget : public UUserWidget
{
GENERATED_BODY()
public:
void BindCharacterStat(class UABCharacterStatComponent* NewCharacterStat);
protected:
virtual void NativeConstruct() override;
void UpdateHPWidget();
private:
UABCharacterStatComponent* CurrentCharacterStat;
UPROPERTY()
class UProgressBar* HPProgressBar;
};
ABCharacterWidget.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "ABCharacterWidget.h"
#include "ABCharacterStatComponent.h"
#include "Components/ProgressBar.h"
void UABCharacterWidget::BindCharacterStat(UABCharacterStatComponent* NewCharacterStat)
{
CurrentCharacterStat = NewCharacterStat;
NewCharacterStat->OnHPChanged.AddUObject(this, &UABCharacterWidget::UpdateHPWidget);
}
void UABCharacterWidget::NativeConstruct()
{
Super::NativeConstruct();
HPProgressBar = Cast<UProgressBar>(GetWidgetFromName(TEXT("PB_HPBar")));
UpdateHPWidget();
}
void UABCharacterWidget::UpdateHPWidget()
{
if (CurrentCharacterStat->IsValidLowLevel())
if (nullptr != HPProgressBar)
HPProgressBar->SetPercent(1- CurrentCharacterStat->GetHPRatio());
}
마지막으로 ABCharacter.cpp 의 BeginPlay
에서 바인딩을 진행해주고
ABCharacter.cpp
void AABCharacter::BeginPlay()
{
Super::BeginPlay();
auto CharacterWidget = Cast<UABCharacterWidget>(HPBarWidget->GetUserWidgetObject());
if (nullptr != CharacterWidget)
CharacterWidget->BindCharacterStat(CharacterStat);
}
블루프린트의 Graph -> Class Setting 안에서 부모 클래스를 ABCharacterWidget으로 바꿔준다
완성된 화면 정상적으로 체력이 차감되는 것을 확인할 수 있다