0. 개요

델리게이트를 사용하여 HUD - GameMode - Player Controller 간 통신하여 UI와 게임 기능을 연결한다.

 

 

1. AuraHUD 구성

(1) HUD에서 카드 선택 UI 뷰와 뷰 모델 저장하기

public:
    // MVVM
    UPROPERTY(EditDefaultsOnly)
    TSubclassOf<UUserWidget> CardSelectionWidgetClass;

    UPROPERTY(BlueprintReadOnly)
    TObjectPtr<ULoadScreenWidget> CardSelectionWidget;

    UPROPERTY(EditDefaultsOnly)
    TSubclassOf<UMVVM_CardSelection> CardSelectionViewModelClass;

    UPROPERTY(BlueprintReadOnly)
    TObjectPtr<UMVVM_CardSelection> CardSelectionViewModel;

HUD에서 카드 선택 UI을 생성하고 초기화한다.

UMVVM_CardSelection* GetCardSelectionViewModel() { return CardSelectionViewModel; }

 

 

(2) 카드 선택 UI 생성

	void InitializeCardSelectionUI();
void AAuraHUD::InitializeCardSelectionUI()
{
    // 뷰 모델과 뷰 생성
    CardSelectionViewModel = NewObject<UMVVM_CardSelection>(this, CardSelectionViewModelClass);
    CardSelectionViewModel->InitializeSlot();

    CardSelectionWidget = CreateWidget<ULoadScreenWidget>(GetWorld(), CardSelectionWidgetClass);
    CardSelectionWidget->AddToViewport();

    CardSelectionWidget->BlueprintInitializeWidget();

    //
    ReceivedCardsDelegate.AddUObject(this, &AAuraHUD::HandleRandomAbilityUpgrade);
    
    // TODO::게임모드의 델리게이트 호출 -> 카드를 이니셜라이즈
    InitializeCardsDelegate.Broadcast(GetOwningPlayerController());
}

 

 

(3) 델리게이트 선언

// GameMode 초기화 이후 호출
DECLARE_MULTICAST_DELEGATE(FOnInitializeGameMode);

// PlayerController 초기화 이후 호출
DECLARE_MULTICAST_DELEGATE(FOnInitializePlayerController);

// GameMode에게 업그레이드 카드 요청
DECLARE_MULTICAST_DELEGATE_OneParam(FOnInitializeCards, APlayerController*);

// GameMode에게서 업그레이드 카드 받음
DECLARE_MULTICAST_DELEGATE_ThreeParams(FOnInitializeCardsReceived, FGameplayTag, FGameplayTag, FGameplayTag);
public:
	FOnInitializeGameMode OnInitializeGameModeDelegate;
	FOnInitializePlayerController OnInitializePlayerControllerDelegate;
	FOnInitializeCards InitializeCardsDelegate;
	FOnInitializeCardsReceived ReceivedCardsDelegate;

카드 선택 UI를 통해 업그레이드 태그를 아바타 액터에 적용하기 위해서는 게임모드 - HUD - 플레이어 컨트롤러 사이의 통신이 필요하다.

 

 

(4) 오버레이 초기화 시 델리게이트 바인딩

void AAuraHUD::InitOverlay(APlayerController *PC, APlayerState *PS, UAbilitySystemComponent *ASC, UAttributeSet *AS)
{
    // 위젯과 위젯 컨트롤러 생성
    checkf(OverlayWidgetClass, TEXT("Overlay Widget not Initialized. BP_AuraHUD"));
    checkf(OverlayWidgetControllerClass, TEXT("Overlay Widget Controller not Initialized. BP_AuraHUD"));

    // 위젯 생성 후 오라 유저 위젯으로 캐스팅
    UUserWidget* Widget = CreateWidget<UUserWidget>(GetWorld(), OverlayWidgetClass);
    OverlayWidget = Cast<UAuraUserWidget>(Widget);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    // 게임 모드가 초기화되면 업그레이드 카드 선택 UI 노출
    OnInitializeGameModeDelegate.AddUObject(this, &AAuraHUD::InitializeCardSelectionUI);
    
    // 플레이어 컨트롤러 초기화 완료 델리게이트 바인딩
    OnInitializePlayerControllerDelegate.AddUObject(this, &AAuraHUD::OnInitializePlayerController);
}

