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(画布)