이번 포스트에서는 본격적인 MVC 패턴을 사용하여 Controller의 역할을 수행하는
WidgetController를 설계하겠습니다.
1) MVC 패턴에서 Controller의 역할을 수행하는 AAuraWidgetController
Controller는 Model(=데이터)과 View(=UI)를 왕복하며 움직여야 합니다.
따라서 양쪽으로부터 접근이 가능해야 하므로 다음과 같이 코드를 재구성하였습니다.
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "UObject/NoExportTypes.h"
#include "AuraWidgetController.generated.h"
class APlayerController;
class APlayerState;
class UAbilitySystemComponent;
class UAttributeSet;
USTRUCT(BlueprintType)
struct FWidgetControllerParams
{
GENERATED_BODY()
FWidgetControllerParams() {}
FWidgetControllerParams(
APlayerController* InPlayerController,
APlayerState* InPlayerState,
UAbilitySystemComponent* InAbilitySystemComponent,
UAttributeSet* InAttributeSet
)
: PlayerController(InPlayerController),
PlayerState(InPlayerState),
AbilitySystemComponent(InAbilitySystemComponent),
AttributeSet(InAttributeSet)
{
}
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;
};
/**
*
*/
UCLASS()
class AURA_API UAuraWidgetController : public UObject
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintCallable)
void SetWidgetControllerParams(const FWidgetControllerParams& WCParams);
protected:
/*
* View Section
*/
// UI 입력시에 입력을 Model(=데이터)로 전달
UPROPERTY(BlueprintReadOnly, Category = "WidgetController")
TObjectPtr<APlayerController> PlayerController;
// Model(=데이터)를 View(=UI)로 전달
UPROPERTY(BlueprintReadOnly, Category = "WidgetController")
TObjectPtr<APlayerState> PlayerState;
/*
* Model Section
*/
// AttributeSet을 전달하기 위한 ASC, Model 단계에 위치함
UPROPERTY(BlueprintReadOnly, Category = "WidgetController")
TObjectPtr<UAbilitySystemComponent> AbilitySystemComponent;
// Model 단계에 위치함
UPROPERTY(BlueprintReadOnly, Category = "WidgetController")
TObjectPtr<UAttributeSet> AttributeSet;
};
코드가 상당히 길지만 내용 자체는 많지 않습니다.
기존에 멤버 변수로 들고 있던 Model의 포인터와 View의 포인터를
구조체를 사용하여 초기화하도록 하였습니다.
이 경우에 코드의 변형이나 수정이 쉬어지므로 이 설정을 유지하겠습니다.
#include "UI/WidgetController/AuraWidgetController.h"
void UAuraWidgetController::SetWidgetControllerParams(const FWidgetControllerParams& WCParams)
{
PlayerController = WCParams.PlayerController;
PlayerState = WCParams.PlayerState;
AbilitySystemComponent = WCParams.AbilitySystemComponent;
AttributeSet = WCParams.AttributeSet;
}
SetWidgetController 함수에서는 Controller를 위한 포인터 설정을
구조체의 멤버로 설정합니다.
2) OverlayWidget을 위한 Controller인 AuraOverlayWidgetController
#pragma once
#include "CoreMinimal.h"
#include "UI/WidgetController/AuraWidgetController.h"
#include "AuraOverlayWidgetController.generated.h"
/**
*
*/
UCLASS()
class AURA_API UAuraOverlayWidgetController : public UAuraWidgetController
{
GENERATED_BODY()
public:
UAuraOverlayWidgetController();
};
별다른 내용은 없으므로 생략하겠습니다.
3) 플레이어를 위한 UI의 모음인 AAuraHUD
HUD에서는 모든 종류의 UI에 대해서 Controller를 설정하게 됩니다.
이때 각기 다른 Widget에 대해 WidgetController를 하나씩만 들고 있게 됩니다.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/HUD.h"
#include "AuraHUD.generated.h"
class UAuraUserWidget;
class UAuraOverlayWidgetController;
class UAbilitySystemComponent;
class UAttributeSet;
struct FWidgetControllerParams;
/**
*
*/
UCLASS()
class AURA_API AAuraHUD : public AHUD
{
GENERATED_BODY()
public:
UAuraOverlayWidgetController* GetOverlayWidgetController(const FWidgetControllerParams& WCParams);
void InitOverlay(
APlayerController* InPlayerController,
APlayerState* InPlayerState,
UAbilitySystemComponent* InAbilitySystemComppnent,
UAttributeSet* InAttributeSet
);
protected:
virtual void BeginPlay() override;
private:
/*
* Widgets and WidgetControllers
*/
UPROPERTY()
TObjectPtr<UAuraUserWidget> OverlayWidget;
UPROPERTY(EditAnywhere)
TSubclassOf<UAuraUserWidget> OverlayWidgetClass;
UPROPERTY()
TObjectPtr<UAuraOverlayWidgetController> AuraOverlayWidgetController;
UPROPERTY(EditAnywhere)
TSubclassOf<UAuraOverlayWidgetController> AuraOverlayWidgetControllerClass;
};
함수 GetOverlayWidgetController는 OverlayWidget에 대한 WidgetController를 가져오게 됩니다.
OverlayWidgetController가 null이라면 생성하고 설정한 후 return하지만,
그렇지 않은 경우 바로 return하게 됩니다.
#include "UI/HUD/AuraHUD.h"
#include "UnrealWidgetFwd.h"
#include "UI/Widgets/AuraUserWidget.h"
#include "UI/WidgetController/AuraOverlayWidgetController.h"
// HUD에서 OverlayWidgetController를 가져오는 함수, 이때 가져오는 OverlayWidgetController는 항상 1개를 유지함
UAuraOverlayWidgetController* AAuraHUD::GetOverlayWidgetController(const FWidgetControllerParams& WCParams)
{
if (AuraOverlayWidgetController == nullptr)
{
AuraOverlayWidgetController = NewObject<UAuraOverlayWidgetController>(this, AuraOverlayWidgetControllerClass);
AuraOverlayWidgetController->SetWidgetControllerParams(WCParams);
}
return AuraOverlayWidgetController;
}
또 다른 함수 InitOverlay의 경우 OverlayWidget을 설정하게 됩니다.
기존에 BeginPlay 함수에서 수행하던 로직에서 다음과 같이 변경하였습니다.
// HUD에서 OverlayWidget을 설정하는 함수
void AAuraHUD::InitOverlay(APlayerController* InPlayerController, APlayerState* InPlayerState, UAbilitySystemComponent* InAbilitySystemComppnent, UAttributeSet* InAttributeSet)
{
// 우선 OverlayWidget 클래스와 OverlayWidgetController 클래스가 존재하는지 확인
checkf(OverlayWidgetClass, TEXT("OverlayWidgetClass uninitialized, fill out BP_AuraHUD"));
checkf(AuraOverlayWidgetControllerClass, TEXT("OverlayWidgetControllerClass uninitialized, fill out BP_AuraHUD"));
// 새로운 OverlayWidgetClass를 생성하고 생성된 OverlayWidget이 AuraWUserWidget에서 상속되었는지 확인
UUserWidget* Widget = CreateWidget<UUserWidget>(GetWorld(), OverlayWidgetClass);
OverlayWidget = Cast<UAuraUserWidget>(Widget);
// OverlayWidgetController를 설정하기 위한 과정 -> 변수를 가져오고 이 변수를 통해 OverlayWidgetController를 생성
const FWidgetControllerParams WidgetControllerParams(InPlayerController, InPlayerState, InAbilitySystemComppnent, InAttributeSet);
UAuraOverlayWidgetController* WidgetController = GetOverlayWidgetController(WidgetControllerParams);
// 생성한 OverlayWidgetController를 생성한 OverlayWidget에 설정해주고 OverlayWidget을 뷰포트에 생성
OverlayWidget->SetWidgetController(WidgetController);
Widget->AddToViewport();
}
과정은 총 4가지로 나뉩니다.
우선 생성할 OverlayWidget과 OverlayWidgetController가 블루 프린트에서 설정되었는지 확인합니다.
두번째로 OverlayWidgetClass를 생성하고 AuraUserWidget에서 상속되었는지 확인합니다.
세번째로 생성된 Widget에 대해 설정할 OverlayWidgetController를 생성합니다.
마지막으로 생성한 Widget의 OverlayWidgetController를 설정하고 뷰포트에 생성합니다.
위의 InitOverlay 함수는 AuraPlayer 클래스의 SetAbilityActorInfo 함수에서 호출합니다.
그 이유는 다음과 같습니다.
첫번째로 HUD는 PlayerController에서 GetHUD 함수로 바로 접근이 가능합니다.
두번째로 WidgetController를 설정하는데 필요한 변수들을 전부 초기화해주기 때문입니다.
그 과정은 다음과 같습니다.
4) AAuraPlayer의 SetAbilityActorInfo()
.
.
.
void AAuraPlayer::SetAbilityActorInfo()
{
// 서버단계에서 ASC의 OwnerActor와 AvatarActor를 초기화 + BaseCharacter에서 상속된 멤버들을 PlayerState 값으로 초기화
AAuraPlayerState* AuraPlayerState = CastChecked<AAuraPlayerState>(GetPlayerState());
AuraPlayerState->GetAbilitySystemComponent()->InitAbilityActorInfo(AuraPlayerState, this);
AbilitySystemComponent = AuraPlayerState->GetAbilitySystemComponent();
AttributeSet = AuraPlayerState->GetAttributeSet();
AAuraPlayerController* AuraPlayerController = CastChecked<AAuraPlayerController>(GetController());
AAuraHUD* AuraHUD = CastChecked<AAuraHUD>(AuraPlayerController->GetHUD());
AuraHUD->InitOverlay(AuraPlayerController, AuraPlayerState, AbilitySystemComponent, AttributeSet);
}
우선 PlayerController를 가져옵니다.
그 후에 PlayerController로부터 HUD를 가져옵니다.
마지막으로 가져온 HUD에서 InitOverlay를 수행합니다.
최종적으로 HUD의 블루 프린트에서 OverlayWidgetClass와 OverlayWidgetControllerClass를 설정합니다.
위와 같이 잘 생성되는 것을 확인할 수 있습니다.
'언리얼 > 게임 프로젝트' 카테고리의 다른 글
GameplayAbilitySystem을 이용한 RPG 프로젝트 - (13) Model의 갱신에 따른 UI 변화 (0) | 2024.11.30 |
---|---|
GameplayAbilitySystem을 이용한 RPG 프로젝트 - (12) 체력 UI에 델리게이트 연결하기 (0) | 2024.11.29 |
GameplayAbilitySystem을 이용한 RPG 프로젝트 - (10) Overlay Widget과 HUD (0) | 2024.11.24 |
GameplayAbilitySystem을 이용한 RPG 프로젝트 - (9) UI를 설계하고 부모 체력 UI 만들기 (0) | 2024.11.23 |
GameplayAbilitySystem을 이용한 RPG 프로젝트 - (8) Attribute Set을 만들고 적용하기 (0) | 2024.11.22 |