1. 업그레이드 카드

(1) 뷰(View) 구성

업그레이드 제목, 설명, 최대 레벨 수를 표시하는 카드 UI를 만든다.

 

 

(2) 뷰 모델(View Model) 구성

#pragma once

#include "CoreMinimal.h"
#include "GameplayTagContainer.h"
#include "MVVMViewModelBase.h"
#include "MVVM_AbilityCard.generated.h"

DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnUpgradeSelected, FGameplayTag, SelectedUpgradeTag);

struct FGameplayTag;

UCLASS()
class AURA_API UMVVM_AbilityCard : public UMVVMViewModelBase
{
    GENERATED_BODY()

public:
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
    FGameplayTag UpgradeTag;

    UPROPERTY()
    int32 CardIndex;

public:
    FOnUpgradeSelected OnUpgradeSelectedDelegate;
    
public:
    FString GetUpgradeName() const { return UpgradeName; }
    FString GetUpgradeDescription() const { return UpgradeDescription; }
    int32 GetUpgradeMaxLevel() const { return UpgradeMaxLevel; }

    void SetUpgradeName(FString InUpgradeName);
    void SetUpgradeDescription(FString InUpgradeDescription);
    void SetUpgradeMaxLevel(int32 InUpgradeMaxLevel);

    UFUNCTION(BlueprintCallable)
    void UpgradeButtonClicked();

private:
    /*필드 노티파이*/
    UPROPERTY(EditAnywhere, BlueprintReadWrite, FieldNotify, Setter, Getter, meta =(AllowPrivateAccess="true"))
    FString UpgradeName;
    
    UPROPERTY(EditAnywhere, BlueprintReadWrite, FieldNotify, Setter, Getter, meta =(AllowPrivateAccess="true"))
    FString UpgradeDescription;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, FieldNotify, Setter, Getter, meta =(AllowPrivateAccess="true"))
    int32 UpgradeMaxLevel;
};

이름, 정보, 최대 레벨의 정보를 필드 노티파이를 이용하여 나타낼 것이다.


- MVVM 모델에서 뷰 모델의 한계

뷰 모델은 모델(Model), 즉 데이터를 가공하지 않고, 데이터를 뷰로 전달하는 역할만 한다.

따라서 버튼을 클릭하면 델리게이트를 이용하여 모델 내에서 데이터를 가공하고 반영하도록 해야 한다.


#include "UI/ViewModel/MVVM_AbilityCard.h"

void UMVVM_AbilityCard::SetUpgradeName(FString InUpgradeName)
{
	UE_MVVM_SET_PROPERTY_VALUE(UpgradeName, InUpgradeName);
}

void UMVVM_AbilityCard::SetUpgradeDescription(FString InUpgradeDescription)
{
	UE_MVVM_SET_PROPERTY_VALUE(UpgradeDescription, InUpgradeDescription);
}

void UMVVM_AbilityCard::SetUpgradeMaxLevel(int32 InUpgradeMaxLevel)
{
	UE_MVVM_SET_PROPERTY_VALUE(UpgradeMaxLevel, InUpgradeMaxLevel);
}

void UMVVM_AbilityCard::UpgradeButtonClicked()
{
	// 바인딩은 플레이어 컨트롤러에서!!
	OnUpgradeSelectedDelegate.Broadcast(UpgradeTag);
}

매크로를 사용하여 값을 Set 하도록 설정해야 한다.

 

 

2. 카드 선택 UI

(1) 뷰 구성

위의 카드 슬롯을 세 개 배치한다.

 

 

(2) 뷰 모델 구성

#pragma once

#include "CoreMinimal.h"
#include "MVVMViewModelBase.h"
#include "MVVM_CardSelection.generated.h"

class UMVVM_AbilityCard;
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FEnableSelectButton, bool, bEnable);

UCLASS()
class AURA_API UMVVM_CardSelection : public UMVVMViewModelBase
{
	GENERATED_BODY()
	
public:
	void InitializeSlot();

public:
	UFUNCTION(BlueprintPure)
	UMVVM_AbilityCard* GetCardViewModelByIndex(int32 Index);

	int32 GetNumCards() { return AbilityCards.Num(); }
	
public:
	UPROPERTY(EditDefaultsOnly)
	TSubclassOf<UMVVM_AbilityCard> AbilityCardViewModelClass;

private:
	UPROPERTY()
	TMap<int32, UMVVM_AbilityCard*> AbilityCards;

