2024-03-22 16:45:37 +08:00
|
|
|
|
---
|
|
|
|
|
title: UE5 Lyra学习笔记(6)—CommonUI
|
|
|
|
|
date: 2024-03-22 15:03:59
|
|
|
|
|
excerpt:
|
|
|
|
|
tags:
|
|
|
|
|
rating: ⭐
|
|
|
|
|
---
|
|
|
|
|
# 前言
|
|
|
|
|
- 全局管理类
|
2024-03-22 19:08:41 +08:00
|
|
|
|
- UGameUIManagerSubsystem:父类为UGameInstanceSubsystem,主要的功能是管理CurrentPolicy(UGameUIPolicy)。
|
|
|
|
|
- UGameUIPolicy:将UPrimaryGameLayout添加到Viewport中。
|
2024-03-22 16:45:37 +08:00
|
|
|
|
- UPrimaryGameLayout
|
|
|
|
|
- Widgets
|
|
|
|
|
使用[[#GameplayAbility]]检测玩家输入并且开启UI。
|
|
|
|
|
|
2024-03-22 19:08:41 +08:00
|
|
|
|
# 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
|
2024-03-22 16:45:37 +08:00
|
|
|
|
|
2024-03-22 19:08:41 +08:00
|
|
|
|
```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");
|
2024-03-22 16:45:37 +08:00
|
|
|
|
|
2024-03-22 19:08:41 +08:00
|
|
|
|
static FName NAME_PushingWidgetToLayer("PushingWidgetToLayer");
|
|
|
|
|
const FName SuspendInputToken = bSuspendInputUntilComplete ? UCommonUIExtensions::SuspendInputForPlayer(GetOwningPlayer(), NAME_PushingWidgetToLayer) : NAME_None;
|
2024-03-22 16:45:37 +08:00
|
|
|
|
|
2024-03-22 19:08:41 +08:00
|
|
|
|
FStreamableManager& StreamableManager = UAssetManager::Get().GetStreamableManager();
|
|
|
|
|
TSharedPtr<FStreamableHandle> StreamingHandle = StreamableManager.RequestAsyncLoad(ActivatableWidgetClass.ToSoftObjectPath(), FStreamableDelegate::CreateWeakLambda(this,
|
|
|
|
|
[this, LayerName, ActivatableWidgetClass, StateFunc, SuspendInputToken]()
|
|
|
|
|
{
|
|
|
|
|
UCommonUIExtensions::ResumeInputForPlayer(GetOwningPlayer(), SuspendInputToken);
|
2024-03-22 16:45:37 +08:00
|
|
|
|
|
2024-03-22 19:08:41 +08:00
|
|
|
|
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:游戏内的模态确认对话框、错误对话框。
|
2024-03-22 16:45:37 +08:00
|
|
|
|
# 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();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|