diff --git a/Plugins/UI/UISourceGenerator.dll b/Plugins/UI/UISourceGenerator.dll index 6aad363..a837344 100644 Binary files a/Plugins/UI/UISourceGenerator.dll and b/Plugins/UI/UISourceGenerator.dll differ diff --git a/Runtime/UI/Constant/UIHolderFactory.cs b/Runtime/UI/Constant/UIHolderFactory.cs index 954cca6..823790a 100644 --- a/Runtime/UI/Constant/UIHolderFactory.cs +++ b/Runtime/UI/Constant/UIHolderFactory.cs @@ -17,7 +17,7 @@ namespace AlicizaX.UI.Runtime ResourceModule = ModuleSystem.GetModule(); } - 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()