优化UI模块 同步异步加载

This commit is contained in:
陈思海 2025-11-13 17:46:20 +08:00
parent 110c3451ea
commit 69699074de
11 changed files with 208 additions and 66 deletions

Binary file not shown.

View File

@ -17,7 +17,7 @@ namespace AlicizaX.UI.Runtime
ResourceModule = ModuleSystem.GetModule<IResourceModule>();
}
public static async UniTask<T> CreateUIHolder<T>(Transform parent) where T : UIHolderObjectBase
public static async UniTask<T> CreateUIHolderAsync<T>(Transform parent) where T : UIHolderObjectBase
{
if (UIResRegistry.TryGet(typeof(T).TypeHandle, out UIResRegistry.UIResInfo resInfo))
{
@ -29,6 +29,19 @@ namespace AlicizaX.UI.Runtime
return null;
}
public static T CreateUIHolderSync<T>(Transform parent) where T : UIHolderObjectBase
{
if (UIResRegistry.TryGet(typeof(T).TypeHandle, out UIResRegistry.UIResInfo resInfo))
{
GameObject obj = LoadUIResourcesSync(resInfo, parent);
return obj.GetComponent<T>();
}
return null;
}
internal static async UniTask<GameObject> LoadUIResourcesAsync(UIResRegistry.UIResInfo resInfo, Transform parent)
{
return resInfo.LoadType == EUIResLoadType.AssetBundle
@ -36,24 +49,39 @@ namespace AlicizaX.UI.Runtime
: await InstantiateResourceAsync(resInfo.Location, parent);
}
internal static async UniTask CreateUIResource(UIMetadata meta, Transform parent, UIBase owner = null)
internal static GameObject LoadUIResourcesSync(UIResRegistry.UIResInfo resInfo, Transform parent)
{
return resInfo.LoadType == EUIResLoadType.AssetBundle
? ResourceModule.LoadGameObject(resInfo.Location, parent)
: InstantiateResourceSync(resInfo.Location, parent);
}
internal static async UniTask CreateUIResourceAsync(UIMetadata meta, Transform parent, UIBase owner = null)
{
if (meta.State != UIState.CreatedUI) return;
GameObject obj = await LoadUIResourcesAsync(meta.ResInfo, parent);
ValidateAndBind(meta, obj, owner);
}
internal static void LoadUIResourcesSync(UIMetadata meta, Transform parent, UIBase owner = null)
internal static void CreateUIResourceSync(UIMetadata meta, Transform parent, UIBase owner = null)
{
if (meta.State != UIState.CreatedUI) return;
GameObject obj = meta.ResInfo.LoadType == EUIResLoadType.AssetBundle
? ResourceModule.LoadGameObject(meta.ResInfo.Location, parent)
: Object.Instantiate(Resources.Load<GameObject>(meta.ResInfo.Location), parent);
GameObject obj = LoadUIResourcesSync(meta.ResInfo, parent);
ValidateAndBind(meta, obj, owner);
}
private static async UniTask<GameObject> InstantiateResourceAsync(string location, Transform parent)
{
GameObject prefab = (GameObject)await Resources.LoadAsync<GameObject>(location);
return Object.Instantiate(prefab, parent);
}
private static GameObject InstantiateResourceSync(string location, Transform parent)
{
GameObject prefab = Resources.Load<GameObject>(location);
return Object.Instantiate(prefab, parent);
}
private static void ValidateAndBind(UIMetadata meta, GameObject holderObject, UIBase owner)
{
@ -68,11 +96,5 @@ namespace AlicizaX.UI.Runtime
meta.View?.BindUIHolder(holder, owner);
}
private static async UniTask<GameObject> InstantiateResourceAsync(string location, Transform parent)
{
GameObject prefab = (GameObject)await Resources.LoadAsync<GameObject>(location);
return Object.Instantiate(prefab, parent);
}
}
}

View File

