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);#endregionprivate 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_EDITORSystem.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch();stopwatch.Start();
#endifif (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_EDITORif (go == null){Debug.LogError($"UI not found, uiPath: {uiPath}");return null;}
#endifuibase = OnUILoaded<T>(go, uiName);}#if UNITY_EDITORstopwatch.Stop();// 输出执行时间if (DebugMode) LogInfo($"UIManager.ShowUI => {uiName} Execution Time: {stopwatch.ElapsedMilliseconds} ms");
#endifreturn 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);elseuibase.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);elset = 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_EDITORSystem.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch();stopwatch.Start();
#endifif (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_EDITORif (go == null){Debug.LogError($"UI not found, uiPath: {uiPath}");return null;}
#endifuibase = OnUILoaded<T>(go, uiName);}#if UNITY_EDITORstopwatch.Stop();// 输出执行时间if (DebugMode) LogInfo($"UIManager.ShowUIAsync => {uiName} Execution Time: {stopwatch.ElapsedMilliseconds} ms");
#endifreturn 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;}}#endregionprivate class MonoController : MonoSingletonTemplate<MonoController>{private event Action OnUpdate;private event Action OnLateUpdate;private event Action OnFixedUpdate;#region >> Unity Updatevoid 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.mzph.cn/news/894850.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

VUE2双向绑定的原理

文章目录 VUE2双向绑定的原理1. 什么是双向绑定2. 双向绑定的原理2.1 ViewModel的重要作用2.2 双向绑定的流程 3. 双向绑定的实现3.1 data响应化处理3.2 Compile编译3.3 依赖收集 VUE2双向绑定的原理 1. 什么是双向绑定 讲双向绑定先讲单项绑定&#xff0c;啥叫单项绑定&…

4G核心网的演变与创新:从传统到虚拟化的跨越

4G核心网 随着移动通信技术的不断发展&#xff0c;4G核心网已经经历了从传统的硬件密集型架构到现代化、虚拟化网络架构的重大转型。这一演变不仅提升了网络的灵活性和可扩展性&#xff0c;也为未来的5G、物联网&#xff08;LOT&#xff09;和边缘计算等技术的发展奠定了基础。…

HTML排版标签、语义化标签、块级和行内元素详解

目录 前言 一、HTML中的排版标签 1. 文本相关标签 1.1 标题标签 ~ 1.2 段落标签 1.3 强调和加粗 1.4 换行标签 1.5 水平线标签 二、HTML中的语义化标签 2.1 语义化标签概述 2.2 常见的语义化标签 示例&#xff08;核心代码部分&#xff09;&#xff1a; 三、HTM…

【字节青训营-7】:初探 Kitex 字节微服务框架(使用ETCD进行服务注册与发现)

本文目录 一、Kitex概述二、第一个Kitex应用三、IDL四、服务注册与发现 一、Kitex概述 长话短说&#xff0c;就是字节跳动内部的 Golang 微服务 RPC 框架&#xff0c;具有高性能、强可扩展的特点&#xff0c;在字节内部已广泛使用。 如果对微服务性能有要求&#xff0c;又希望…

【数学】矩阵、向量(内含矩阵乘法C++)

目录 一、前置知识&#xff1a;向量&#xff08;一列或一行的矩阵&#xff09;、矩阵1. 行向量2. 列向量3. 向量其余基本概念4. 矩阵基本概念5. 关于它们的细节 二、运算1. 转置&#xff08;1&#xff09;定义&#xff08;2&#xff09;性质 2. 矩阵&#xff08;向量&#xff0…

浅尝yolo11全程记录1-准备环境+官网模型推理(个人备份)

准备工作&#xff08;虚拟环境、导入项目&#xff09; 安装Anaconda 主要是为了创建和管理虚拟环境&#xff0c;在pycharm里按照项目里的requirments.txt安装依赖的时候&#xff0c;使用虚拟环境会好很多&#xff08;我记得不用Anaconda也可以直接在pycharm的terminal里头创建…

5.攻防世界 fileinclude

进入题目页面如下 提示flag在flag.php ctrlu&#xff0c;查看源码 给出了一段PHP代码&#xff0c;进行代码审计 <?php // 检查是否开启了错误显示功能 if( !ini_get(display_errors) ) {// 如果没有开启&#xff0c;则将错误显示功能设置为开启状态ini_set(display_error…

红包雨项目前端部分

创建项目 pnpm i -g vue/cli vue create red_pakage pnpm i sass sass-locader -D pnpm i --save normalize.css pnpm i --save-dev postcss-px-to-viewportpnpm i vantlatest-v2 -S pnpm i babel-plugin-import -Dhttps://vant.pro/vant/v2/#/zh-CN/<van-button click&…

蓝桥杯嵌入式备赛(三)—— LED +按键 + LCD

