0. 개요

위젯과 데이터를 연결하는 위젯 컨트롤러를 생성하여 오버레이 위젯과 연결하도록 할 것이다.

 

1. 위젯 컨트롤러 초기화

(1) 기존 코드

UCLASS()
class AURA_API UAuraWidgetController : public UObject
{
	GENERATED_BODY()

protected:
	// 종속성 설정
	// 플레이어 스테이트 , 플레이어 컨트롤러, 속성 세트, 어빌리티 시스템 컴포넌트
	UPROPERTY(BlueprintReadOnly, Category="WidgetController")
	TObjectPtr<APlayerController> PlayerController;

	UPROPERTY(BlueprintReadOnly, Category="WidgetController")
	TObjectPtr<APlayerState> PlayerState;

	UPROPERTY(BlueprintReadOnly, Category="WidgetController")
	TObjectPtr<UAbilitySystemComponent> AbilitySystemComponent;

	UPROPERTY(BlueprintReadOnly, Category="WidgetController")
	TObjectPtr<UAttributeSet> AttributeSet;
};

기존에 위젯 컨트롤러를 사용할 때 필요한 포인터를 설정했었는데, 이제 이런 멤버 변수들을 구조체를 통해 쉽게 초기화할 수 있도록 위젯 컨트롤러 파라미터 구조체를 만든다.

 

 

(2) 구조체 정의하기

USTRUCT(BlueprintType)
struct FWidgetControllerParams
{
	GENERATED_BODY()

	FWidgetControllerParams() {}
	FWidgetControllerParams(APlayerController* PC, APlayerState* PS, UAbilitySystemComponent* ASC, UAttributeSet* AS)
	: PlayerController(PC), PlayerState(PS), AbilitySystemComponent(ASC), AttributeSet(AS) {}

	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	TObjectPtr<APlayerController> PlayerController = nullptr;

	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	TObjectPtr<APlayerState> PlayerState = nullptr;

	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	TObjectPtr<UAbilitySystemComponent> AbilitySystemComponent = nullptr;

	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	TObjectPtr<UAttributeSet> AttributeSet = nullptr;
};

해당 구조체는 인수로 플레이어 컨트롤러와 플레이어 스테이트, 어빌리티 시스템 컴포넌트, 속성 세트의 4가지 인수를 입력받아 멤버 변수로 초기화하게 된다.

 

 

(3) 위젯 컨트롤러에서 구조체로 멤버 변수를 초기화하는 함수 정의

UCLASS()
class AURA_API UAuraWidgetController : public UObject
{
	GENERATED_BODY()

public:
	UFUNCTION(BlueprintCallable)
	void SetWidgetControllerParams(const FWidgetControllerParams& WCParams);

SetWidgetControllerParams 함수를 이용해 받아온 구조체의 참조를 이용하여 멤버 변수를 초기화하게 한다.

#include "UI/WidgetController/AuraWidgetController.h"

void UAuraWidgetController::SetWidgetControllerParams(const FWidgetControllerParams& WCParams)
{
    PlayerController = WCParams.PlayerController;
    PlayerState = WCParams.PlayerState;
    AbilitySystemComponent = WCParams.AbilitySystemComponent;
    AttributeSet = WCParams.AttributeSet;
}

 

즉, 초기화 순서는 다음과 같다.

- 구조체 생성자 오버로딩

-> SetWidgetControllerParams 함수의 인수로 구조체의 참조를 넘김

-> 위젯 컨트롤러의 4가지 멤버 변수 초기화

 

이제 이 위젯 컨트롤러 클래스를 상속받아 각 위젯을 데이터와 연결할 수 있게 된다.

 

 

2. 오버레이 위젯 컨트롤러

이제 HUD에서 위젯 컨트롤러를 생성할 것이다.

시작하기 전, 위젯 컨트롤러 클래스를 상속하는 OverlayWidgetController 클래스를 생성한다.

 

(1) 코드

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/HUD.h"
#include "AuraHUD.generated.h"

class UAuraUserWidget;
class UOverlayWidgetController;
class UAbilitySystemComponent;
class UAttributeSet;
struct FWidgetControllerParams;

UCLASS()
class AURA_API AAuraHUD : public AHUD
{
	GENERATED_BODY()

public:
	UPROPERTY()
	TObjectPtr<UAuraUserWidget> OverlayWidget;

	UOverlayWidgetController* GetOverlayWidgetController(const FWidgetControllerParams& WCParams);

	void InitOverlay(APlayerController* PC, APlayerState* PS, UAbilitySystemComponent* ASC, UAttributeSet* AS);

private:
	UPROPERTY(EditAnywhere)
	TSubclassOf<UAuraUserWidget> OverlayWidgetClass;

	UPROPERTY(EditAnywhere)
	TSubclassOf<UOverlayWidgetController> OverlayWidgetControllerClass;

