BlueRoseNote/03-UnrealEngine/Gameplay/Lyra/UE5 Lyra学习笔记(6)—CommonUI.md

5.4 KiB
Raw Blame History

title, date, excerpt, tags, rating
title date excerpt tags rating
UE5 Lyra学习笔记(6)—CommonUI 2024-03-22 15:03:59

前言

  • 全局管理类
    • UGameUIManagerSubsystem父类为UGameInstanceSubsystem主要的功能是管理CurrentPolicy(UGameUIPolicy)。
      • UGameUIPolicy将UPrimaryGameLayout添加到Viewport中。
        • UPrimaryGameLayout
  • Widgets 使用#GameplayAbility检测玩家输入并且开启UI。

UPrimaryGameLayout

父类为UCommonUserWidget。主要功能为UILayer以及Dormant状态管理。

UILayer

使用TMap<FGameplayTag, TObjectPtr<UCommonActivatableWidgetContainerBase>> Layers;进行多UI层的管理不同Layer使用GameplayTag进行区分。Lyra的UILayer分为以下4个层

  • UI.Layer.Game
  • UI.Layer.GameMenu
  • UI.Layer.Menu
  • UI.Layer.Modal
template <typename ActivatableWidgetT = UCommonActivatableWidget>
TSharedPtr<FStreamableHandle> PushWidgetToLayerStackAsync(FGameplayTag LayerName, bool bSuspendInputUntilComplete, TSoftClassPtr<UCommonActivatableWidget> ActivatableWidgetClass, TFunction<void(EAsyncWidgetLayerState, ActivatableWidgetT*)> StateFunc)
{
	static_assert(TIsDerivedFrom<ActivatableWidgetT, UCommonActivatableWidget>::IsDerived, "Only CommonActivatableWidgets can be used here");

	static FName NAME_PushingWidgetToLayer("PushingWidgetToLayer");
	const FName SuspendInputToken = bSuspendInputUntilComplete ? UCommonUIExtensions::SuspendInputForPlayer(GetOwningPlayer(), NAME_PushingWidgetToLayer) : NAME_None;

	FStreamableManager& StreamableManager = UAssetManager::Get().GetStreamableManager();
	TSharedPtr<FStreamableHandle> StreamingHandle = StreamableManager.RequestAsyncLoad(ActivatableWidgetClass.ToSoftObjectPath(), FStreamableDelegate::CreateWeakLambda(this,
		[this, LayerName, ActivatableWidgetClass, StateFunc, SuspendInputToken]()
		{
			UCommonUIExtensions::ResumeInputForPlayer(GetOwningPlayer(), SuspendInputToken);

			ActivatableWidgetT* Widget = PushWidgetToLayerStack<ActivatableWidgetT>(LayerName, ActivatableWidgetClass.Get(), [StateFunc](ActivatableWidgetT& WidgetToInit) {
				StateFunc(EAsyncWidgetLayerState::Initialize, &WidgetToInit);
			});

			StateFunc(EAsyncWidgetLayerState::AfterPush, Widget);
		})
	);

	// Setup a cancel delegate so that we can resume input if this handler is canceled.
	StreamingHandle->BindCancelDelegate(FStreamableDelegate::CreateWeakLambda(this,
		[this, StateFunc, SuspendInputToken]()
		{
			UCommonUIExtensions::ResumeInputForPlayer(GetOwningPlayer(), SuspendInputToken);
			StateFunc(EAsyncWidgetLayerState::Canceled, nullptr);
		})
	);

	return StreamingHandle;
}

template <typename ActivatableWidgetT = UCommonActivatableWidget>
ActivatableWidgetT* PushWidgetToLayerStack(FGameplayTag LayerName, UClass* ActivatableWidgetClass)
{
	return PushWidgetToLayerStack<ActivatableWidgetT>(LayerName, ActivatableWidgetClass, [](ActivatableWidgetT&) {});
}

template <typename ActivatableWidgetT = UCommonActivatableWidget>
ActivatableWidgetT* PushWidgetToLayerStack(FGameplayTag LayerName, UClass* ActivatableWidgetClass, TFunctionRef<void(ActivatableWidgetT&)> InitInstanceFunc)
{
	static_assert(TIsDerivedFrom<ActivatableWidgetT, UCommonActivatableWidget>::IsDerived, "Only CommonActivatableWidgets can be used here");

	if (UCommonActivatableWidgetContainerBase* Layer = GetLayerWidget(LayerName))
	{
		return Layer->AddWidget<ActivatableWidgetT>(ActivatableWidgetClass, InitInstanceFunc);
	}

	return nullptr;
}

RegisterLayer

用于向PrimaryGameLayout注册UILayer。为了方便管理里面的CommonActivatabbleWidgetStack)

void RegisterLayer(UPARAM(meta = (Categories = "UI.Layer")) FGameplayTag LayerTag, UCommonActivatableWidgetContainerBase* LayerWidget);

Content/UI的W_OverallUILayout调用它是一个根组件为Overlay下面有4个子组件(CommonActivatabbleWidgetStack)

  • GameLayer_Stack游戏内UI类似HUD。
  • GameMenu_Stack游戏相关的 "菜单",例如游戏中的库存用户界面。
  • Menu_Stack设置界面等。
  • Model_Stack游戏内的模态确认对话框、错误对话框。

GameplayAbility

Lyra使用2个位于Plugins/ShooterCore/Content/Input/Abilites/

  • GAB_ShowWidget_WhenInputPressed
  • GAB_ShowWidget_WhileInputHeld

GAB_ShowWidget_WhenInputPressed!GAB_ShowWidget_WhenInputPressed.png GAB_ShowWidget_WhileInputHeld略微复杂一些会多一些!GAB_ShowWidget_WhileInputHeld.png

GameplayTasks

核心函数逻辑如下主要调用UPrimaryGameLayout::PushWidgetToLayerStackAsync()

void UAsyncAction_PushContentToLayerForPlayer::Activate()
{
	if (UPrimaryGameLayout* RootLayout = UPrimaryGameLayout::GetPrimaryGameLayout(OwningPlayerPtr.Get()))
	{
		TWeakObjectPtr<UAsyncAction_PushContentToLayerForPlayer> WeakThis = this;
		StreamingHandle = RootLayout->PushWidgetToLayerStackAsync<UCommonActivatableWidget>(LayerName, bSuspendInputUntilComplete, WidgetClass, [this, WeakThis](EAsyncWidgetLayerState State, UCommonActivatableWidget* Widget) {
			if (WeakThis.IsValid())
			{
				switch (State)
				{
					case EAsyncWidgetLayerState::Initialize:
						BeforePush.Broadcast(Widget);
						break;
					case EAsyncWidgetLayerState::AfterPush:
						AfterPush.Broadcast(Widget);
						SetReadyToDestroy();
						break;
					case EAsyncWidgetLayerState::Canceled:
						SetReadyToDestroy();
						break;
				}
			}
			SetReadyToDestroy();
		});
	}
	else
	{
		SetReadyToDestroy();
	}
}