C++/Unreal Engine

[UE4] 디버깅 기록 UTextBlock의 TextDelegate

로파이 2021. 10. 11. 23:22

 

UTextBlock의 Text

UE 4.27 기준 UI중에 블루프린트로 UTextBlock의 Text 설정을 함수를 만들어서 바인딩할 수 있으나 C++에서 해당 기능을 구현 중에 있었는데, 관련 델리게이트가 보이길래 UTextBlockTextDelegate를 이용하여 특정 이벤트가 발생했을 때 TextDelegate함수를 호출하고 다음과 같이 UI에 해당 내용을 바꾸려고 했다.

//	UPROPERTY(VisibleAnywhere, Category = UI, Meta = (PrivateAccess = true));
//	class UTextBlock* AmmoText;

AmmoText->SetText(AmmoText->TextDelegate.Execute());

하지만 실제로는 한번만 실행되고 해당 델리게이트가 Empty가 되어 어디서 Unbind()가 되나 했더니

void UTextBlock::SetText(FText InText)
{
	Text = InText;
	TextDelegate.Unbind(); // ????
	if ( MyTextBlock.IsValid() )
	{
		TAttribute<FText> TextBinding = GetDisplayText();
		MyTextBlock->SetText(TextBinding);
	}
}

해당 객체의 SetText를 호출하면 실제 텍스트를 바꾸는 것뿐만아니라 바인딩된 델리게이트도 해제하고 있었다.

따라서 다음과 같이 UI 클래스에서 직접 싱글캐스트 델리게이트를 선언해서 그 객체에 바인딩하는 것으로 일단 Unbind()를 회피했다.

UProgressBar

UProgressBar의 SetPercent는 바인드된 함수를 제거하지는 않아서 당연히 UTextBlock도 바인드된 함수를 안 건드리는 줄 알았다.

void UProgressBar::SetPercent(float InPercent)
{
	Percent = InPercent;
	if (MyProgressBar.IsValid())
	{
		MyProgressBar->SetPercent(InPercent);
	}
}

// 실제 사용코드
if (HealthBar->PercentDelegate.IsBound())
{
	HealthBar->SetPercent(HealthBar->PercentDelegate.Execute());
}
else
{
	LOG(Warning, TEXT("HealthBar PercentageDelegate Is Not Bound"));
}

 

- 클래스 정의

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

#pragma once

#include "../GameCore.h"
#include "Blueprint/UserWidget.h"
#include "FPSHUDWidget.generated.h"

DECLARE_DELEGATE_RetVal(FText, FOnAmmoChangedDelegate);
DECLARE_DELEGATE_RetVal(FText, FOnMaxAmmoChangedDelegate);
/**
 * 
 */
UCLASS()
class GAMECORE_API UFPSHUDWidget : public UUserWidget
{
	GENERATED_BODY()
	friend class AMainPlayer;
	friend class AMainPlayerController;
public:
	virtual void NativeConstruct() override;

	virtual void BeginDestroy() override;
private:
	void BeginPlay(class AMainPlayer* MainPlayer);

	void UpdatePlayerStat();
	void UpdateWeaponStat();
private:
	UPROPERTY(VisibleAnywhere, Category = UI, Meta = (PrivateAccess = true));
	class UProgressBar* ArmorBar;

	UPROPERTY(VisibleAnywhere, Category = UI, Meta = (PrivateAccess = true));
	class UProgressBar* HealthBar;

	UPROPERTY(VisibleAnywhere, Category = UI, Meta = (PrivateAccess = true));
	class UTextBlock* AmmoText;

	UPROPERTY(VisibleAnywhere, Category = UI, Meta = (PrivateAccess = true));
	class UTextBlock* MaxAmmoText;

	FOnAmmoChangedDelegate FOnAmmoChanged;
	FOnMaxAmmoChangedDelegate FOnMaxAmmoChangedDelegate;
};

 

- 함수 바인딩

//   Weapon.h
//
//	UFUNCTION()
//	FText GetAmmoInText() const { return FText::FromString(FString::FromInt(Ammo)); }

//	UFUNCTION()
//	FText GetMaxAmmoInText() const { return FText::FromString(FString::FromInt(MaxAmmo)); }

//  FPSHUD.cpp
// 바인딩
FOnAmmoChanged.BindUFunction(Weapon, TEXT("GetAmmoInText"));
FOnMaxAmmoChangedDelegate.BindUFunction(Weapon, TEXT("GetMaxAmmoInText"));

// 호출
if (FOnAmmoChanged.IsBound())
{
	AmmoText->SetText(FOnAmmoChanged.Execute());
}
else
{
	LOG(Warning, TEXT("AmmoText TextDelegate Is Not Bound"));
}