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.pswp.cn/news/894850.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/894850.shtml
英文地址,請注明出處:http://en.pswp.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;和邊緣計算等技術的發展奠定了基礎。…

云計算——AWS Solutions Architect – Associate(saa)1、什么是云,AWS介紹

什么是云? 什么是云? 云計算(cloud computing)是基于互聯網的相關服務的增加、使用和交付模式&#xff0c;通常涉及通過互聯網來提供動態易護展且經常是虛擬化的資源。云是網絡、互聯網的一種比喻說法。 簡單理解為&#xff1a;云是 共享資源&#xff0c;按需付費&#xff0…

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…

TCP/IP 郵件

TCP/IP 郵件 引言 在互聯網技術飛速發展的今天,電子郵件(Email)已成為人們日常工作和生活中不可或缺的通信工具。TCP/IP協議作為互聯網通信的基礎,為電子郵件的傳輸提供了強大的技術支持。本文將詳細介紹TCP/IP在電子郵件傳輸過程中的作用,以及相關的協議和實現方式。 …

離線安裝Appium Server

1、問題概述? 安裝Appium通常有兩種方式: 第一種:下載exe安裝包,這種是Appium Server GUI安裝方式,缺點是通過命令啟動不方便。 第二種:通過cmd安裝appium server,可以通過命令方式啟動,比較方便。 問題:在沒有外網的情況下,無法通過命令在cmd中安裝appium server…

設計模式六大原則和單例模式

設計模式 目的 實現可重用解決方案&#xff0c;構筑易維護、可擴展的軟件系統。 六大原則 單一職責&#xff1a; 類的職責單一&#xff0c;一個方法做一件事。 開閉原則&#xff1a; 拓展開放&#xff0c;修改關閉。 里氏替換&#xff1a; 父類能出現的地方&#xff0c;子…

淺嘗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…

深入淺出 NRM:加速你的 npm 包管理之旅

文章目錄 前言一、NRM 是什么&#xff1f;二、為什么需要 NRM&#xff1f;三、NRM 的優勢四、NRM 的安裝與使用4.1 安裝 NRM4.2 查看可用的 npm 源4.3 切換 npm 源4.4 測試 npm 源速度4.5 添加自定義 npm 源4.6 刪除 npm 源 五、NRM 的進階使用六、總結 前言 作為一名 JavaScr…

《C#之集訓1-20121019c#基礎》

&#xfeff;&#xfeff; C#是微軟公司發布的一種面向對象的、運行于.NET Framework之上的高級程序設計語言。它是微軟公司研究員Anders Hejlsberg的最新成果。 C#曾經的它在我眼中是很高大上的&#xff0c;一直沒有目睹其風采&#xff0c;現在終于揭開了它神秘的面紗&#xf…

紅包雨項目前端部分

創建項目 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;當輸出為低電平時點亮。其中…

深入剖析 HTML5 新特性:語義化標簽和表單控件完全指南

系列文章目錄 01-從零開始學 HTML&#xff1a;構建網頁的基本框架與技巧 02-HTML常見文本標簽解析&#xff1a;從基礎到進階的全面指南 03-HTML從入門到精通&#xff1a;鏈接與圖像標簽全解析 04-HTML 列表標簽全解析&#xff1a;無序與有序列表的深度應用 05-HTML表格標簽全面…

[Java基礎]函數式編程

Lambda函數 JDK8新增的語法形式, 使用Lambda函數替代某些匿名內部類對象&#xff0c;從而讓程序代碼更簡潔&#xff0c;可讀性更好。 基本使用 lambda表達式只能簡化函數式接口的匿名內部類寫法 // 1.定義抽象類 abstract class Animal {public abstract void crt(); }publi…

Vue通過觸發與監聽事件進行數據傳遞: 子組件調用 $emit 方法來將數據傳遞給父組件。

文章目錄 引言I 組件事件事件參數defineEmits 宏聲明需要拋出的事件事件校驗例子:子組件告訴父組件放大所有博客文章的文字II 【詳細說明】 子組件通過觸發一個事件,將數據傳遞給父組件調用內建的 `$emit `方法傳入事件名稱來觸發一個事件子組件通過`this.$emit`來觸發一個事…

Vim 多窗口編輯及文件對比

水平分割 :split 默認使用水平分割的方式。 :split :sp 垂直分割 :vsplit :vs 帶文件的分割 :split 文件名 :sp 文件名 在光標所在的窗口&#xff0c;輸入分割窗口命令就會對那個窗口進行分割。 切換窗口 Ctrlw 切換正在編輯的窗口 快速分割窗口 Ctrlwn 快速分割當前…

“衛星-無人機-地面”遙感數據快速使用及地物含量計算的實現方法

在與上千學員交流過程中&#xff0c;發現科研、生產和應用多源遙感數據時&#xff0c;能快速上手&#xff0c;發揮數據的時效性&#xff0c;盡快出創新性成果&#xff0c;是目前的學員最迫切的需求。特別是按照“遙感數據獲取-處理-分析-計算-制圖”全流程的答疑解惑&#xff0…