当前位置: 首页 > article >正文

Unity 简易的UI框架

核心内容
UIType.cs

namespace MYTOOL.UI
{
    /// <summary>
    /// UI层级
    /// </summary>
    public enum UILayer
    {
        /// <summary>
        /// 主界面层
        /// </summary>
        MainUI = 0,
        /// <summary>
        /// 普通界面层
        /// </summary>
        NormalUI = 1,
        /// <summary>
        /// 弹出层
        /// </summary>
        PopupUI = 2,
        /// <summary>
        /// Top层 高于弹出层
        /// </summary>
        Top = 3,
        /// <summary>
        /// 加载层 加载进度或加载动画
        /// </summary>
        Loading = 4,
    }


    /// <summary>
    /// UI组
    /// </summary>
    public enum UIGroup
    {
        Default = 0,

        //TODO
    }
}

UIBase.cs

using UnityEngine;

namespace MYTOOL.UI
{
    [DisallowMultipleComponent]
    [RequireComponent(typeof(RectTransform))]
    public abstract class UIBase : MonoBehaviour
    {
        /// <summary>
        /// UI层级
        /// </summary>
        public virtual UILayer Layer { get; } = UILayer.NormalUI;
        /// <summary>
        /// UI组
        /// </summary>
        public virtual UIGroup Group { get; } = UIGroup.Default;

        /// <summary>
        /// 同一层单独存在
        /// </summary>
        public virtual bool IsOnly { get; } = false;
        /// <summary>
        /// 总显示
        /// </summary>
        public virtual bool AlwaysDisplay { get; } = false;

        /// <summary>
        /// UI隐藏所需时间
        /// </summary>
        public virtual float UIHideTime { get; } = 0;
        /// <summary>
        /// UI关闭所需时间
        /// </summary>
        public virtual float UICloseTime { get; } = 0;

        /// <summary>
        /// UI名称
        /// </summary>
        public string UIName { get; private set; }

        private bool dirty = true;

        /// <summary>
        /// 脏标记
        /// </summary>
        public void SetDirty()
        {
            dirty = true;
        }

        protected virtual void Update()
        {
            if (dirty)
            {
                dirty = false;
                OnDirty();
                OnRefresh();
            }

            OnUpdate();
        }

        /// <summary>
        /// 创建时执行一次
        /// </summary>
        protected abstract void OnCreate();
        /// <summary>
        /// 显示UI
        /// </summary>
        protected virtual void OnShow() { }
        /// <summary>
        /// 隐藏UI
        /// </summary>
        protected virtual void OnHide() { }
        /// <summary>
        /// 关闭UI/销毁UI
        /// </summary>
        protected virtual void OnClose() { }
        /// <summary>
        /// 每帧执行
        /// </summary>
        protected virtual void OnUpdate() { }
        /// <summary>
        /// 刷新其它
        /// </summary>
        protected virtual void OnDirty() { }
        /// <summary>
        /// 刷新UI
        /// </summary>
        protected virtual void OnRefresh() { }

        /// <summary>
        /// 进入效果(显示)
        /// </summary>
        protected virtual void OnEnterEffect() { }
        /// <summary>
        /// 隐藏效果
        /// </summary>
        protected virtual void OnHideEffect() => OnExitEffect();
        /// <summary>
        /// 退出效果(隐藏/关闭)
        /// </summary>
        protected virtual void OnExitEffect() { }


        #region >> 内部方法

        protected RectTransform root;
        internal void InitUI(string uiName)
        {
            root = GetComponent<RectTransform>();
            root.localPosition = Vector3.zero;
            root.localScale = Vector3.one;
            root.anchorMin = Vector2.zero;
            root.anchorMax = Vector2.one;
            root.offsetMin = Vector2.zero;
            root.offsetMax = Vector2.zero;

            UIName = uiName;
            OnCreate();
        }

        internal void ShowUI()
        {
            gameObject.SetActive(true);
            transform.SetAsLastSibling();

            OnEnterEffect();
            OnShow();
        }

        internal void HideUI()
        {
            if (AlwaysDisplay)
            {
                Debug.LogWarning("Can't hide");
                return;
            }

            if (UIHideTime > 0)
            {
                OnHideEffect();
                OnHide();
                this.Seconds(UIHideTime, () => gameObject.SetActive(false));
            }
            else
            {
                OnHide();
                gameObject.SetActive(false);
            }
        }

        internal void CloseUI()
        {
            if (UICloseTime > 0)
            {
                OnExitEffect();
            }
            OnClose();
            Destroy(gameObject, UICloseTime);
        }

        internal void RefreshUI()
        {
            SetDirty();
        }

        #endregion
    }
}

UIManager.cs

using System;
using System.Linq;
using System.Reflection;
using System.Collections.Generic;
using UnityEngine;

namespace MYTOOL.UI
{
    /// <summary>
    /// UI管理类
    /// </summary>
    public partial class UIManager : MonoBehaviour
    {
        /// <summary>
        /// UI预设加载函数
        /// </summary>
        public Func<string, GameObject> ResLoaderFunc = uiPath => ResLoader.Ins.Load<GameObject>(uiPath, true);
        /// <summary>
        /// UI预设基础路径格式
        /// </summary>
        private const string UI_PREFAB_PATH_FORMAT = "UI/{0}";
        /// <summary>
        /// UI预设模块路径格式
        /// </summary>
        private const string UI_PREFAB_MODULE_PATH_FORMAT = "UI/{0}/{1}";


        #region >> 私有变量

