Unreal - UI (HpWidget, PostureWidget)

2025. 11. 21. 05:34·Unreal 프로젝트 다이어리/두번째 프로젝트

설계방향

게임플레이를 진행하면서 보여질 위젯 즉 UI 를 작업해보겠습니다

전 프로젝트를 거치며 플레이어의 위젯은 플레이어 컨트롤러에서 띄우는방식이 옳게된 방향성인것을 알고

MainWidget은 플레이어 컨트롤러에서 띄우도록 하겠습니다

 

MainWidget은 플레이어 화면에 띄워지는 여러가지 위젯을 한번에 보여지는

"위젯 컨테이너" 의 역할을 합니다

 

StatComponent에서 가지고있는 값들로 위젯을 초기화할텐데

해당 위젯들은 컴포넌트내에서 구독 방식으로 위젯을 업데이트하는 함수를 호출합니다

 

1. UI 기획안 짜기

1인개발이라서 기획도 전부 혼자 구성해야했습니다

UI의 베이스는 세키로 라는 게임의 UI를 베이스로 삼되 여러가지 알림 위젯을 추가하여주었습니다

UI의 초기 배치, 디자인 구상도입니다

 

Widget은 컨테이너 역할을 하는 MainWidget을 제외하고 

총 4가지 종류로 나뉜다

1. 캐릭터 상태를 나타내는 상태위젯

HpBarWidget

PostureWidget

 

2. 플레이어의 입력과 연관된 슬롯위젯

QuickslotWidget

 

3. 알림을 담당하는 알림 위젯

NotificationWidget

QuestWidget

InteractionWidget

 

4. 화면효과적 전역효과를 나타내는 효과위젯

ScreenEffectWidget

 

 

또한 I키를 누르면 보여지는 인벤토리위젯, 레벨에 따라 강화할수있는 능력Tree 여러가지 위젯 기초 구상도는 이와같습니다

2. Widget 구현하기

HPBar위젯, Posture위젯을 구현해보도록 하겠습니다

HPBar위젯은 플레이어의 HP를 기반으로 프로그래스바 형식으로 구현하였습니다

위젯의 하이어라키는 이와같습니다

 

HpBarWidget

HpBar은 보조HP바가 목표 체력 비율까지 부드럽게 감소하도록 매프레임 보간하여 업데이트하는 함수를 만들어서

줄어든만큼 흰색게이지로 보여지게 설계하였습니다

void UHpBarWidget::UpdateSecondHPBar(float currentHp, float maxHp)
{
	if (ProgressBar_SecondProgress)
	{
		float barPercent = FMath::Clamp(currentHp / maxHp, 0.f, 1.f);
		ProgressBar_SecondProgress->SetPercent(barPercent);
	}
}

void UHpBarWidget::UpdateSecondHPBarStep()
{
	if (!ProgressBar_SecondProgress) return;

	currentSubHpPercent = FMath::FInterpTo(currentSubHpPercent, targetHpPercent, 0.016f, interpSpeed);
	ProgressBar_SecondProgress->SetPercent(currentSubHpPercent);

	if (FMath::IsNearlyEqual(currentSubHpPercent, targetHpPercent, 0.001f))
	{
		currentSubHpPercent = targetHpPercent;
		ProgressBar_SecondProgress->SetPercent(targetHpPercent);
		return;
	}

	FTimerDelegate timerDel;
	timerDel.BindUFunction(this, FName("UpdateSecondHPBarStep"));
	GetWorld()->GetTimerManager().SetTimer(th_subHPTimerHandle, timerDel, 0.016f, false);
}