目录 一、LED1、原理图介绍2、程序代码 二、按键1、原理图介绍2、程序代码 三、LCD1、原理图介绍2、程序代码 一、LED 1、原理图介绍 如果所示&#xff0c;STM32G431RBT6中有八个LED&#xff0c;由八个GPIO控制&#xff0c;分别为PC8-15&#xff0c;当输出为低电平时点亮。其中…

[Java基础]函数式编程

Lambda函数 JDK8新增的语法形式, 使用Lambda函数替代某些匿名内部类对象&#xff0c;从而让程序代码更简洁&#xff0c;可读性更好。 基本使用 lambda表达式只能简化函数式接口的匿名内部类写法 // 1.定义抽象类 abstract class Animal {public abstract void crt(); }publi…

Vim 多窗口编辑及文件对比

水平分割 :split 默认使用水平分割的方式。 :split :sp 垂直分割 :vsplit :vs 带文件的分割 :split 文件名 :sp 文件名 在光标所在的窗口&#xff0c;输入分割窗口命令就会对那个窗口进行分割。 切换窗口 Ctrlw 切换正在编辑的窗口 快速分割窗口 Ctrlwn 快速分割当前…

二级C语言题解:十进制转其他进制、非素数求和、重复数统计

目录 一、程序填空&#x1f4dd; --- 十进制转其他进制 题目&#x1f4c3; 分析&#x1f9d0; 二、程序修改&#x1f6e0;️ --- 非素数求和 题目&#x1f4c3; 分析&#x1f9d0; 三、程序设计&#x1f4bb; --- 重复数统计 题目&#x1f4c3; 分析&#x1f9d0; 前言…

使用服务器部署DeepSeek-R1模型【详细版】

文章目录 引言deepseek-r1IDE或者终端工具算力平台体验deepseek-r1模型总结 引言 在现代的机器学习和深度学习应用中&#xff0c;模型部署和服务化是每个开发者面临的重要任务。无论是用于智能推荐、自然语言处理还是图像识别&#xff0c;如何高效、稳定地将深度学习模型部署到…

央行发布《贸易金融分布式账本技术要求》,参考架构包括5部分

《银行科技研究社》(作者 木子剑):2024年12月11日,中国人民银行发布金融行业标准《贸易金融分布式账本技术要求》(JR/T 0308-2024)(以下简称“《要求》”),当日实施。据悉,该文件的起草单位包括6大行和多家股份制银行等。 《要求》规定了分布式账本技术在贸易金融领域…

Python aiortc API

本研究的主要目的是基于Python aiortc api实现抓取本地设备媒体流&#xff08;摄像机、麦克风&#xff09;并与Web端实现P2P通话。本文章仅仅描述实现思路&#xff0c;索要源码请私信我。 1 demo-server解耦 1.1 原始代码解析 1.1.1 http服务器端 import argparse import …

记录 | WPF基础学习Style局部和全局调用

目录 前言一、Style1.1 例子1.2 为样式起名字1.3 BasedOn 继承上一个样式 二、外部StyleStep1 创建资源字典BaseButtonStyle.xamlStep2 在资源字典中写入StyleStep3 App.xaml中写引用路径【全局】Step4 调用三、代码提供四、x:Key和x:Name区别 更新时间 前言 参考文章&#xff…

吴恩达深度学习——卷积神经网络实例分析

内容来自https://www.bilibili.com/video/BV1FT4y1E74V&#xff0c;仅为本人学习所用。 文章目录 LeNet-5AlexNetVGG-16ResNets残差块 1*1卷积 LeNet-5 输入层&#xff1a;输入为一张尺寸是 32 32 1 32321 32321的图像&#xff0c;其中 32 32 3232 3232是图像的长和宽&…

【Uniapp-Vue3】z-paging插件组件实现触底和下拉加载数据

一、下载z-paing插件 注意下载下载量最多的这个 进入Hbuilder以后点击“确定” 插件的官方文档地址&#xff1a; https://z-paging.zxlee.cn 二、z-paging插件的使用 在文档中向下滑动&#xff0c;会有使用方法。 使用z-paging标签将所有的内容包起来 配置标签中的属性 在s…

【B站保姆级视频教程:Jetson配置YOLOv11环境(七)Ultralytics YOLOv11配置】

Jetson配置YOLOv11环境&#xff08;7&#xff09;Ultralytics YOLOv11环境配置 文章目录 1. 下载YOLOv11 github项目2. 安装ultralytics包3. 验证ultralytics安装3.1 下载yolo11n.pt权重文件3.2 推理 1. 下载YOLOv11 github项目 创建一个目录&#xff0c;用于存放YOLOv11的项目…

第二天:系统从BIOS/UEFI到GRUB/bootloader的启动过程

目录 **一、BIOS/UEFI初始化阶段****二、引导加载程序&#xff08;GRUB&#xff09;的启动过程****1. BIOS模式下的GRUB分阶段加载****2. UEFI模式下的GRUB加载** **三、操作系统内核加载与初始化****四、关键组件与配置文件****五、故障排查与恢复****总结**常见问题如何在UEF…