        private Transform MainParent;
        private Transform NormalParent;
        private Transform PopParent;
        private Transform TopParent;
        private Transform LoadingParent;

        private readonly Dictionary<string, UIBase> UIBaseMap = new Dictionary<string, UIBase>();
        private UIBase CurrentMainUI;

        private readonly List<UIBase> NormalUIList = new List<UIBase>(16);
        private readonly List<UIBase> PopUIList = new List<UIBase>(16);

        #endregion


        private static UIManager _instace;

        public static UIManager Ins => Instance;

        public static UIManager Instance
        {
            get
            {
                if (_instace == null)
                {
                    var canvasPrefab = Resources.Load<GameObject>("MainCanvas");
                    var canvsGo = Instantiate(canvasPrefab);
                    canvsGo.name = "MainCanvas";
                    if (!canvsGo.TryGetComponent<UIManager>(out _instace))
                    {
                        _instace = canvsGo.AddComponent<UIManager>();
                    }
                    _instace.OnInit();

                    if (DebugMode) LogInfo($"UIManager initalize !");

                    DontDestroyOnLoad(_instace);
                }

                return _instace;
            }
        }

        private void OnInit()
        {
            MainParent = transform.Find("MainUI");
            NormalParent = transform.Find("NormalUI");
            PopParent = transform.Find("PopupUI");
            TopParent = transform.Find("Top");
            LoadingParent = transform.Find("Loading");
        }

        private void OnDestroy()
        {
            UIBaseMap.Clear();
            NormalUIList.Clear();
            PopUIList.Clear();
        }

        /// <summary>
        /// 获取UI相机
        /// </summary>
        /// <returns></returns>
        public Camera GetUICamera()
        {
            Canvas canvas = GetComponent<Canvas>();
            if (canvas.worldCamera)
                return canvas.worldCamera;

            return Camera.main;
        }

        public Transform GetUIParent(UILayer layer)
        {
            return layer switch
            {
                UILayer.MainUI => MainParent,
                UILayer.NormalUI => NormalParent,
                UILayer.PopupUI => PopParent,
                UILayer.Top => TopParent,
                UILayer.Loading => LoadingParent,
                _ => transform,
            };
        }


        #region >> 显示UI

        /// <summary>
        /// 显示UI
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public T ShowUI<T>() where T : UIBase
        {
            return InternalShowUI<T>(typeof(T).Name, string.Empty);
        }

        /// <summary>
        /// 显示UI
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="uiName">UI名称/预制体名称</param>
        /// <returns></returns>
        public T ShowUI<T>(string uiName) where T : UIBase
        {
            return InternalShowUI<T>(uiName, string.Empty);
        }

        /// <summary>
        /// 显示模块UI
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="modulePath">模块路径</param>
        /// <returns></returns>
        public T ShowModuleUI<T>(string modulePath) where T : UIBase
        {
            return InternalShowUI<T>(typeof(T).Name, modulePath);
        }

        /// <summary>
        /// 显示模块UI
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="uiName">UI名称/预制体名称</param>
        /// <param name="modulePath">模块路径</param>
        /// <returns></returns>
        public T ShowModuleUI<T>(string uiName, string modulePath) where T : UIBase
        {
            return InternalShowUI<T>(uiName, modulePath);
        }

        private T InternalShowUI<T>(string uiName, string modulePath) where T : UIBase
        {
#if UNITY_EDITOR
            System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch();
            stopwatch.Start();
#endif

            if (DebugMode) LogInfo($"UIManager.ShowUI => {uiName}");

            if (UIBaseMap.TryGetValue(uiName, out UIBase uibase))
            {
                //如果这个层唯一存在 那么先隐藏其他
                if (uibase.IsOnly)
                    PopAllUI(uibase.Layer);

                PushUI(uibase);
                uibase.ShowUI();
            }
            else
            {
                string uiPath = string.IsNullOrWhiteSpace(modulePath) ? string.Format(UI_PREFAB_PATH_FORMAT, uiName) : string.Format(UI_PREFAB_MODULE_PATH_FORMAT, modulePath, uiName);
                GameObject go = ResLoaderFunc(uiPath);
#if UNITY_EDITOR
                if (go == null)
                {
                    Debug.LogError($"UI not found, uiPath: {uiPath}");
                    return null;
                }
#endif
                uibase = OnUILoaded<T>(go, uiName);
            }

#if UNITY_EDITOR
            stopwatch.Stop();
            // 输出执行时间
            if (DebugMode) LogInfo($"UIManager.ShowUI => {uiName} Execution Time: {stopwatch.ElapsedMilliseconds} ms");
#endif

            return uibase as T;
        }

        #endregion


        #region >> 隐藏UI

        /// <summary>
        /// 隐藏UI
        /// </summary>
        /// <param name="uiName">UI名称</param>
        public void HideUI(string uiName)
        {
            if (UIBaseMap.TryGetValue(uiName, out UIBase uibase))
            {
                if (DebugMode) LogInfo($"UIManager.HideUI => {uiName}");

                if (uibase.Layer == UILayer.NormalUI)
                    PopUI(uibase, NormalUIList);
                else if (uibase.Layer == UILayer.PopupUI)
                    PopUI(uibase, PopUIList);
                else
                    uibase.HideUI();
            }
        }

        /// <summary>
        /// 隐藏UI
        /// </summary>
        /// <typeparam name="T"></typeparam>
        public void HideUI<T>() where T : UIBase
        {
            var uiName = typeof(T).Name;
            HideUI(uiName);
        }