	UPROPERTY()
	TObjectPtr<UMVVM_AbilityCard> Card_0;
	
	UPROPERTY()
	TObjectPtr<UMVVM_AbilityCard> Card_1;
	
	UPROPERTY()
	TObjectPtr<UMVVM_AbilityCard> Card_2;
};

세 개의 카드의 뷰 모델을 저장하여 가지고 있도록 한다. 맵에 각 인덱스 별로 하나 씩 지정한다.

 

#include "UI/ViewModel/MVVM_CardSelection.h"

#include "AuraGameplayTags.h"
#include "UI/ViewModel/MVVM_AbilityCard.h"

void UMVVM_CardSelection::InitializeSlot()
{
	Card_0 = NewObject<UMVVM_AbilityCard>(this, AbilityCardViewModelClass);
	Card_0->CardIndex = 0;
	Card_0->UpgradeTag = FAuraGameplayTags::Get().Abilities_None;
	
	Card_1 = NewObject<UMVVM_AbilityCard>(this, AbilityCardViewModelClass);
	Card_1->CardIndex = 1;
	Card_1->UpgradeTag = FAuraGameplayTags::Get().Abilities_None;

	Card_2 = NewObject<UMVVM_AbilityCard>(this, AbilityCardViewModelClass);
	Card_2->CardIndex = 2;
	Card_2->UpgradeTag = FAuraGameplayTags::Get().Abilities_None;

	AbilityCards.Add(0, Card_0);
	AbilityCards.Add(1, Card_1);
	AbilityCards.Add(2, Card_2);
}

UMVVM_AbilityCard* UMVVM_CardSelection::GetCardViewModelByIndex(int32 Index)
{
	return AbilityCards.FindChecked(Index);
}

업그레이드 카드의 뷰 모델 클래스를 이용하여 업그레이드 카드의 뷰 모델을 생성한 이후, 각 카드 슬롯의 인덱스와 업그레이트 태그를 초기화한다.

 

 

(3) 카드 선택 UI에 뷰 모델 연결

 

(3-1) 부모 변경

카드 선택 UI인 WBP_CardSelection의 부모를 LoadScreenWidget 클래스를 상속받는 WBP_CardSelection_Base를 생성하여 이로 교체한다.

LoadScreenWidget 클래스는 이전에 불러오기 UI에서 사용한 MVVM 모델의 위젯 중 기본적인 클래스이다.

 

 

(3-2) WBP_CardSelection_Base에서 뷰 모델 찾기 함수

HUD에 저장된 카드 선택 UI의 뷰 모델을 가져와 반환한다.

 

위의 과정을 통해 카드 선택 UI가 올바른 뷰 모델을 가지게 되었다.

 

 

(4) WBP_CardSelection 이벤트 그래프

저장된 카드 위젯(뷰)들에 대해 카드를 초기화하는 함수를 호출한다.

 

 

(4-1) WBP_UpgradeCard에 뷰 모델 할당하기

카드의 뷰 모델은 Manual로 생성하여 따로 지정할 것이다.

 

 

(4-2) 뷰 모델의 블루프린트 버전 생성

카드 선택 UI의 블루프린트 버전을 생성하고 어빌리티 카드의 뷰 모델의 클래스를 지정한다.

카드의 블루프린트 버전을 생성한다.

 

 

(4-3) WBP_UpgradeCard의 InitializeCard 함수

카드의 뷰 모델을 Manual로 지정하기로 했기 때문에, Set MVVM Ability Card 함수를 사용해 뷰 모델을 직접 지정해야 한다.

WBP_UpgradeCard와 WBP_CardSelection의 부모는 WBP_CardSelection_Base 로 되어 있기 때문에, Find Card Selection View Model 함수를 사용할 수 있다.

이 함수를 사용해 카드 선택 UI의 뷰 모델에 접근해서 맵에서 카드 슬롯 인덱스와 매칭된 뷰 모델을 꺼내와서 업그레이드 카드의 뷰 모델을 가져올 수 있다.


- 카드 선택 UI의 뷰 모델을 하위 뷰 모델에서 참조해도 되는가?

카드의 뷰 모델을 가져오기 위해, 상위 뷰 모델의 맵을 참조하는 것은 약한 종속성을 가지기 때문에 괜찮다.

단, 추후에 상위 UI의 뷰 모델을 참조하지 않는 방향을 유지해야 한다.