void UHpBarWidget::UpdateHp(float currentHp, float maxHp)
{
	if (ProgressBar_InnerBar)
	{
		float hpPercent = currentHp / maxHp;
		hpPercent = FMath::Clamp(hpPercent, 0.f, 1.f);
		ProgressBar_InnerBar->SetPercent(hpPercent);

		targetHpPercent = hpPercent;

		if (GetWorld()->GetTimerManager().IsTimerActive(th_subHPTimerHandle))
		{
			GetWorld()->GetTimerManager().ClearTimer(th_subHPTimerHandle);
		}

		FTimerDelegate timerDel;
		timerDel.BindUFunction(this, FName("UpdateSecondHPBarStep"));
		GetWorld()->GetTimerManager().SetTimer(th_subHPTimerHandle, timerDel, 0.5f, false);
	}


}

 

Posture Widget

하이어라키는이와 같습니다

위젯의 Render x값을 키우거나 줄여서 체간값을 시각적으로 나타내었으며

크기에따라 색상을 변동합니다

void UPostureWidget::UpdatePosture(float currentPosture, float maxPosture)
{
	float posturePercent = FMath::Clamp(currentPosture / maxPosture, 0.f, 1.f);
	scaleX = FMath::Lerp(0.f, 1.2f, posturePercent);

	FWidgetTransform setTransform = Image_Posture->RenderTransform;
	setTransform.Scale.X = scaleX;
	Image_Posture->SetRenderTransform(setTransform);

	if (FMath::IsNearlyEqual(posturePercent, 1.f, 0.001f))
		Image_Posture->SetColorAndOpacity(FLinearColor(1.f, 0.088349f, 0.f, 1.f));
	else
		Image_Posture->SetColorAndOpacity(FLinearColor::White);
}

 

 

스탯컴포넌트에서 체력이 변할 때 델리게이트를 브로드캐스트하고,

위젯은 그 델리게이트를 구독하여 위젯의 UpdateHp같은 함수를 호출할수 있도록 구성합니다

DECLARE_MULTICAST_DELEGATE_TwoParams(FOnHpChanged, float, float); //currentHp, maxHp
DECLARE_MULTICAST_DELEGATE_TwoParams(FOnPostureChanged, float, float); //currentPosture, maxPosture

UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class PORTFOLIOMS_API UStatComponent : public UActorComponent
{
	GENERATED_BODY()

public:	
	UStatComponent();
/// Widget----------------------
public:
	FOnHpChanged onHpChanged;
	float GetMaxHp() const
	{
		if (stats.Contains(EBaseStatType::Hp))
			return stats[EBaseStatType::Hp].EntryTotal();
		return 0.f;
	}

	FOnPostureChanged onPostureChanged;
	float GetMaxPosture() const
	{
		if (stats.Contains(EBaseStatType::Posture))
			return stats[EBaseStatType::Posture].EntryTotal();
		return 0.f;
	}
}

 

스탯컴포넌트에서 

이 이벤트를 구독하면 값이 바뀔떄 자동으로 알림을 받을수 있도록 구독하며 발신자 역할을 합니다

void UStatComponent::UpdateCurrentStats()
{
	if (stats.Contains(EBaseStatType::Hp))
		currentHp = FMath::Clamp(currentHp, 0.f, stats[EBaseStatType::Hp].EntryTotal());

	// Posture
	if (stats.Contains(EBaseStatType::Posture))
		currentPosture = FMath::Clamp(currentPosture, 0.f, stats[EBaseStatType::Posture].EntryTotal());

	if (onHpChanged.IsBound())
		onHpChanged.Broadcast(currentHp, stats[EBaseStatType::Hp].EntryTotal());

	if (onPostureChanged.IsBound())
		onPostureChanged.Broadcast(currentPosture, stats[EBaseStatType::Posture].EntryTotal());

}

 

수신자 역할을 하며 statcomponent에서 값이 바뀌면 자동으로 메인위젯이 알림을 받습니다

void UMainWidget::InitValue()
{
	APawn* player = UGameplayStatics::GetPlayerPawn(this, 0);
	if (!player)
		return;
	statComp = player->FindComponentByClass<UStatComponent>();
	if (!statComp)
		return;

	//델리 구독
	statComp->onHpChanged.AddUObject(this, &UMainWidget::HandleHpChange);
	HandleHpChange(statComp->currentHp, statComp->GetMaxHp());

	statComp->onPostureChanged.AddUObject(this, &UMainWidget::HandlePostureChange);
	HandlePostureChange(statComp->currentPosture, statComp->GetMaxPosture());
}

