KoreanFoodie's Study
[언리얼] 언리얼에서 더티 플래그(Dirty Flag) 패턴 사용하기 본문
더티 플래그 패턴이란?
더티 플래그 패턴에 대해서는, 이전 블로그 글에서 간단하게 언급한 바 있다. 더티 플래그 패턴은 일반적으로 렌더링 과정에서, 계층 구조가 존재할 경우, 필요할 때 / 필요한 타이밍에 화면을 갱신하는 부분에서 자주 쓰인다.
또한 더티 플래그를 체크해 UI 를 갱신하게 되면, Tick 마다 UI 업데이트가 중복해서 일어나는 것을 막을 수 있다.
그렇다면 이 패턴을 언리얼에서는 어떤 방식으로 적용해 볼 수 있을까? 예를 들어, 우리가 어떤 UI 를 갱신 하는데,Dirty Flag 를 두어 해당 더티 플래그가 켜져 있을 때만 UI 를 갱신한다고 가정해 보자.
일단, 실제로 UI 클래스를 만들기 전에, 더티 플래그 패턴을 사용할 위젯들이 공통적으로 사용할 인터페이스를 다음과 같이 정의할 수 있다.
DirtyUpdateUIInterface.h
#pragma once
#include "CoreMinimal.h"
#include "UObject/ObjectMacros.h"
#include "UObject/Interface.h"
#include "UpdateUIInterface.generated.h"
UINTERFACE(meta=( CannotImplementInterfaceInBlueprint ))
class MYPROJECT_API UDirtyUpdateUIInterface : public UInterface
{
GENERATED_UINTERFACE_BODY()
};
class MYPROJECT_API IDirtyUpdateUIInterface
{
GENERATED_IINTERFACE_BODY()
protected:
bool _isDirty = false;
protected:
// Implement this in derived classes; Only include code for updating UI
virtual void UpdateUIInternal() = 0;
public:
// Set Dirty Flag 'true'
void UpdateUI();
// **Important** Bind this to "Tick" function!
void CheckUpdate();
// Getter/Setter for _isDirty flag
void SetDirtyFlag(bool InDirty);
bool GetDirtyFlag() const { return _bDirty; }
};
DirtyUpdateUIInterface.cpp
#include "DirtyUpdateUIInterface.h"
UDirtyUpdateUIInterface::UDirtyUpdateUIInterface(const class FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
}
void IDirtyUpdateUIInterface::UpdateUI()
{
SetDirtyFlag(true);
}
void IDirtyUpdateUIInterface::SetDirtyFlag(bool IsDirty)
{
_isDirty = IsDirty;
}
void IDirtyUpdateUIInterface::CheckUpdate()
{
if (GetDirtyFlag())
{
SetDirtyFlag(false);
UpdateUIInternal();
}
}
이제 우리가 사용할 UI 클래스들은 위의 인터페이스를 상속받는다. 예를 들어, HP 상태를 보여주는 HP 바 위젯이 위의 더티 플래그 인터페이스를 상속받는다고 가정해 보자. 구현은 다음과 같을 것이다(실제 프로젝트에서 만든 것이 아닌 테스트 코드이니 실제로 적용할 때는 조금 고쳐서 사용하자).
HPBar.h
#pragma once
#include "CoreMinimal.h"
#include "IDirtyUpdateUIInterface.h"
#include "HPBar.generated.h"
class UserWidget;
UClass()
class MYPROJECT_API UHPBar : public UUserWidget, public IDirtyUpdateUIInterface
{
GENERATED_BODY()
protected:
// UUserWidget 을 오버라이딩한다
virtual void NativeOnInitialized() override;
virtual void NativeDestruct() override;
// NativeTick 을 오버라이드 해 주어야 한다
virtual void NativeTick(const FGeometry& MyGeometry, float InDeltaTime) override;
// IDirtyUpdateUIInterface 의 함수를 오버라이딩한다
virtual void UpdateUIInternal() override;
// UObject 오버라이딩
virtual void BeginDestroy() override;
protected:
// HP 바 클릭
UFUNCTION()
void OnClickHPBar();
private:
void UpdateHP();
};
HPBar.cpp
void UHPBar::NativeTick(const FGeometry& MyGeometry, float InDeltaTime)
{
IDirtyUpdateUIInterface::CheckUpdate();
Super::NativeTick(MyGeometry, InDeltaTime);
}
void UHPBar::NativeOnInitialized()
{
// 기타 필요한 작업
Super::NativeOnInitialized();
}
void UHPBar::NativeDestruct()
{
// 기타 필요한 작업
Super::NativeDestruct();
}
void UHPBar::InvalidateAllInternal()
{
UpdateHP();
}
void UHPBar::BeginDestroy()
{
// 기타 필요한 작업
Super::BeginDestroy();
}
void UHPBar::OnClickHPBar()
{
// 기타 필요한 작업
}
void UHPBar::UpdateList()
{
// 기타 필요한 작업
// HP 바에 들어갈 수치를 조정해 주는 코드가 들어갈 것이다
}
위의 코드에서의 핵심은, 바로 NativeTick 에서 IDirtyUpdateUIInterface::CheckUpdate() 를 호출하는 부분이다!
더티 플래그를 이용하면 UI 갱신의 흐름이 다음과 같이 정립된다 :
- 어떤 이벤트가 발생 했을 때, 실제로 업데이트를 하는 것이 아니라 더티 플래그만 바꾸어 준다.
- 다음 틱(NativeTick) 에서 더티 플래그를 체크해 플래그가 ON 되었을 경우, UI 를 업데이트해 준다.
위의 HPBar 을 상속받은 위젯이 있다고 가정해 보자. UI 업데이트의 순서가 (하위에 있는 것) -> (상위에 있는 것) 순으로 이루어지므로, 기존 더티 플래그 패턴의 이점인, Hierarchy 가 있을 때의 업데이트 비효율성 문제도 해결됨을 파악할 수 있다. 호출 순서를 보면, Super::NativeTick(...) 을 현재 필요한 UI 업데이트 작업 이후에 수행하기 때문이다!
'Game Dev > Unreal C++ : Dev Log' 카테고리의 다른 글
[언리얼] Slate(슬레이트)의 구조와 철학 (공식 문서 해설 및 정리) (0) | 2022.12.07 |
---|---|
[언리얼] 위젯(UUserWidget) 과 Native 함수들, UObject Interface (0) | 2022.12.06 |
[언리얼] Enemy 의 MoveTo 걷는 애니메이션 재생 안됨 (0) | 2022.08.31 |
[언리얼] 설치시, .uproject 파일 링크하기 (Generate Visual Studio project files 띄우기) (0) | 2022.08.29 |
[언리얼] Unreal unresolved external symbol 에러 (0) | 2022.07.20 |