        /// <summary>
        /// 隐藏UI
        /// </summary>
        /// <param name="uibase"></param>
        public void HideUI(UIBase uibase)
        {
            if (uibase != null)
            {
                HideUI(uibase.UIName);
            }
        }

        /// <summary>
        /// 隐藏所有UI
        /// </summary>
        public void HideAllUI()
        {
            if (DebugMode) LogInfo($"UIManager.HideAllUI => {UIBaseMap.Count}");

            foreach (var item in UIBaseMap)
            {
                HideUI(item.Key);
            }
        }

        #endregion


        #region >> 关闭UI/销毁UI

        /// <summary>
        /// 关闭UI/销毁UI
        /// </summary>
        /// <param name="uiName">UI名称</param>
        public void CloseUI(string uiName)
        {
            if (UIBaseMap.TryGetValue(uiName, out UIBase uibase))
            {
                if (DebugMode) LogInfo($"UIManager.CloseUI => {uiName}");

                //清理列表中元素
                if (uibase.Layer == UILayer.NormalUI && NormalUIList.Contains(uibase))
                {
                    NormalUIList.Remove(uibase);
                }
                else if (uibase.Layer == UILayer.PopupUI && PopUIList.Contains(uibase))
                {
                    PopUIList.Remove(uibase);
                }

                uibase.CloseUI();

                UIBaseMap.Remove(uiName);
            }
        }

        /// <summary>
        /// 关闭UI/销毁UI
        /// </summary>
        /// <typeparam name="T"></typeparam>
        public void CloseUI<T>() where T : UIBase
        {
            var uiName = typeof(T).Name;
            CloseUI(uiName);
        }

        /// <summary>
        /// 关闭UI/销毁UI
        /// </summary>
        /// <param name="uibase"></param>
        public void CloseUI(UIBase uibase)
        {
            if (uibase != null)
            {
                CloseUI(uibase.UIName);
            }
        }

        /// <summary>
        /// 关闭所有UI/销毁所有UI
        /// </summary>
        public void CloseAllUI()
        {
            if (DebugMode) LogInfo($"UIManager.CloseAllUI => {UIBaseMap.Count}");

            NormalUIList.Clear();
            PopUIList.Clear();

            var uiNames = UIBaseMap.Keys.ToList();
            foreach (var uiName in uiNames)
            {
                CloseUI(uiName);
            }
        }

        /// <summary>
        /// 关闭指定UI组/销毁指定UI组
        /// </summary>
        /// <param name="group">组</param>
        public void CloseUIWithGroup(UIGroup group)
        {
            var uiBases = UIBaseMap.Values.ToList();
            foreach (var uibase in uiBases)
            {
                if (uibase.Group == group)
                {
                    CloseUI(uibase.UIName);
                }
            }
        }

        #endregion


        #region >> 刷新UI

        /// <summary>
        /// 刷新UI
        /// </summary>
        /// <param name="uiName">UI名称</param>
        public void RefreshUI(string uiName)
        {
            if (UIBaseMap.TryGetValue(uiName, out UIBase uibase))
            {
                uibase.RefreshUI();
            }
        }

        /// <summary>
        /// 刷新UI
        /// </summary>
        /// <typeparam name="T"></typeparam>
        public void RefreshUI<T>() where T : UIBase
        {
            var uiName = typeof(T).Name;
            RefreshUI(uiName);
        }

        /// <summary>
        /// 刷新所有UI
        /// </summary>
        public void RefreshAllUI()
        {
            var uiBases = UIBaseMap.Values.ToList();
            foreach (var uibase in uiBases)
            {
                uibase.RefreshUI();
            }
        }

        #endregion


        #region >> 获取UI

        /// <summary>
        /// 获取UI
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public T GetUI<T>() where T : UIBase
        {
            TryGetUI(typeof(T).Name, out UIBase uibase);
            return uibase as T;
        }

        /// <summary>
        /// 获取UI
        /// </summary>
        /// <param name="uiName">UI名称</param>
        /// <returns></returns>
        public UIBase GetUI(string uiName)
        {
            TryGetUI(uiName, out UIBase uibase);
            return uibase;
        }

        /// <summary>
        /// 尝试获取UI
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public bool TryGetUI<T>(out T uibase) where T : UIBase
        {
            if (TryGetUI(typeof(T).Name, out UIBase value))
            {
                uibase = value as T;
                return true;
            }

            uibase = null;
            return false;
        }

        /// <summary>
        /// 尝试获取UI
        /// </summary>
        /// <param name="uiName">UI名称</param>
        /// <returns></returns>
        public bool TryGetUI(string uiName, out UIBase uibase)
        {
            if (UIBaseMap.TryGetValue(uiName, out uibase))
            {
                return true;
            }

            return false;
        }

        #endregion


        #region >> UI是否打开

        /// <summary>
        /// UI是否打开
        /// </summary>
        /// <param name="uiName">UI名称</param>
        /// <returns></returns>
        public bool IsUIOpen(string uiName)
        {
            if (UIBaseMap.TryGetValue(uiName, out UIBase uibase))
                return uibase != null && uibase.gameObject.activeSelf;

            return false;
        }

        /// <summary>
        /// UI是否打开
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public bool IsUIOpen<T>() where T : UIBase
        {
            var uiName = typeof(T).Name;
            return IsUIOpen(uiName);
        }

        #endregion


        #region >> 私有方法