	UPROPERTY(EditAnywhere)
	TObjectPtr<UOverlayWidgetController> OverlayWidgetController;
};

싱글톤 매니저 클래스 같은 역할을 하게 될 것이다.

GetOverlayWidgetController를 통해 오버레이 위젯 컨트롤러를 꺼내올 수 있다.

 

#include "UI/HUD/AuraHUD.h"
#include "UI/Widget/AuraUserWidget.h"
#include "UI/WidgetController/OverlayWidgetController.h"
#include "AbilitySystem/AuraAbilitySystemComponent.h"
#include "AbilitySystem/AuraAttributeSet.h"

UOverlayWidgetController *AAuraHUD::GetOverlayWidgetController(const FWidgetControllerParams &WCParams)
{
    if (OverlayWidgetController == nullptr)
    {   // 없으면 생성
        OverlayWidgetController = NewObject<UOverlayWidgetController>(this, OverlayWidgetControllerClass);
        OverlayWidgetController->SetWidgetControllerParams(WCParams);

        return OverlayWidgetController;
    }

    return OverlayWidgetController;
}

위젯 컨트롤러는 UObject 타입이므로 NewObject 템플릿 함수를 사용한다.

NewObject 함수는 첫 인자로 해당 오브젝트를 소유할 클래스, 두번째 인자로 생성할 오브젝트의 클래스를 받는다.

 

이제 주요 변수 4개(플레이어 컨트롤러, 플레이어 스테이트, 어빌리티 시스템 컴포넌트, 속성 세트)를 받아와 FWidgetControllerParams에 대입하고 GetOverlayWidgetController에 인자로 넘겨주면 된다.

 

 

(2) HUD에서 주요 변수를 위젯 컨트롤러로 가져오기

초기화 함수를 새로 만든다.

void AAuraHUD::InitOverlay(APlayerController *PC, APlayerState *PS, UAbilitySystemComponent *ASC, UAttributeSet *AS)
{
    // 위젯과 위젯 컨트롤러 생성
    checkf(OverlayWidgetClass, TEXT("오버레이 위젯 클래스가 초기화되지 않음. BP_AuraHUD"));
    checkf(OverlayWidgetControllerClass, TEXT("오버레이 위젯 컨트롤러 클래스가 초기화되지 않음. BP_AuraHUD"));

    // 위젯 생성 후 오라 유저 위젯으로 캐스팅
    UUserWidget* Widget = CreateWidget<UUserWidget>(GetWorld(), OverlayWidgetClass);
    OverlayWidget = Cast<UAuraUserWidget>(Widget);

    // 구조체에 할당 후 오버레이 위젯 컨트롤러를 초기화
    const FWidgetControllerParams WidgetControllerParams(PC, PS, ASC, AS);
    UOverlayWidgetController* WidgetController = GetOverlayWidgetController(WidgetControllerParams);

    // 위젯에 위젯 컨트롤러를 연결
    OverlayWidget->SetWidgetController(WidgetController);
    
    Widget->AddToViewport();
}

기존 BeginPlay에서 위젯을 뷰포트에 추가하던 로직을 전부 제거한다.

오버레이 위젯과 위젯 컨트롤러 클래스가 정상적으로 활성화 되었는지 확인한다.

이제 4가지 인자를 이용해 오버레이 위젯 컨트롤러를 초기화한다.

마지막으로 생성한 위젯 컨트롤러를 위젯에 연결해주면 된다.

 

 

(3) InitOverlay 함수를 어디에서 호출할까?

이제 위의 4가지 변수에 접근할 수 있는 곳에서 InitOverlay 함수를 호출한다.

적절한 위치는 AuraCharacter 클래스이다(4가지 변수에 접근 가능한 클래스).

 

HUD는 플레이어 컨트롤러를 통해서 접근할 수 있다.

void AAuraCharacter::InitAbilityActorInfo()
{
    AAuraPlayerState* AuraPlayerState = GetPlayerState<AAuraPlayerState>();
    check(AuraPlayerState);
    
    // 소유자 액터, 아바타 액터
    AuraPlayerState->GetAbilitySystemComponent()->InitAbilityActorInfo(AuraPlayerState, this);
    
    // ASC와 특성 세트를 가져와서 할당
    AbilitySystemComponent = AuraPlayerState->GetAbilitySystemComponent();
    AttributeSet = AuraPlayerState->GetAttributeSet();

    // 이 클라이언트가 조작하는 컨트롤러가 아니면 null로 표시됨 -> null check 필요
    if (AAuraPlayerController* AuraPlayerController = Cast<AAuraPlayerController>(GetController()))
    {
        if (AAuraHUD* AuraHUD = Cast<AAuraHUD>(AuraPlayerController->GetHUD()))
        {
            AuraHUD->InitOverlay(AuraPlayerController, AuraPlayerState, AbilitySystemComponent, AttributeSet);
        }
    }
}

이전에 어빌리티 액터 정보를 초기화하던 함수의 끝에 HUD를 컨트롤러에서 가져와 오버레이를 초기화하도록 한다.

 

 

(4) HUD에 오버레이 위젯 컨트롤러 할당

BP_AuraHUD에서 Overlay Widget Controller Class를 할당해줘야 한다.

OverlayWidgetController에 대한 블루프린트를 생성해야 할 수 도 있지만 지금은 C++ 클래스로 지정하고 테스트한다.

 

 

(5) 위젯 컨트롤러가 올바르게 할당되고 있는지 확인하기

WBP_Overlay의 이벤트 그래프에서 다음과 같이 설정한다.

실행 버튼을 눌렀을 때 위젯 컨트롤러의 이름이 출력되면 정상적으로 할당된 것이다.