void UMainWidget::HandleHpChange(float currentHp, float maxHp)
{
	if (hpBarWidget)
		hpBarWidget->UpdateHp(currentHp, maxHp);

}

void UMainWidget::HandlePostureChange(float currentPosture, float maxPosture)
{
	if (postureWidget)
		postureWidget->UpdatePosture(currentPosture, maxPosture);
}

 

아직 AI를 구현하지않았기떄문에 0번은 데미지받기 9번은 힐하기로 연동하여주었습니다

(테스트코드)

void AMainCharacterController::InputKeyboard0Pressed()
{
	float damage = 50.f;
	EGuardType guard = EGuardType::None;
	mainCharacter->statComp->ApplyGetDamage(damage, guard);

}
void AMainCharacterController::InputKeyboard9Pressed()
{
	float heal = 30.f;
	mainCharacter->statComp->ApplyHeal(heal);
}

 

결과

 

'Unreal 프로젝트 다이어리 > 두번째 프로젝트' 카테고리의 다른 글

Unreal - 인벤토리(2) ( 아이템 추가하기 )  (0) 2025.11.23
Unreal - 인벤토리(1) ( 크기변경하기, 창옮기기 )  (2) 2025.11.22
Unreal - StatComponent  (0) 2025.11.21
Unreal - 상호작용(사다리 타기)  (0) 2025.11.15
Unreal - 상호작용(승강기)  (0) 2025.11.12
'Unreal 프로젝트 다이어리/두번째 프로젝트' 카테고리의 다른 글
  • Unreal - 인벤토리(2) ( 아이템 추가하기 )
  • Unreal - 인벤토리(1) ( 크기변경하기, 창옮기기 )
  • Unreal - StatComponent
  • Unreal - 상호작용(사다리 타기)
lucodev
lucodev
커피와 노트북 그리고 개발
  • lucodev
    루코 개발테이블
    lucodev
  • 전체
    오늘
    어제
    • 분류 전체보기 (211) N
      • Unreal 프로젝트 다이어리 (108) N
        • 첫번째 프로젝트 (73)
        • 두번째 프로젝트 (35) N
      • Unreal 팁 (8)
      • Unreal 디버깅 (8)
      • C++ 프로그래머스 (52)
        • Stack,Queue (7)
        • Hash (4)
        • Heap (2)
        • Sort (5)
        • Exhaustive search (5)
        • Greedy (2)
        • BFS , DFS (7)
        • Graph (2)
        • Dynamic Programming (1)
        • C++ Math (2)
        • 기타 문제 (14)
      • C++ 백준 (4)
      • C++ 팁 (1)
      • 개인 코테 & 스타디 <비공개> (29)
        • 코드 개인보관함 (9)
        • 코딩테스트+@ (11)
        • 알고리즘 스타디 (6)
        • 알고리즘 스타디 과제 (3)
        • 비공개 (0)
  • 인기 글

  • 최근 글

  • 최근 댓글

  • 링크

  • 공지사항

  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 태그

    언리얼 parkour
    Unreal Parkour
    unreal 시퀀스
    언리얼 프로그래스바
    unreal inventory
    unreal 인벤토리
    언리얼 motionmatching
    언리얼 behaviortree
    언리얼 상호작용
    언리얼 behavior tree
    언리얼 시퀀스
    언리얼 컷씬
    언리얼 인벤토리
    unreal 모션매칭
    언리얼 모션매칭
    언리얼
    unreal 파쿠르
    언리얼 비헤이비어트리
    언리얼 ui
    언리얼 파쿠르
  • hELLO· Designed By정상우.v4.10.3
lucodev
Unreal - UI (HpWidget, PostureWidget)
상단으로

티스토리툴바