게임 모드와 플레이어 컨트롤러가 초기화 되면 델리게이트를 호출하고, 콜백 함수를 바인딩한다.

콜백 함수는 카드 선택 UI를 생성하는 함수이다.

 

 

2. 플레이어 컨트롤러에서 델리게이트 바인딩하기

(1) 카드 선택 UI 초기화 완료 후 호출되는 델리게이트 선언

public:
    /*
     * 어빌리티 업그레이드 카드
     */
    // 카드 선택 UI 초기화 완료시 호출되는 델리게이트
    FOnCardSelectionInitilized OnCardSelectionInitializedDelegate;

 

(2) 카드 선택 UI 초기화 델리게이트 바인딩

 
void AAuraPlayerController::BeginPlay()
{
    Super::BeginPlay();
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    // 델리게이트 바인딩
    OnCardSelectionInitializedDelegate.AddLambda([this]()
    {
        if (IsLocalPlayerController()) // 로컬 플레이어 컨트롤러에서만 UI 로직 처리
        {
            // HUD가 생성된 후 (또는 Upgrade UI가 활성화될 때)
            if (AAuraHUD* AuraHUD = Cast<AAuraHUD>(GetHUD()))
            {
                if (UMVVM_CardSelection* CardSelectionViewModel = AuraHUD->GetCardSelectionViewModel())
                {
                    // 각 CardViewModel의 OnUpgradeSelected 델리게이트 구독
                    for (int32 i = 0; i < CardSelectionViewModel->GetNumCards(); ++i) // GetNumCards는 예시
                    {
                        if (UMVVM_AbilityCard* CardViewModel = CardSelectionViewModel->GetCardViewModelByIndex(i))
                        {
                            CardViewModel->OnUpgradeSelectedDelegate.AddDynamic(this, &AAuraPlayerController::HandleAbilityCardSelected);
                        }
                    }
                }
            }
        }
    });

    // 초기화 완료 델리게이트 호출 - 카드 선택 UI
    if (AAuraHUD* AuraHUD = Cast<AAuraHUD>(GetHUD()))
    {
        AuraHUD->OnInitializePlayerControllerDelegate.Broadcast();
    }
}

AuraHUD의 카드 선택 뷰 모델에 접근하여 각 카드의 뷰 모델을 가져온다.

카드 뷰 모델의 선택 버튼 델리게이트에 플레이어 컨트롤러의 어빌리티 카드 선택됨 함수를 바인딩한다.


- OnInitializePlayerControllerDelegate가 호출되면 AuraHUD에서 아래 함수가 호출된다.

void AAuraHUD::OnInitializePlayerController()
{
    if (AAuraPlayerController* AuraPC = Cast<AAuraPlayerController>(GetOwningPlayerController()))
    {
        AuraPC->OnCardSelectionInitializedDelegate.Broadcast();
    }
}

그리고 위의 람다 함수가 호출되어 각 카드의 선택 버튼에 업그레이드 기능을 부여하는 함수가 바인딩된다.


 

 

(3) 아바타 액터에 업그레이드 태그 적용

// 카드 선택 버튼 콜백
UFUNCTION()
void HandleAbilityCardSelected(FGameplayTag SelectedUpgradeTag);

UFUNCTION(Server, Reliable)
void ServerSelectUpgrade(FGameplayTag SelectedUpgradeTag);
void AAuraPlayerController::HandleAbilityCardSelected(FGameplayTag SelectedUpgradeTag)
{
    ServerSelectUpgrade(SelectedUpgradeTag);

    // 카드 선택 UI 닫기
    if (AAuraHUD* AuraHUD = Cast<AAuraHUD>(GetHUD()))
    {
        AuraHUD->CardSelectionWidget->RemoveFromParent();
    }
}

void AAuraPlayerController::ServerSelectUpgrade_Implementation(FGameplayTag SelectedUpgradeTag)
{
    if (!HasAuthority())
        return;
    
    AActor* AvatarActor = GetPawn();
    if (AvatarActor == nullptr)
        return;

    // 업그레이드 태그 적용
    UAuraAbilitySystemLibrary::ApplyGameplayTagEffectToSelf(SelectedUpgradeTag, AvatarActor);
}

