From 69699074de4e895c427c7670bf5ccbb0dd30e6fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E6=80=9D=E6=B5=B7?= <1464576565@qq.com> Date: Thu, 13 Nov 2025 17:46:20 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96UI=E6=A8=A1=E5=9D=97=20?= =?UTF-8?q?=E5=90=8C=E6=AD=A5=E5=BC=82=E6=AD=A5=E5=8A=A0=E8=BD=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Plugins/UI/UISourceGenerator.dll | Bin 25088 -> 25600 bytes Runtime/UI/Constant/UIHolderFactory.cs | 50 ++++++++++++----- Runtime/UI/Constant/UIMetaRegistry.cs | 23 +++++--- Runtime/UI/Constant/WindowAttribute.cs | 12 +++- Runtime/UI/Manager/IUIModule.cs | 66 ++++++++++++++++++++-- Runtime/UI/Manager/UIModule.Initlize.cs | 2 +- Runtime/UI/Manager/UIModule.Open.cs | 27 ++++++--- Runtime/UI/Manager/UIModule.cs | 20 +++---- Runtime/UI/UIBase/UIBase.Widget.cs | 70 ++++++++++++++++++++---- Runtime/UI/UIBase/UITabWindow.cs | 2 +- Runtime/UI/UIComponent.cs | 2 +- 11 files changed, 208 insertions(+), 66 deletions(-) diff --git a/Plugins/UI/UISourceGenerator.dll b/Plugins/UI/UISourceGenerator.dll index 6aad363b7c36d243e4bc7fb7e701110e8743b3c4..a8373445c23efc3bf60f3044d7163395f7f61d11 100644 GIT binary patch delta 7749 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 } - public static async UniTask CreateUIHolder(Transform parent) where T : UIHolderObjectBase + public static async UniTask CreateUIHolderAsync(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(Transform parent) where T : UIHolderObjectBase + { + if (UIResRegistry.TryGet(typeof(T).TypeHandle, out UIResRegistry.UIResInfo resInfo)) + { + GameObject obj = LoadUIResourcesSync(resInfo, parent); + + return obj.GetComponent(); + } + + return null; + } + + internal static async UniTask 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(meta.ResInfo.Location), parent); - + GameObject obj = LoadUIResourcesSync(meta.ResInfo, parent); ValidateAndBind(meta, obj, owner); } + private static async UniTask InstantiateResourceAsync(string location, Transform parent) + { + GameObject prefab = (GameObject)await Resources.LoadAsync(location); + return Object.Instantiate(prefab, parent); + } + + private static GameObject InstantiateResourceSync(string location, Transform parent) + { + GameObject prefab = Resources.Load(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 InstantiateResourceAsync(string location, Transform parent) - { - GameObject prefab = (GameObject)await Resources.LoadAsync(location); - return Object.Instantiate(prefab, parent); - } } } diff --git a/Runtime/UI/Constant/UIMetaRegistry.cs b/Runtime/UI/Constant/UIMetaRegistry.cs index a431500..b410143 100644 --- a/Runtime/UI/Constant/UIMetaRegistry.cs +++ b/Runtime/UI/Constant/UIMetaRegistry.cs @@ -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 _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; } diff --git a/Runtime/UI/Constant/WindowAttribute.cs b/Runtime/UI/Constant/WindowAttribute.cs index 4abd175..c1f50d0 100644 --- a/Runtime/UI/Constant/WindowAttribute.cs +++ b/Runtime/UI/Constant/WindowAttribute.cs @@ -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 diff --git a/Runtime/UI/Manager/IUIModule.cs b/Runtime/UI/Manager/IUIModule.cs index 1b96006..3c1474b 100644 --- a/Runtime/UI/Manager/IUIModule.cs +++ b/Runtime/UI/Manager/IUIModule.cs @@ -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 { + /// + /// UI 模块接口:负责 UI 的创建、显示、关闭与查询。 + /// 支持异步与同步(预加载)两种打开方式。 + /// public interface IUIModule : IModule, IModuleUpdate { - void Initlize(Transform root,bool isOrthographic); + /// + /// 初始化 UI 模块。 + /// + /// UI 根节点(通常为 Canvas 根) + /// 摄像机是否正交模式 + void Initialize(Transform root, bool isOrthographic); + + /// + /// UI 摄像机 + /// Camera UICamera { get; set; } + + /// + /// UI 根节点 + /// Transform UICanvasRoot { get; set; } - UniTask ShowUI(params System.Object[] userDatas) where T : UIBase; + + // ─────────────────────────────────────────────── + // Show 系列:异步为主,同步为辅 + // ─────────────────────────────────────────────── + + /// + /// 异步显示 UI(推荐方式) + /// + UniTask ShowUI(params object[] userDatas) where T : UIBase; + + /// + /// 异步显示 UI(使用字符串类型名) + /// UniTask? ShowUI(string type, params object[] userDatas); - UniTask ShowUIAsync(params System.Object[] userDatas) where T : UIBase; + + /// + /// 同步显示 UI(仅限资源已预加载时使用,避免死锁) + /// + T ShowUISync(params object[] userDatas) where T : UIBase; + + // ─────────────────────────────────────────────── + // Close / Get 系列 + // ─────────────────────────────────────────────── + + /// + /// 关闭指定类型 UI。 + /// void CloseUI(bool force = false) where T : UIBase; + + /// + /// 关闭指定类型 UI。 + /// + void CloseUI(RuntimeTypeHandle handle, bool force = false); + + /// + /// 获取当前已打开的指定类型 UI。 + /// T GetUI() where T : UIBase; - void CloseUI(RuntimeTypeHandle handle, bool force = false); + // ─────────────────────────────────────────────── + // 内部注入 + // ─────────────────────────────────────────────── + + /// + /// 由框架内部注入计时模块。 + /// protected internal void SetTimerManager(ITimerModule timerModule); } } diff --git a/Runtime/UI/Manager/UIModule.Initlize.cs b/Runtime/UI/Manager/UIModule.Initlize.cs index 415fa02..f17d021 100644 --- a/Runtime/UI/Manager/UIModule.Initlize.cs +++ b/Runtime/UI/Manager/UIModule.Initlize.cs @@ -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); diff --git a/Runtime/UI/Manager/UIModule.Open.cs b/Runtime/UI/Manager/UIModule.Open.cs index 5302871..283f956 100644 --- a/Runtime/UI/Manager/UIModule.Open.cs +++ b/Runtime/UI/Manager/UIModule.Open.cs @@ -22,11 +22,22 @@ namespace AlicizaX.UI.Runtime { private readonly LayerData[] _openUI = new LayerData[(int)UILayer.All]; - private async UniTask ShowUIImplAsync(UIMetadata meta, params object[] userDatas) + private async UniTask 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 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)] diff --git a/Runtime/UI/Manager/UIModule.cs b/Runtime/UI/Manager/UIModule.cs index 5085d30..e68b26e 100644 --- a/Runtime/UI/Manager/UIModule.cs +++ b/Runtime/UI/Manager/UIModule.cs @@ -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 ShowUI(params System.Object[] userDatas) where T : UIBase + public T ShowUISync(params object[] userDatas) where T : UIBase { - return ShowUI(MetaTypeCache.Metadata, userDatas); + return (T)ShowUIImplSync(MetaTypeCache.Metadata, userDatas); } - public async UniTask ShowUIAsync(params System.Object[] userDatas) where T : UIBase + public async UniTask ShowUI(params System.Object[] userDatas) where T : UIBase { return (T)await ShowUIAsync(MetaTypeCache.Metadata, userDatas); } @@ -69,7 +68,7 @@ namespace AlicizaX.UI.Runtime public T GetUI() where T : UIBase { - return (T)GetUI(MetaTypeCache.Metadata); + return (T)GetUIImpl(MetaTypeCache.Metadata); } @@ -93,11 +92,6 @@ namespace AlicizaX.UI.Runtime } } - private UIBase GetUI(UIMetadata meta) - { - return (UIBase)GetUIImpl(meta); - } - void IUIModule.SetTimerManager(ITimerModule timerModule) { diff --git a/Runtime/UI/UIBase/UIBase.Widget.cs b/Runtime/UI/UIBase/UIBase.Widget.cs index 1455fa6..418260c 100644 --- a/Runtime/UI/UIBase/UIBase.Widget.cs +++ b/Runtime/UI/UIBase/UIBase.Widget.cs @@ -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.Shared.Return(temp); + ArrayPool.Shared.Return(temp, true); } _children.Clear(); @@ -61,28 +61,40 @@ namespace AlicizaX.UI.Runtime } } - internal async UniTask CreateWidget(UIMetadata metadata, Transform parent, bool visible) + internal async UniTask 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 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 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 CreateWidget(Transform parent, bool visible = true) where T : UIBase + protected async UniTask CreateWidgetAsync(Transform parent, bool visible = true) where T : UIBase { UIMetadata metadata = MetaTypeCache.Metadata; - return (T)await CreateWidget(metadata, parent, visible); + return (T)await CreateWidgetUIAsync(metadata, parent, visible); } - protected async UniTask CreateWidget(UIHolderObjectBase holder) where T : UIBase + protected async UniTask CreateWidgetAsync(UIHolderObjectBase holder) where T : UIBase { UIMetadata metadata = MetaTypeCache.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(Transform parent, bool visible = true) where T : UIBase + { + UIMetadata metadata = MetaTypeCache.Metadata; + return (T)CreateWidgetUISync(metadata, parent, visible); + } + + protected T CreateWidgetSync(UIHolderObjectBase holder) where T : UIBase + { + UIMetadata metadata = MetaTypeCache.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) diff --git a/Runtime/UI/UIBase/UITabWindow.cs b/Runtime/UI/UIBase/UITabWindow.cs index abbe286..ccff64d 100644 --- a/Runtime/UI/UIBase/UITabWindow.cs +++ b/Runtime/UI/UIBase/UITabWindow.cs @@ -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; diff --git a/Runtime/UI/UIComponent.cs b/Runtime/UI/UIComponent.cs index b588eef..3c47f34 100644 --- a/Runtime/UI/UIComponent.cs +++ b/Runtime/UI/UIComponent.cs @@ -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()