        /// <summary>
        /// UI加载完成后的回调
        /// </summary>
        private UIBase OnUILoaded<T>(GameObject go, string uiName)
        {
            UIBase uibase = GetOrAddComponent(go.transform, typeof(T).Name) as UIBase;
            go.transform.SetParent(GetUIParent(uibase.Layer), false);
            UIBaseMap[uiName] = uibase;

            uibase.InitUI(uiName);
            //如果这个层唯一存在 那么先关闭其他
            if (uibase.IsOnly)
                PopAllUI(uibase.Layer);

            PushUI(uibase);
            uibase.ShowUI();
            return uibase;
        }

        private static Assembly assembly;
        /// <summary>
        /// 加载外部程序集
        /// </summary>
        /// <param name="assemblyString">UI脚本程序集名称</param>
        public static void LoadAssembly(string assemblyString = "Assembly-CSharp")
        {
            assembly = Assembly.Load(assemblyString);
        }

        private Component GetOrAddComponent(Transform trans, string type)
        {
            Component component = trans.GetComponent(type);
            if (component == null)
            {
                Type t;
                if (assembly == null)
                    t = Type.GetType(type);
                else
                    t = assembly.GetType(type);

                component = trans.gameObject.AddComponent(t);
            }

            return component;
        }

        private void PushUI(UIBase uibase)
        {
            switch (uibase.Layer)
            {
                case UILayer.MainUI:
                    if (CurrentMainUI && CurrentMainUI != uibase)
                        CurrentMainUI.HideUI();
                    CurrentMainUI = uibase;
                    break;

                case UILayer.NormalUI:
                    if (NormalUIList.Contains(uibase) == false)
                        NormalUIList.Add(uibase);
                    break;

                case UILayer.PopupUI:
                    if (PopUIList.Contains(uibase) == false)
                        PopUIList.Add(uibase);
                    break;
            }
        }

        private void PopUI(UIBase uibase, List<UIBase> uiList)
        {
            if (uiList.Contains(uibase))
            {
                uiList.Remove(uibase);
                uibase.HideUI();
            }
        }

        private void PopAllUI(UILayer layer)
        {
            if (layer == UILayer.NormalUI)
                ClearAndHideUI(NormalUIList);
            else if (layer == UILayer.PopupUI)
                ClearAndHideUI(PopUIList);
        }

        private void ClearAndHideUI(List<UIBase> uiList)
        {
            foreach (var uibase in uiList)
            {
                if (uibase != null)
                {
                    uibase.HideUI();
                }
            }

            uiList.Clear();
        }

        public static bool DebugMode = true;
        private static void LogInfo(string info, string color = "#00ffff")
        {
            if (DebugMode) Debug.LogFormat("<color={0}>{1}</color>", color, info);
        }

        #endregion
    }
}

UIManagerAsync.cs

using System;
using System.Threading.Tasks;
using UnityEngine;

namespace MYTOOL.UI
{
    /// <summary>
    /// UIManager异步方法
    /// </summary>
    public partial class UIManager : MonoBehaviour
    {
        /// <summary>
        /// UI预设异步加载函数
        /// </summary>
        public Func<string, Task<GameObject>> ResLoaderFuncAsync = uiPath => ResLoader.Ins.LoadAsync<GameObject>(uiPath, true);


        #region >> 异步显示UI

        /// <summary>
        /// 异步显示UI
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public async Task<T> ShowUIAsync<T>() where T : UIBase
        {
            return await InternalShowUIAsync<T>(typeof(T).Name, string.Empty);
        }

        /// <summary>
        /// 异步显示UI
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="uiName">UI名称/预制体名称</param>
        /// <returns></returns>
        public async Task<T> ShowUIAsync<T>(string uiName) where T : UIBase
        {
            return await InternalShowUIAsync<T>(uiName, string.Empty);
        }

        /// <summary>
        /// 异步显示模块UI
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="modulePath">模块路径</param>
        /// <returns></returns>
        public async Task<T> ShowModuleUIAsync<T>(string modulePath) where T : UIBase
        {
            return await InternalShowUIAsync<T>(typeof(T).Name, modulePath);
        }

        /// <summary>
        /// 异步显示模块UI
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="uiName">UI名称/预制体名称</param>
        /// <param name="modulePath">模块路径</param>
        /// <returns></returns>
        public async Task<T> ShowModuleUIAsync<T>(string uiName, string modulePath) where T : UIBase
        {
            return await InternalShowUIAsync<T>(uiName, modulePath);
        }


        private async Task<T> InternalShowUIAsync<T>(string uiName, string modulePath) where T : UIBase
        {
#if UNITY_EDITOR
            System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch();
            stopwatch.Start();
#endif

            if (DebugMode) LogInfo($"UIManager.ShowUIAsync => {uiName}");

            if (UIBaseMap.TryGetValue(uiName, out UIBase uibase))
            {
                //如果这个层唯一存在 那么先隐藏其他
                if (uibase.IsOnly)
                    PopAllUI(uibase.Layer);

                PushUI(uibase);
                uibase.ShowUI();
            }
            else
            {
                string uiPath = string.IsNullOrWhiteSpace(modulePath) ? string.Format(UI_PREFAB_PATH_FORMAT, uiName) : string.Format(UI_PREFAB_MODULE_PATH_FORMAT, modulePath, uiName);
                GameObject go = await ResLoaderFuncAsync(uiPath);
#if UNITY_EDITOR
                if (go == null)
                {
                    Debug.LogError($"UI not found, uiPath: {uiPath}");
                    return null;
                }
#endif
                uibase = OnUILoaded<T>(go, uiName);
            }

#if UNITY_EDITOR
            stopwatch.Stop();
            // 输出执行时间
            if (DebugMode) LogInfo($"UIManager.ShowUIAsync => {uiName} Execution Time: {stopwatch.ElapsedMilliseconds} ms");
#endif

            return uibase as T;
        }

