본문 바로가기

게임프로그래밍/실습2

[실습2] 18. 위젯 컨트롤러로 설정하기

1. 위젯컨트롤러 변수들 할당하기

 

전에 위젯 컨트롤러 클래스를 만들고 변수들을 선언하기는 했다. 하지만 선언만 했을 분 실제로 할당하지는 않았는데 이 변수들을 할당하는 방법을 알아보자.

 

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;

 

현재 위젯 컨트롤러에는 위와 같은 변수들이 있다. 그리고 이 변수들은 반드시 채워져야하는 요소이고 하나하나 채우기 보다는 한번에 채우면 까먹고 특정 변수를 채우지 못하는 일을 방지할 수 있다.

 

그렇게 하기 위해 위의 주요 4개의 변수가 포함된 구조체를 만드려고 한다. 구조체를 만들 경우 나중에 변수를 손 쉽게 추가하거나 제거하는 것도 가능해진다.

 

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;
    
};

 

구조체를 위와 같이 만들어 주었다.

 

이제 이 구조체를 이용하면 위젯 컨트롤러를 쉽게 초기화할 수 있다. 다른 클래스에서는 구조체를 만들고 이 구조체의 값을 위젯 컨트롤러에 있는 변수들에 할당하면 된다.

 

이제 이 구조체를 컨트롤러 변수에 할당을 하는 함수를 만들자.

class PRACTICE2_API UAuraWidgetController : public UObject
{
    GENERATED_BODY()
    
public:
    UFUNCTION(BlueprintCallable)
    void SetWidgetControllerParams(const FWidgetControllerParams& WCParams);

 

위젯 컨트롤러 클래스에 함수를 선언했으니 이제 정의해보자.

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

 

이제 구조체를 만들고 함수를 호출하기만 하면 컨트롤러 클래스가 채워지게 된다.

 


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

 

지금까지 만든 위젯 컨트롤러를 베이스 컨트롤러로 하여 다양한 자식 컨트롤러들을 추가로 만들 것이다. 지금은 오버레이 위젯을 위한 컨트롤러를 생성할 것이다.

 

컴파일하고 에디터로 돌아가 자식 클래스를 만들도록 하자.

 

 

오버레이 위젯 컨트롤러를 만들었다.

 

이제 실제로 위젯 컨트롤러를 생성하도록하자. HUD 클래스로 들어가자.


3. HUD 클래스에서 위젯컨트롤러 생성함수 만들기

 

HUD 클래스에서 위젯 컨트롤러가 아직 생성되지 않았을 경우 위젯 컨트롤러를 만들고 이를 리턴하며 위젯 컨트롤러가 존재하면 기존에 존재하는 컨트롤러를 리턴할 함수를 구현할 것이다.

 

이렇게 하면 일종의 싱글톤 디자인으로 취급된다고 한다.

 

프라이빗 섹션에 컨트롤러를 저장할 변수를 선언한다.

private:
    UPROPERTY()
    TObjectPtr<UOverlayWidgetController> OverlayWidgetController;
    
    UPROPERTY(EditAnywhere)
    TSubclassOf<UOverlayWidgetController> OverlayWidgetControllerClass;
TSubclassOf 포인터는 오버레이 위젯 컨트롤러를 만들때 필요한 변수이다.

 

 

이제 퍼블릭 섹션에 함수를 선언하고 구현하도록 하자.

UOverlayWidgetController* GetOverlayWidgetController(const FWidgetControllerParams& WCParams);
UOverlayWidgetController* AAuraHUD::GetOverlayWidgetController(const FWidgetControllerParams& WCParams)
{
    if (OverlayWidgetController == nullptr)
    {
       OverlayWidgetController = NewObject<UOverlayWidgetController>(this, OverlayWidgetControllerClass);
       OverlayWidgetController->SetWidgetControllerParams(WCParams);
    }

    return OverlayWidgetController;
}

 

이제 게터가 완성되었으니 이 게터를 이용해 오버레이위젯컨트롤러를 초기화하고 값을 가져올 수 있다.

 

이제 이 함수를 호출할 곳을 만들어보자. 더이상 비긴플레이에서 위젯을 추가하거나 하지 않을 것이다.

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

 

위의 함수에서 오버레이 위젯 컨트롤러를 초기화 할 것이다. 비긴플레이는 삭제하도록 하자.

 

void AAuraHUD::InitOverlay(APlayerController* PC, APlayerState* PS, UAbilitySystemComponent* ASC, UAttributeSet* AS)
{
    checkf(OverlayWidgetClass, TEXT("Overlay Widget Class uninitialized, fill out BP_AuraHUD"));
    checkf(OverlayWidgetControllerClass, TEXT("Overlay Widget Controller Class uninitialized, fill out 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();
    
}

 

이제 적절한 시점에 위의 함수를 호출하면 위젯이 화면에 추가되는 것은 물론 위젯 컨트롤러까지 할당이 된다. 그렇다면 이 함수를 호출할 곳은 어디일까?


4.  위젯 생성함수 호출하기

 

위의 함수를 언제 호출해야할까? 매개변수를 보면 그 힌트를 얻을 수 있다. 위젯을 제대로 설정하기 위해서는 GAS가 초기화 되어있어야 할 것이며 이러한 초기화는 캐릭터 클래스에서 진행되고 있다.

 

void AAuraCharacter::InitAbilityActorInfo()
{
    AAuraPlayerState* AuraPlayerState = GetPlayerState<AAuraPlayerState>();
    check(AuraPlayerState);
    AuraPlayerState->GetAbilitySystemComponent()->InitAbilityActorInfo(AuraPlayerState, this);
    AbilitySystemComponent = AuraPlayerState->GetAbilitySystemComponent();
    AttributeSet = AuraPlayerState->GetAttributeSet();
}

 

캐릭터 클래스에서 해당 요소들을 전부 설정하는 함수가 있다. 이곳에서 해당 변수들이 더이상 널포인트가 아닌 것을 확신할 수 있다.

 

이제 이곳에서 HUD에 접근하기만 한다. 그리고 플레이어 컨트롤러에서는 항상 HUD에 접근할 수 있다.

 

위의 함수에 구문을 추가하자.

 

if (AAuraPlayerController* AuraPlayerController = Cast<AAuraPlayerController>(GetController()))
{
    if (AAuraHUD* AuraHUD = Cast<AAuraHUD>(AuraPlayerController->GetHUD()))
    {
       AuraHUD->InitOverlay(AuraPlayerController, AuraPlayerState, AbilitySystemComponent, AttributeSet);
    }
}

 

이제 컴파일하고 제대로 되는지 확인하면 된다.