서버 RPC 함수를 통해 플레이어의 ASC에게 업그레이드 태그를 적용한다.

그리고 카드 선택 UI를 제거한다.

 

 

 

3. AuraHUD에서 업그레이드 내용을 UI에 반영하기

	void HandleRandomAbilityUpgrade(FGameplayTag UpgradeTag0, FGameplayTag UpgradeTag1, FGameplayTag UpgradeTag2);
void AAuraHUD::HandleRandomAbilityUpgrade(FGameplayTag UpgradeTag0, FGameplayTag UpgradeTag1,
    FGameplayTag UpgradeTag2)
{
    auto* Info = UAuraAbilitySystemLibrary::GetAbilityUpgradeInfo(this);
    if (Info == nullptr)
        return;
    
    auto* CardViewModel_0 = CardSelectionViewModel->GetCardViewModelByIndex(0);
    if (CardViewModel_0)
    {
        auto Upgrade = Info->GetUpgradeInfoForUpgradeTag(UpgradeTag0);
        
        CardViewModel_0->UpgradeTag = Upgrade.UpgradeEffectTag;
        CardViewModel_0->SetUpgradeDescription(Upgrade.UpgradeDescription);
        CardViewModel_0->SetUpgradeName(Upgrade.UpgradeName);
        CardViewModel_0->SetUpgradeMaxLevel(Upgrade.UpgradeMaxLevel);
    }

    auto* CardViewModel_1 = CardSelectionViewModel->GetCardViewModelByIndex(1);
    if (CardViewModel_1)
    {
        auto Upgrade = Info->GetUpgradeInfoForUpgradeTag(UpgradeTag1);
        
        CardViewModel_1->UpgradeTag = Upgrade.UpgradeEffectTag;
        CardViewModel_1->SetUpgradeDescription(Upgrade.UpgradeDescription);
        CardViewModel_1->SetUpgradeName(Upgrade.UpgradeName);
        CardViewModel_1->SetUpgradeMaxLevel(Upgrade.UpgradeMaxLevel);
    }

    auto* CardViewModel_2 = CardSelectionViewModel->GetCardViewModelByIndex(2);
    if (CardViewModel_2)
    {
        auto Upgrade = Info->GetUpgradeInfoForUpgradeTag(UpgradeTag2);
        
        CardViewModel_2->UpgradeTag = Upgrade.UpgradeEffectTag;
        CardViewModel_2->SetUpgradeDescription(Upgrade.UpgradeDescription);
        CardViewModel_2->SetUpgradeName(Upgrade.UpgradeName);
        CardViewModel_2->SetUpgradeMaxLevel(Upgrade.UpgradeMaxLevel);
    }

업그레이드 태그를 이용하여 업그레이드 정보를 가져오고 카드 선택 UI에 반영한다.

 

 

4. 게임 모드 구성

(1) 카드 초기화 델리게이트

DECLARE_MULTICAST_DELEGATE_OneParam(FOnInitializeCard, APlayerController*);
public:
    /*
     * 로그라이크
     */
    FOnInitializeCard InitializeCardDelegate;

    void InitializeUpgradeCards(APlayerController* PlayerController);

 

 

(2) 카드 초기화 콜백 함수

테스트를 위해 세 개의 태그를 하드 코딩한다.

void AAuraGameModeBase::InitializeUpgradeCards(APlayerController* PlayerController)
{
    // TODO::업그레이드 태그를 중복 없이 3개 뽑아 AuraHUD의 델리게이트로 넘김
    auto AllAbilityUpgradeInfo = UAuraAbilitySystemLibrary::GetAbilityUpgradeInfo(PlayerController);
    const auto& AuraTags = FAuraGameplayTags::Get();
    
    // TODO::보유한 어빌리티의 업그레이드만 노출됨
    auto tag0 = AuraTags.Upgrades_Fire_FireBolt_IncreaseNum;
    auto tag1 = AuraTags.Upgrades_Arcane_ArcaneShards_IncreaseNum;
    auto tag2 = AuraTags.Upgrades_Lightning_Teleport_DecreaseCoolDown;

    if (AAuraHUD* AuraHUD = Cast<AAuraHUD>(PlayerController->GetHUD()))
    {
       AuraHUD->ReceivedCardsDelegate.Broadcast(tag0, tag1, tag2);
    }
}

 

 

(3) 카드 초기화 콜백 함수 바인딩 및 HUD로 게임 모드 초기화 전달

void AAuraGameModeBase::PostLogin(APlayerController* NewPlayer)
{
	Super::PostLogin(NewPlayer);

	if (NewPlayer)
	{
		if (AAuraHUD* AuraHUD = Cast<AAuraHUD>(NewPlayer->GetHUD()))
		{
			// 어빌리티 업그레이드 카드 세팅
			AuraHUD->InitializeCardsDelegate.AddUObject(this, &AAuraGameModeBase::InitializeUpgradeCards);
			AuraHUD->OnInitializeGameModeDelegate.Broadcast();
		}
	}
}

AuraHUD의 InitializeCardsDelegate를 호출하여 카드 선택 UI를 생성한 이후

void AAuraGameModeBase::InitializeUpgradeCards(APlayerController* PlayerController)
{
	// TODO::업그레이드 태그를 중복 없이 3개 뽑아 AuraHUD의 델리게이트로 넘김
	auto AllAbilityUpgradeInfo = UAuraAbilitySystemLibrary::GetAbilityUpgradeInfo(PlayerController);
	const auto& AuraTags = FAuraGameplayTags::Get();
	
	// TODO::보유한 어빌리티의 업그레이드만 노출됨
	auto tag0 = AuraTags.Upgrades_Fire_FireBolt_IncreaseNum;
	auto tag1 = AuraTags.Upgrades_Arcane_ArcaneShards_IncreaseNum;
	auto tag2 = AuraTags.Upgrades_Lightning_Teleport_DecreaseCoolDown;

	if (AAuraHUD* AuraHUD = Cast<AAuraHUD>(PlayerController->GetHUD()))
	{
		AuraHUD->ReceivedCardsDelegate.Broadcast(tag0, tag1, tag2);
	}
}

세 개의 태그를 뽑아 HUD로 넘겨준다.

void AAuraHUD::HandleRandomAbilityUpgrade(FGameplayTag UpgradeTag0, FGameplayTag UpgradeTag1,
    FGameplayTag UpgradeTag2)
{
    auto* Info = UAuraAbilitySystemLibrary::GetAbilityUpgradeInfo(this);
    if (Info == nullptr)
        return;
    
    auto* CardViewModel_0 = CardSelectionViewModel->GetCardViewModelByIndex(0);
    if (CardViewModel_0)
    {
        auto Upgrade = Info->GetUpgradeInfoForUpgradeTag(UpgradeTag0);
        
        CardViewModel_0->UpgradeTag = Upgrade.UpgradeEffectTag;
        CardViewModel_0->SetUpgradeDescription(Upgrade.UpgradeDescription);
        CardViewModel_0->SetUpgradeName(Upgrade.UpgradeName);
        CardViewModel_0->SetUpgradeMaxLevel(Upgrade.UpgradeMaxLevel);
    }

넘겨 받은 태그를 사용하여 정보를 순회하고, 정보를 카드에 대입한다.


- 이후에 수정해야 할 점

(1) AuraHUD의 InitOverlay의 OnInitializeGameModeDelegate 호출 시점

지금은 테스트를 위해 해당 델리게이트를 오버레이 초기화 때에 호출하고 있으나, 추후에 게임 사양에 맞춰 함수 호출 시점을 다르게 해야 한다.

 

(2) AuraGameModeBase의 InitializeUpgradeCards 함수

지금은 적용할 업데이트를 랜덤으로 뽑지 않고 하드 코딩하였으므로, 추후에 보유한 어빌리티를 대상으로 순회하여 랜덤한 업그레이드 정보를 가져오도록 한다.


 

DA_AbilityUpgradeInfo에 어빌리티 업그레이드 정보가 적절하게 채워져 있다면 위와 같이 나타난다.

버튼을 클릭해 UI가 적절히 제거되는지 확인한다.

 

 

' ` ' 키를 눌러 콘솔 창을 띄운 뒤, showdebug abilitysystem을 입력한다.

Owned Tags에 버튼을 누른 업그레이드 태그가 적용되었다면 성공적으로 연결된 것이다.