        #endregion
    }
}

UIBaseExtension.cs

using System;
using UnityEngine.UI;
using UnityEngine.Events;

namespace MYTOOL.UI
{
    /// <summary>
    /// UIBase扩展类
    /// </summary>
    public static class UIBaseExtension
    {
        /// <summary>
        /// 显示UI
        /// </summary>
        public static void ShowUI<T>(this UIBase uibase) where T : UIBase
        {
            UIManager.Instance.ShowUI<T>();
        }
        /// <summary>
        /// 显示UI
        /// </summary>
        public static void ShowUI<T>(this UIBase uibase, string uiName) where T : UIBase
        {
            UIManager.Instance.ShowUI<T>(uiName);
        }

        /// <summary>
        /// 显示模块UI
        /// </summary>
        public static void ShowModuleUI<T>(this UIBase uibase, string modulePath) where T : UIBase
        {
            UIManager.Instance.ShowModuleUI<T>(modulePath);
        }
        /// <summary>
        /// 显示模块UI
        /// </summary>
        public static void ShowModuleUI<T>(this UIBase uibase, string uiName, string modulePath) where T : UIBase
        {
            UIManager.Instance.ShowModuleUI<T>(uiName, modulePath);
        }

        /// <summary>
        /// 刷新自己
        /// </summary>
        /// <param name="uibase"></param>
        public static void RefreshSelf(this UIBase uibase)
        {
            UIManager.Instance.RefreshUI(uibase.UIName);
        }

        /// <summary>
        /// 隐藏自己
        /// </summary>
        /// <param name="uibase"></param>
        public static void HideSelf(this UIBase uibase)
        {
            UIManager.Instance.HideUI(uibase);
        }

        /// <summary>
        /// 关闭自己
        /// </summary>
        /// <param name="uibase"></param>
        public static void CloseSelf(this UIBase uibase)
        {
            UIManager.Instance.CloseUI(uibase);
        }

        /// <summary>
        /// 延迟N帧执行
        /// </summary>
        /// <param name="uibase"></param>
        /// <param name="frame"></param>
        /// <param name="callback"></param>
        public static UnityEngine.Coroutine Frames(this UIBase uibase, int frame, Action callback)
        {
            return MonoManager.Ins.Frames(frame, callback);
        }
        /// <summary>
        /// 每N帧执行一次
        /// </summary>
        /// <param name="uibase"></param>
        /// <param name="frame"></param>
        /// <param name="count">执行次数</param>
        /// <param name="callback"></param>
        public static UnityEngine.Coroutine FrameLoops(this UIBase uibase, int frame, int count, Action callback)
        {
            return MonoManager.Ins.FrameLoops(frame, count, callback);
        }
        /// <summary>
        /// 延迟N秒执行
        /// </summary>
        /// <param name="uibase"></param>
        /// <param name="seconds"></param>
        /// <param name="callback"></param>
        public static UnityEngine.Coroutine Seconds(this UIBase uibase, float seconds, Action callback)
        {
            return MonoManager.Ins.Seconds(seconds, callback);
        }
        /// <summary>
        /// 每N秒执行一次
        /// </summary>
        /// <param name="uibase"></param>
        /// <param name="seconds"></param>
        /// <param name="count">执行次数</param>
        /// <param name="callback"></param>
        public static UnityEngine.Coroutine SecondLoops(this UIBase uibase, float seconds, int count, Action callback)
        {
            return MonoManager.Ins.SecondLoops(seconds, count, callback);
        }

        /// <summary>
        /// 按钮绑定监听
        /// </summary>
        public static void BindListener(this UIBase uibase, Button button, UnityAction callback, bool overwrite = false)
        {
            if (button != null)
            {
                if (overwrite)
                    button.onClick.RemoveAllListeners();

                button.onClick.AddListener(callback);
            }
            else
            {
                UnityEngine.Debug.LogWarning($"[{uibase.GetType()}] - Some button is null", uibase);
            }
        }

        /// <summary>
        /// 开关绑定监听
        /// </summary>
        public static void BindListener(this UIBase uibase, Toggle toggle, UnityAction<bool> callback, bool overwrite = false)
        {
            if (toggle != null)
            {
                if (overwrite)
                    toggle.onValueChanged.RemoveAllListeners();

                toggle.onValueChanged.AddListener(callback);
            }
            else
            {
                UnityEngine.Debug.LogWarning($"[{uibase.GetType()}] - Some toggle is null", uibase);
            }
        }
    }
}

其它工具类
MonoManager.cs

using System;
using System.Collections;
using UnityEngine;

namespace MYTOOL
{
    public enum UpdateType : byte
    {
        Update,
        LateUpdate,
        FixedUpdate
    }

    /// <summary>
    /// Mono管理类
    /// </summary>
    public class MonoManager : SingletonTemplate<MonoManager>
    {
        private MonoManager() { }

        private int runningCoroutineCount = 0;

        /// <summary>
        /// 获取当前运行中的协程数量
        /// </summary>
        /// <returns></returns>
        public int GetRunningCoroutineCount()
        {
            return runningCoroutineCount;
        }

        /// <summary>
        /// 开启协程
        /// </summary>
        /// <param name="routine"></param>
        /// <returns></returns>
        public Coroutine StartCoroutine(IEnumerator routine)
        {
            if (routine != null)
            {
                runningCoroutineCount++;
                return MonoController.Ins.StartCoroutine(TrackCoroutine(routine));
            }

            return null;
        }

