135 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			135 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
---
 | 
						||
title: UE5 Lyra学习笔记(6)—CommonUI
 | 
						||
date: 2024-03-22 15:03:59
 | 
						||
excerpt: 
 | 
						||
tags: 
 | 
						||
rating: ⭐
 | 
						||
---
 | 
						||
# 前言
 | 
						||
- 全局管理类
 | 
						||
	- 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
 | 
						||
 | 
						||
```c++
 | 
						||
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)
 | 
						||
```c++
 | 
						||
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|800]]
 | 
						||
 GAB_ShowWidget_WhileInputHeld,略微复杂一些会多一些:![[GAB_ShowWidget_WhileInputHeld.png|1200]]
 | 
						||
 | 
						||
## GameplayTasks
 | 
						||
- UCancellableAsyncAction
 | 
						||
	- [[#UAsyncAction_PushContentToLayerForPlayer]]
 | 
						||
 | 
						||
核心函数逻辑如下,主要调用UPrimaryGameLayout::PushWidgetToLayerStackAsync()
 | 
						||
```c++
 | 
						||
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();
 | 
						||
	}
 | 
						||
}
 | 
						||
``` |