10.2 캐릭터 위젯 UI 제작
캐릭터 위에 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());
}
}
}