        /// <summary>
        /// 停止协程
        /// </summary>
        /// <param name="routine"></param>
        public void StopCoroutine(Coroutine routine)
        {
            if (routine != null)
            {
                runningCoroutineCount--;
                MonoController.Ins.StopCoroutine(routine);
            }
        }

        /// <summary>
        /// 停止所有协程
        /// </summary>
        public void StopAllCoroutines()
        {
            runningCoroutineCount = 0;
            MonoController.Ins.StopAllCoroutines();
        }

        private IEnumerator TrackCoroutine(IEnumerator routine)
        {
            yield return routine;
            runningCoroutineCount--;
        }


        #region >> Unity Update

        /// <summary>
        /// 添加监听
        /// </summary>
        /// <param name="type">类型</param>
        /// <param name="callback">回调</param>
        public void AddUpdate(UpdateType type, Action callback)
        {
            if (callback != null)
            {
                MonoController.Ins.AddUpdate(type, callback);
            }
        }

        /// <summary>
        /// 移除监听
        /// </summary>
        /// <param name="type">类型</param>
        /// <param name="callback">回调</param>
        public void RemoveUpdate(UpdateType type, Action callback)
        {
            if (callback != null)
            {
                MonoController.Ins.RemoveUpdate(type, callback);
            }
        }

        /// <summary>
        /// 移除指定类型所有监听
        /// </summary>
        /// <param name="type">指定类型</param>
        public void RemoveAll(UpdateType type)
        {
            MonoController.Ins.RemoveAll(type);
        }

        /// <summary>
        /// 移除所有监听
        /// </summary>
        public void RemoveAll()
        {
            MonoController.Ins.RemoveAll();
        }

        #endregion


        #region >> Delay Methods

        /// <summary>
        /// 延迟N帧后执行一次
        /// </summary>
        /// <param name="frame"></param>
        /// <param name="callback"></param>
        public Coroutine Frames(int frame, Action callback)
        {
            if (callback != null)
            {
                return StartCoroutine(InternalFrames(frame, 1, callback));
            }

            return null;
        }

        /// <summary>
        /// 每N帧执行一次
        /// </summary>
        /// <param name="frame"></param>
        /// <param name="count">执行次数</param>
        /// <param name="callback"></param>
        public Coroutine FrameLoops(int frame, int count, Action callback)
        {
            if (callback != null)
            {
                return StartCoroutine(InternalFrames(frame, count, callback));
            }

            return null;
        }

        private IEnumerator InternalFrames(int frame, int count, Action callback)
        {
            frame = Math.Max(1, frame);
            while (count == -1 || count-- > 0)
            {
                for (int i = 0; i < frame; i++)
                {
                    yield return null;
                }

                callback.Invoke();
            }
        }

        /// <summary>
        /// 延迟N秒后执行一次
        /// </summary>
        /// <param name="seconds"></param>
        /// <param name="callback"></param>
        public Coroutine Seconds(float seconds, Action callback)
        {
            if (callback != null)
            {
                return StartCoroutine(InternalSeconds(seconds, 1, callback));
            }

            return null;
        }

        /// <summary>
        /// 每N秒执行一次
        /// </summary>
        /// <param name="seconds"></param>
        /// <param name="count">执行次数</param>
        /// <param name="callback"></param>
        public Coroutine SecondLoops(float seconds, int count, Action callback)
        {
            if (callback != null)
            {
                return StartCoroutine(InternalSeconds(seconds, count, callback));
            }

            return null;
        }

        private IEnumerator InternalSeconds(float seconds, int count, Action callback)
        {
            seconds = Mathf.Max(0.01f, seconds);
            while (count == -1 || count-- > 0)
            {
                yield return Wait(seconds);
                callback.Invoke();
            }
        }

        //等待时间的精度与帧率有关
        private IEnumerator Wait(float interval)
        {
            var waitForEndOfFrame = new WaitForEndOfFrame();
            float time = 0;
            while (time < interval)
            {
                time += Time.deltaTime;
                yield return waitForEndOfFrame;
            }
        }

        #endregion


        private class MonoController : MonoSingletonTemplate<MonoController>
        {
            private event Action OnUpdate;
            private event Action OnLateUpdate;
            private event Action OnFixedUpdate;


            #region >> Unity Update

            void Update()
            {
                OnUpdate?.Invoke();
            }

            private void LateUpdate()
            {
                OnLateUpdate?.Invoke();
            }

            private void FixedUpdate()
            {
                OnFixedUpdate?.Invoke();
            }

            public void AddUpdate(UpdateType type, Action callback)
            {
                switch (type)
                {
                    case UpdateType.Update:
                        OnUpdate += callback;
                        return;

                    case UpdateType.LateUpdate:
                        OnLateUpdate += callback;
                        return;

                    case UpdateType.FixedUpdate:
                        OnFixedUpdate += callback;
                        return;
                }
            }

            public void RemoveUpdate(UpdateType type, Action callback)
            {
                switch (type)
                {
                    case UpdateType.Update:
                        OnUpdate -= callback;
                        return;

                    case UpdateType.LateUpdate:
                        OnLateUpdate -= callback;
                        return;

                    case UpdateType.FixedUpdate:
                        OnFixedUpdate -= callback;
                        return;
                }
            }

            public void RemoveAll(UpdateType type)
            {
                switch (type)
                {
                    case UpdateType.Update:
                        OnUpdate = null;
                        return;

                    case UpdateType.LateUpdate:
                        OnLateUpdate = null;
                        return;

                    case UpdateType.FixedUpdate:
                        OnFixedUpdate = null;
                        return;
                }
            }