@ -4,6 +4,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using UnityEngine.PlayerLoop;
namespace AlicizaX.UI.Runtime
{
@ -16,14 +17,16 @@ namespace AlicizaX.UI.Runtime
public readonly int UILayer;
public readonly bool FullScreen;
public readonly int CacheTime;
public readonly bool NeedUpdate;
public UIMetaInfo(RuntimeTypeHandle runtimeTypeHandle, RuntimeTypeHandle holderRuntimeTypeHandle, UILayer windowLayer, bool fullScreen, int cacheTime)
public UIMetaInfo(RuntimeTypeHandle runtimeTypeHandle, RuntimeTypeHandle holderRuntimeTypeHandle, UILayer windowLayer, bool fullScreen, int cacheTime, bool needUpdate)
{
RuntimeTypeHandle = runtimeTypeHandle;
HolderRuntimeTypeHandle = holderRuntimeTypeHandle;
UILayer = (int)windowLayer;
FullScreen = fullScreen;
CacheTime = cacheTime;
NeedUpdate = needUpdate;
}
}
@ -31,11 +34,11 @@ namespace AlicizaX.UI.Runtime
private static readonly Dictionary<string, RuntimeTypeHandle> _stringHandleMap = new();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Register(Type uiType, Type holderType, UILayer layer = UILayer.UI, bool fullScreen = false, int cacheTime = 0)
public static void Register(Type uiType, Type holderType, UILayer layer = UILayer.UI, bool fullScreen = false, int cacheTime = 0, bool needUpdate = false)
{
var holderHandle = holderType.TypeHandle;
var uiHandle = uiType.TypeHandle;
_typeHandleMap[uiHandle] = new UIMetaInfo(uiHandle, holderHandle, layer, fullScreen, cacheTime);
_typeHandleMap[uiHandle] = new UIMetaInfo(uiHandle, holderHandle, layer, fullScreen, cacheTime, needUpdate);
_stringHandleMap[uiType.Name] = uiHandle;
}
@ -80,21 +83,25 @@ namespace AlicizaX.UI.Runtime
UILayer layer = UILayer.UI;
bool fullScreen = false;
int cacheTime = 0;
bool needUpdate = false;
var cad = CustomAttributeData.GetCustomAttributes(uiType)
var windowAttribute = CustomAttributeData.GetCustomAttributes(uiType)
.FirstOrDefault(a => a.AttributeType.Name == nameof(WindowAttribute));
if (cad != null)
var uiUpdateAttribute = CustomAttributeData.GetCustomAttributes(uiType)
.FirstOrDefault(a => a.AttributeType.Name == nameof(UIUpdateAttribute));
if (windowAttribute != null)
{
var args = cad.ConstructorArguments;
var args = windowAttribute.ConstructorArguments;
if (args.Count > 0) layer = (UILayer)(args[0].Value ?? UILayer.UI);
if (args.Count > 1) fullScreen = (bool)(args[1].Value ?? false);
if (args.Count > 2) cacheTime = (int)(args[2].Value ?? 0);
}
needUpdate = uiUpdateAttribute != null;
if (holderType != null)
{
Register(uiType, holderType, layer, fullScreen, cacheTime);
Register(uiType, holderType, layer, fullScreen, cacheTime, needUpdate);
info = _typeHandleMap[uiType.TypeHandle];
return true;
}

View File