            public void RemoveAll()
            {
                OnUpdate = null;
                OnLateUpdate = null;
                OnFixedUpdate = null;
            }

            #endregion


            /// <summary>
            /// 重写-退出时也可以访问实例对象
            /// </summary>
            protected override void OnApplicationQuit()
            {
                IsApplicationQuit = false;
            }
        }
    }
}

ResLoader.cs

using UnityEngine;
using UnityEngine.U2D;
using System.Collections;
using System.Collections.Generic;
using System.Threading.Tasks;
using UObject = UnityEngine.Object;

namespace MYTOOL
{
    public class ResLoader : MonoSingletonTemplate<ResLoader>
    {
        private readonly Dictionary<string, UObject> cacheRes = new Dictionary<string, UObject>(128);

        /// <summary>
        /// 同步加载资源
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="path">资源路径</param>
        /// <param name="useCache">是否缓存</param>
        /// <returns></returns>
        public T Load<T>(string path, bool useCache = false) where T : UObject
        {
            if (cacheRes.TryGetValue(path, out var value))
            {
                return ProcessRes<T>(value);
            }
            else
            {
                T resObj = Resources.Load<T>(path);
                if (useCache && resObj != null) cacheRes[path] = resObj;

                return ProcessRes<T>(resObj);
            }
        }

        /// <summary>
        /// 异步加载资源
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="path">资源路径</param>
        /// <param name="callback">回调函数</param>
        /// <param name="useCache">是否缓存</param>
        /// <returns></returns>
        public void LoadAsync<T>(string path, System.Action<T> callback, bool useCache = false) where T : UObject
        {
            StartCoroutine(LoadAsyncCoroutine(path, callback, useCache));
        }
        private IEnumerator LoadAsyncCoroutine<T>(string path, System.Action<T> callback, bool useCache) where T : UObject
        {
            if (cacheRes.TryGetValue(path, out var value))
            {
                callback?.Invoke(ProcessRes<T>(value));
            }
            else
            {
                ResourceRequest R = Resources.LoadAsync<T>(path);
                yield return R;

                if (useCache && R.asset != null) cacheRes[path] = R.asset;

                callback?.Invoke(ProcessRes<T>(R.asset));
            }
        }

        /// <summary>
        /// 同步加载GameObject
        /// </summary>
        /// <param name="path">资源路径</param>
        /// <param name="parent"></param>
        /// <param name="useCache">是否缓存</param>
        /// <returns></returns>
        public GameObject LoadGameObject(string path, Transform parent, bool useCache = false)
        {
            return LoadGameObject(path, parent, false, useCache);
        }
        /// <summary>
        /// 同步加载GameObject
        /// </summary>
        /// <param name="path">资源路径</param>
        /// <param name="parent"></param>
        /// <param name="worldPositionStays"></param>
        /// <param name="useCache">是否缓存</param>
        /// <returns></returns>
        public GameObject LoadGameObject(string path, Transform parent, bool worldPositionStays, bool useCache = false)
        {
            if (cacheRes.TryGetValue(path, out var value))
            {
                return Instantiate(value, parent, worldPositionStays) as GameObject;
            }
            else
            {
                GameObject resObj = Resources.Load<GameObject>(path);
                if (useCache && resObj != null) cacheRes[path] = resObj;

                return Instantiate(resObj, parent, worldPositionStays);
            }
        }

        /// <summary>
        /// 异步加载GameObject
        /// </summary>
        /// <param name="path">资源路径</param>
        /// <param name="parent"></param>
        /// <param name="callback">回调函数</param>
        /// <param name="useCache">是否缓存</param>
        public void LoadGameObjectAsync(string path, Transform parent, System.Action<GameObject> callback, bool useCache = false)
        {
            StartCoroutine(LoadGameObjectAsyncCoroutine(path, parent, false, callback, useCache));
        }
        /// <summary>
        /// 异步加载GameObject
        /// </summary>
        /// <param name="path">资源路径</param>
        /// <param name="parent"></param>
        /// <param name="worldPositionStays"></param>
        /// <param name="callback">回调函数</param>
        /// <param name="useCache">是否缓存</param>
        public void LoadGameObjectAsync(string path, Transform parent, bool worldPositionStays, System.Action<GameObject> callback, bool useCache = false)
        {
            StartCoroutine(LoadGameObjectAsyncCoroutine(path, parent, worldPositionStays, callback, useCache));
        }

        private IEnumerator LoadGameObjectAsyncCoroutine(string path, Transform parent, bool worldPositionStays, System.Action<GameObject> callback, bool useCache)
        {
            if (cacheRes.TryGetValue(path, out var value))
            {
                callback?.Invoke(Instantiate(value, parent, worldPositionStays) as GameObject);
            }
            else
            {
                ResourceRequest R = Resources.LoadAsync<GameObject>(path);
                yield return R;

                if (useCache && R.asset != null) cacheRes[path] = R.asset;

                callback?.Invoke(Instantiate(R.asset, parent, worldPositionStays) as GameObject);
            }
        }

        /// <summary>
        /// 同步加载SpriteAtlas
        /// </summary>
        /// <param name="path">资源路径</param>
        /// <param name="useCache">是否缓存</param>
        /// <returns></returns>
        public SpriteAtlas LoadSpriteAtlas(string path, bool useCache = false)
        {
            if (cacheRes.TryGetValue(path, out var value))
            {
                return value as SpriteAtlas;
            }
            else
            {
                SpriteAtlas atlas = Resources.Load<SpriteAtlas>(path);
                if (useCache && atlas != null) cacheRes[path] = atlas;

                return atlas;
            }
        }

        /// <summary>
        /// 异步加载SpriteAtlas
        /// </summary>
        /// <param name="path">资源路径</param>
        /// <param name="callback">回调函数</param>
        /// <param name="useCache">是否缓存</param>
        public void LoadSpriteAtlasAsync(string path, System.Action<SpriteAtlas> callback, bool useCache = false)
        {
            StartCoroutine(LoadSpriteAtlasAsyncCoroutine(path, callback, useCache));
        }
        /// <summary>
        /// 异步加载SpriteAtlas
        /// </summary>
        /// <param name="path">资源路径</param>
        /// <param name="callback">回调函数</param>
        /// <param name="useCache">是否缓存</param>
        /// <returns></returns>
        private IEnumerator LoadSpriteAtlasAsyncCoroutine(string path, System.Action<SpriteAtlas> callback, bool useCache)
        {
            if (cacheRes.TryGetValue(path, out var value))
            {
                callback?.Invoke(value as SpriteAtlas);
            }
            else
            {
                ResourceRequest R = Resources.LoadAsync<SpriteAtlas>(path);
                yield return R;

                if (useCache && R.asset != null) cacheRes[path] = R.asset;

                callback?.Invoke(R.asset as SpriteAtlas);
            }
        }

        /// <summary>
        /// 异步加载资源(可等待)
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="path">资源路径</param>
        /// <param name="useCache">是否缓存</param>
        /// <returns></returns>
        public async Task<T> LoadAsync<T>(string path, bool useCache = false) where T : UObject
        {
            if (cacheRes.TryGetValue(path, out var value))
            {
                return ProcessRes<T>(value);
            }
            else
            {
                ResourceRequest R = Resources.LoadAsync<T>(path);
                while (!R.isDone)
                {
                    await Task.Yield();
                }

                if (useCache && R.asset != null) cacheRes[path] = R.asset;
                return ProcessRes<T>(R.asset);
            }
        }

        /// <summary>
        /// 移除缓存资源
        /// </summary>
        /// <param name="key">资源路径</param>
        /// <returns></returns>
        public bool RemoveCache(string key)
        {
            return cacheRes.Remove(key);
        }

        /// <summary>
        /// 移除所有缓存资源
        /// </summary>
        public void RemoveAllCache()
        {
            cacheRes.Clear();
        }

        private static T ProcessRes<T>(UObject resObj) where T : UObject
        {
            if (resObj is GameObject)
            {
                return Instantiate(resObj) as T;
            }
            else
            {
                return resObj as T;
            }
        }
    }
}

TransformExtension.cs

using UnityEngine;
using System.Collections.Generic;

namespace MYTOOL.UI
{
    public static class TransformExtension
    {
        /// <summary>
        /// Find & GetComponent
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="transform"></param>
        /// <param name="n">路径</param>
        /// <returns></returns>
        public static T FindAndGet<T>(this Transform transform, string n) where T : Component
        {
            return transform.Find(n).GetComponent<T>();
        }

        /// <summary>
        /// Find & GetComponents
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="transform"></param>
        /// <param name="n">路径</param>
        /// <returns></returns>
        public static List<T> FindAndGets<T>(this Transform transform, string n) where T : Component
        {
            List<T> list = new List<T>();

            Transform parent = transform.Find(n);
            foreach (Transform child in parent)
            {
                if (child.TryGetComponent<T>(out var component))
                {
                    list.Add(component);
                }
            }

            return list;
        }

        public static void GetComponentAtPath<T>(this Transform transform, string n, out T value) where T : Component
        {
            value = transform.Find(n).GetComponent<T>();
        }
    }
}

MainCanvas(画布)
在这里插入图片描述


http://www.kler.cn/a/535398.html

相关文章:

  • 三星OEM版SSD固态硬盘Model码对应关系
  • 【React】受控组件和非受控组件
  • Spring的Bean的生命周期 笔记250206
  • SpringBoot开发(五)SpringBoot接收请求参数
  • 文献阅读分享《新闻推荐中的审议式多样性:操作化与实验用户研究》
  • 71.StackPanel黑白棋盘 WPF例子 C#例子
  • C和Rust的一些区别
  • C中静态库和动态库的使用
  • 数据治理项目为什么沦为了PPT工程?
  • 2025.2.6(c++杂项补充及qt基础介绍)
  • Vue Dom截图插件,截图转Base64 html2canvas
  • H5+CSS+JS制作好看的轮播图
  • OSPF基础(2):数据包详解
  • 51单片机07 串口通信
  • 【C语言】常量指针和指针常量,指针数组和数组指针,指针函数和函数指针怎么区分?
  • vue2-nextTick
  • JAVA面试框架篇
  • 注册中心不知选哪个?Zookeeper、Eureka、Nacos、Consul和Etcd 5种全方位剖析对比
  • Python利用VideoCapture和FFmpeg读取多个rtsp流性能的比较
  • idea整合deepseek实现AI辅助编程
  • 【React】表单校验:从基础到集成库
  • Chapter 4-1. Troubleshooting Congestion in Fibre Channel Fabrics
  • π0开源了且推出自回归版π0-FAST——打造机器人动作专用的高效Tokenizer:比扩散π0的训练速度快5倍但效果相当
  • 2025_1_31 C语言中关于数组和指针
  • CentOS 7.9-2207更换实时内核
  • 【2025最新计算机毕业设计】基于SSM的智能停车场管理系统【提供源码+答辩PPT+文档+项目部署】(高质量源码,可定制,提供文档,免费部署到本地)