@ -3,7 +3,6 @@ using UnityEngine;
namespace AlicizaX.UI.Runtime
{
[AttributeUsage(AttributeTargets.Class)]
public class WindowAttribute : Attribute
{
@ -36,6 +35,15 @@ namespace AlicizaX.UI.Runtime
}
}
[AttributeUsage(AttributeTargets.Class)]
public class UIUpdateAttribute : Attribute
{
public UIUpdateAttribute()
{
}
}
[AttributeUsage(AttributeTargets.Class)]
public class UIResAttribute : Attribute
{
@ -49,7 +57,7 @@ namespace AlicizaX.UI.Runtime
}
}
public enum EUIResLoadType:byte
public enum EUIResLoadType : byte
{
Resources,
AssetBundle

View File

@ -1,24 +1,78 @@
using System;
using AlicizaX.Resource.Runtime;
using AlicizaX;
using AlicizaX.Timer.Runtime;
using Cysharp.Threading.Tasks;
using UnityEngine;
namespace AlicizaX.UI.Runtime
{
/// <summary>
/// UI 模块接口:负责 UI 的创建、显示、关闭与查询。
/// 支持异步与同步(预加载)两种打开方式。
/// </summary>
public interface IUIModule : IModule, IModuleUpdate
{
void Initlize(Transform root,bool isOrthographic);
/// <summary>
/// 初始化 UI 模块。
/// </summary>
/// <param name="root">UI 根节点(通常为 Canvas 根)</param>
/// <param name="isOrthographic">摄像机是否正交模式</param>
void Initialize(Transform root, bool isOrthographic);
/// <summary>
/// UI 摄像机
/// </summary>
Camera UICamera { get; set; }
/// <summary>
/// UI 根节点
/// </summary>
Transform UICanvasRoot { get; set; }
UniTask<UIBase> ShowUI<T>(params System.Object[] userDatas) where T : UIBase;
// ───────────────────────────────────────────────
// Show 系列:异步为主,同步为辅
// ───────────────────────────────────────────────
/// <summary>
/// 异步显示 UI推荐方式
/// </summary>
UniTask<T> ShowUI<T>(params object[] userDatas) where T : UIBase;
/// <summary>
/// 异步显示 UI使用字符串类型名
/// </summary>
UniTask<UIBase>? ShowUI(string type, params object[] userDatas);
UniTask<T> ShowUIAsync<T>(params System.Object[] userDatas) where T : UIBase;
/// <summary>
/// 同步显示 UI仅限资源已预加载时使用避免死锁
/// </summary>
T ShowUISync<T>(params object[] userDatas) where T : UIBase;
// ───────────────────────────────────────────────
// Close / Get 系列
// ───────────────────────────────────────────────
/// <summary>
/// 关闭指定类型 UI。
/// </summary>
void CloseUI<T>(bool force = false) where T : UIBase;
/// <summary>
/// 关闭指定类型 UI。
/// </summary>
void CloseUI(RuntimeTypeHandle handle, bool force = false);
/// <summary>
/// 获取当前已打开的指定类型 UI。
/// </summary>
T GetUI<T>() where T : UIBase;
void CloseUI(RuntimeTypeHandle handle, bool force = false);
// ───────────────────────────────────────────────
// 内部注入
// ───────────────────────────────────────────────
/// <summary>
/// 由框架内部注入计时模块。
/// </summary>
protected internal void SetTimerManager(ITimerModule timerModule);
}
}

View File

@ -21,7 +21,7 @@ namespace AlicizaX.UI.Runtime
private RectTransform UICacheLayer;
private bool _isOrthographic;
public void Initlize(Transform root, bool isOrthographic)
public void Initialize(Transform root, bool isOrthographic)
{
UIRoot = root;
Object.DontDestroyOnLoad(root.gameObject);

View File

@ -22,11 +22,22 @@ namespace AlicizaX.UI.Runtime
{
private readonly LayerData[] _openUI = new LayerData[(int)UILayer.All];
private async UniTask<UIBase> ShowUIImplAsync(UIMetadata meta, params object[] userDatas)
private async UniTask<UIBase> ShowUIImplAsync(UIMetadata metaInfo, params object[] userDatas)
{
var metaInfo = GetOrCreateMeta(meta);
await UIHolderFactory.CreateUIResource(metaInfo, UICacheLayer);
return await FinalizeShow(metaInfo, userDatas);
CreateMetaUI(metaInfo);
await UIHolderFactory.CreateUIResourceAsync(metaInfo, UICacheLayer);
FinalizeShow(metaInfo, userDatas);
await UpdateVisualState(metaInfo);
return metaInfo.View;
}
private UIBase ShowUIImplSync(UIMetadata metaInfo, params object[] userDatas)
{
CreateMetaUI(metaInfo);
UIHolderFactory.CreateUIResourceSync(metaInfo, UICacheLayer);
FinalizeShow(metaInfo, userDatas);
UpdateVisualState(metaInfo).Forget();
return metaInfo.View;
}
private async UniTask CloseUIImpl(UIMetadata meta, bool force)
@ -35,6 +46,7 @@ namespace AlicizaX.UI.Runtime
{
return;
}
await meta.View.InternalClose();
Pop(meta);
SortWindowVisible(meta.MetaInfo.UILayer);
@ -49,14 +61,13 @@ namespace AlicizaX.UI.Runtime
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private UIMetadata GetOrCreateMeta(UIMetadata meta)
private void CreateMetaUI(UIMetadata meta)
{
if (meta.State == UIState.Uninitialized) meta.CreateUI();
return meta;
}
private async UniTask<UIBase> FinalizeShow(UIMetadata meta, object[] userDatas)
private void FinalizeShow(UIMetadata meta, object[] userDatas)
{
if (meta.InCache)
{
@ -77,8 +88,6 @@ namespace AlicizaX.UI.Runtime
}
meta.View.RefreshParams(userDatas);
await UpdateVisualState(meta);
return meta.View;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]

View File

@ -19,12 +19,11 @@ namespace AlicizaX.UI.Runtime
void IModuleUpdate.Update(float elapseSeconds, float realElapseSeconds)
{
// 遍历所有层级
for (int layerIndex = 0; layerIndex < _openUI.Length; layerIndex++)
{
var layer = _openUI[layerIndex];
int count = layer.OrderList.Count;
if (count == 0) continue; // 跳过空层级
if (count == 0) continue;
for (int i = 0; i < count; i++)
{
if (layer.OrderList.Count != count)
@ -33,7 +32,8 @@ namespace AlicizaX.UI.Runtime
}
var window = layer.OrderList[i];
window.View.InternalUpdate();
if (window.MetaInfo.NeedUpdate)
window.View.InternalUpdate();
}
}
}
@ -50,13 +50,12 @@ namespace AlicizaX.UI.Runtime
return null;
}
public UniTask<UIBase> ShowUI<T>(params System.Object[] userDatas) where T : UIBase
public T ShowUISync<T>(params object[] userDatas) where T : UIBase
{
return ShowUI(MetaTypeCache<T>.Metadata, userDatas);
return (T)ShowUIImplSync(MetaTypeCache<T>.Metadata, userDatas);
}
public async UniTask<T> ShowUIAsync<T>(params System.Object[] userDatas) where T : UIBase
public async UniTask<T> ShowUI<T>(params System.Object[] userDatas) where T : UIBase
{
return (T)await ShowUIAsync(MetaTypeCache<T>.Metadata, userDatas);
}
@ -69,7 +68,7 @@ namespace AlicizaX.UI.Runtime
public T GetUI<T>() where T : UIBase
{
return (T)GetUI(MetaTypeCache<T>.Metadata);
return (T)GetUIImpl(MetaTypeCache<T>.Metadata);
}
@ -93,11 +92,6 @@ namespace AlicizaX.UI.Runtime
}
}
private UIBase GetUI(UIMetadata meta)
{
return (UIBase)GetUIImpl(meta);
}
void IUIModule.SetTimerManager(ITimerModule timerModule)
{

View File

@ -17,7 +17,7 @@ namespace AlicizaX.UI.Runtime
var values = _children.Values;
foreach (var meta in values)
{
if (meta.View.State == UIState.Opened)
if (meta.View.State == UIState.Opened && meta.MetaInfo.NeedUpdate)
{
meta.View.InternalUpdate();
}
@ -43,7 +43,7 @@ namespace AlicizaX.UI.Runtime
}
finally
{
ArrayPool<UIMetadata>.Shared.Return(temp);
ArrayPool<UIMetadata>.Shared.Return(temp, true);
}
_children.Clear();
@ -61,28 +61,40 @@ namespace AlicizaX.UI.Runtime
}
}
internal async UniTask<UIBase> CreateWidget(UIMetadata metadata, Transform parent, bool visible)
internal async UniTask<UIBase> CreateWidgetUIAsync(UIMetadata metadata, Transform parent, bool visible)
{
metadata.CreateUI();
await UIHolderFactory.CreateUIResource(metadata, parent, this);
await UIHolderFactory.CreateUIResourceAsync(metadata, parent, this);
await ProcessWidget(metadata, visible);
return (UIBase)metadata.View;
}
protected async UniTask<UIBase> CreateWidget(string typeName, Transform parent, bool visible = true)
internal UIBase CreateWidgetUISync(UIMetadata metadata, Transform parent, bool visible)
{
metadata.CreateUI();
UIHolderFactory.CreateUIResourceSync(metadata, parent, this);
ProcessWidget(metadata, visible).Forget();
return (UIBase)metadata.View;
}
#region CreateWidget
#region Async
protected async UniTask<UIBase> CreateWidgetAsync(string typeName, Transform parent, bool visible = true)
{
UIMetaRegistry.TryGet(typeName, out var metaRegistry);
UIMetadata metadata = UIMetadataFactory.GetMetadata(metaRegistry.RuntimeTypeHandle);
return await CreateWidget(metadata, parent, visible);
return await CreateWidgetUIAsync(metadata, parent, visible);
}
protected async UniTask<T> CreateWidget<T>(Transform parent, bool visible = true) where T : UIBase
protected async UniTask<T> CreateWidgetAsync<T>(Transform parent, bool visible = true) where T : UIBase
{
UIMetadata metadata = MetaTypeCache<T>.Metadata;
return (T)await CreateWidget(metadata, parent, visible);
return (T)await CreateWidgetUIAsync(metadata, parent, visible);
}
protected async UniTask<T> CreateWidget<T>(UIHolderObjectBase holder) where T : UIBase
protected async UniTask<T> CreateWidgetAsync<T>(UIHolderObjectBase holder) where T : UIBase
{
UIMetadata metadata = MetaTypeCache<T>.Metadata;
metadata.CreateUI();
@ -92,9 +104,42 @@ namespace AlicizaX.UI.Runtime
return (T)widget;
}
#endregion
#region Sync
protected UIBase CreateWidgetSync(string typeName, Transform parent, bool visible = true)
{
UIMetaRegistry.TryGet(typeName, out var metaRegistry);
UIMetadata metadata = UIMetadataFactory.GetMetadata(metaRegistry.RuntimeTypeHandle);
return CreateWidgetUISync(metadata, parent, visible);
}
protected T CreateWidgetSync<T>(Transform parent, bool visible = true) where T : UIBase
{
UIMetadata metadata = MetaTypeCache<T>.Metadata;
return (T)CreateWidgetUISync(metadata, parent, visible);
}
protected T CreateWidgetSync<T>(UIHolderObjectBase holder) where T : UIBase
{
UIMetadata metadata = MetaTypeCache<T>.Metadata;
metadata.CreateUI();
UIBase widget = (UIBase)metadata.View;
widget.BindUIHolder(holder, this);
ProcessWidget(metadata, true).Forget();
return (T)widget;
}
#endregion
#endregion
private async UniTask ProcessWidget(UIMetadata meta, bool visible)
{
AddWidget(meta);
if (!AddWidget(meta)) return;
await meta.View.InternalInitlized();
meta.View.Visible = visible;
if (meta.View.Visible)
@ -103,13 +148,16 @@ namespace AlicizaX.UI.Runtime
}
}
private void AddWidget(UIMetadata meta)
private bool AddWidget(UIMetadata meta)
{
if (!_children.TryAdd(meta.View, meta))
{
Log.Warning("Already has widget:{0}", meta.View);
meta.Dispose();
return false;
}
return true;
}
public async UniTask RemoveWidget(UIBase widget)

View File

@ -105,7 +105,7 @@ namespace AlicizaX.UI.Runtime
var metadata = UIMetadataFactory.GetMetadata(typeHandle);
var parent = _tabCache[typeHandle];
var widget = await CreateWidget(metadata, parent, false);
var widget = await CreateWidgetUIAsync(metadata, parent, false);
if (widget is not UIWidget tabWidget) return;
_loadedTabs[typeHandle] = tabWidget;

View File

@ -38,7 +38,7 @@ namespace AlicizaX.UI.Runtime
obj.name = "------UI Root------";
_instanceRoot = obj.transform;
Object.DontDestroyOnLoad(_instanceRoot);
_uiModule.Initlize(_instanceRoot,_isOrthographic);
_uiModule.Initialize(_instanceRoot,_isOrthographic);
}
private void Start()