Eevee框架13——资源整合
到目前为止框架和轮子基本完成,本篇来整合一下目前已有的框架、工具。
首先是重申一下代码规范,在整理过程中按照代码规范修改:https://www.jianshu.com/p/dd303f59027a
首先整合了Singleton和MonoSingleton,作为单例实例化基类,所有只需要一个对象的非静态类均需继承这俩。
namespace EeveeFramework
{
/// <summary>
/// 单例基类
/// </summary>
/// <typeparam name="T">T</typeparam>
public abstract class Singleton<T> where T : Singleton<T>, new()
{
//实例对象
protected static T m_Instance = null;
/// <summary>
/// 构造函数
/// </summary>
protected Singleton()
{
}
/// <summary>
/// 实例
/// </summary>
public static T Instance
{
get
{
if (m_Instance == null)
{
m_Instance = new T();
}
return m_Instance;
}
}
}
}
namespace EeveeFramework
{
using UnityEngine;
/// <summary>
/// mono单例
/// </summary>
/// <typeparam name="T">T</typeparam>
public abstract class MonoSingleton<T> : MonoBehaviour where T : MonoBehaviour
{
//实例对象
private static T m_Instance = null;
/// <summary>
/// 实例
/// </summary>
public static T Instance
{
get
{
if (null == m_Instance)
{
//FindObjectOfType可与Awake中的赋值替换即可不需要重写Awake函数,但性能略差
//m_Instance = FindObjectOfType(typeof(T)) as T;
if (m_Instance == null) m_Instance = new GameObject("Single of " + typeof(T).ToString(), typeof(T)).GetComponent<T>();
}
return m_Instance;
}
}
protected virtual void Awake()
{
if (m_Instance == null) m_Instance = this as T;
}
}
}
对象池基本用的不多,丢弃,暂不整合。
事件系统EventCenter,规范一下,直接拿过来~后续在MVC之类架构设计时,使用此事件系统为基础,可实现解耦。
namespace EeveeFramework
{
using System;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 定义一个空接口,作为EventInfo的基类(使用基类存储子类),则字典中存储的为EventInfo类型
/// </summary>
public interface IEventInfo { }
/// <summary>
/// 将Action转化为泛型,包裹在EventInfo,则字典中存储的为EventInfo类型
/// </summary>
/// <typeparam name="T">T</typeparam>
public class EventInfo<T> : IEventInfo
{
public Action<T> m_Actions;
public EventInfo(Action<T> action)
{
m_Actions += action;
}
}
/// <summary>
/// 用于不需要指定泛型的事件使用
/// </summary>
public class EventInfo : IEventInfo
{
public Action m_Actions;
public EventInfo(Action action)
{
m_Actions += action;
}
}
/// <summary>
/// 事件中心
/// </summary>
public class EventCenter : Singleton<EventCenter>
{
//用于储存所有事件的Dictionary
private Dictionary<string, IEventInfo> m_EventDictionary = new Dictionary<string, IEventInfo>();
/// <summary>
/// 添加监听事件
/// </summary>
/// <param name="name">事件名</param>
/// <param name="action">需添加的委托</param>
public void AddEventListener(string name, Action action)
{
if (m_EventDictionary.ContainsKey(name))
{
if (null == (m_EventDictionary[name] as EventInfo))
Debug.LogError("添加了不同参数的委托");
else
(m_EventDictionary[name] as EventInfo).m_Actions += action;
}
else
m_EventDictionary.Add(name, new EventInfo(action));
}
/// <summary>
/// 添加监听事件(有参)
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="name">事件名</param>
/// <param name="action">需添加的委托</param>
public void AddEventListener<T>(string name, Action<T> action)
{
if (m_EventDictionary.ContainsKey(name))
{
if (null == (m_EventDictionary[name] as EventInfo<T>))
Debug.LogError("添加了不同参数的委托");
else
(m_EventDictionary[name] as EventInfo<T>).m_Actions += action;
}
else
m_EventDictionary.Add(name, new EventInfo<T>(action));
}
/// <summary>
/// 移除监听
/// </summary>
/// <param name="name">事件名</param>
/// <param name="action">需移除的委托</param>
public void RemoveEventListener(string name, Action action)
{
if (m_EventDictionary.ContainsKey(name))
{
(m_EventDictionary[name] as EventInfo).m_Actions -= action;
if (null == m_EventDictionary[name])
m_EventDictionary.Remove(name);
}
}
/// <summary>
/// 移除监听(有参)
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="name">事件名</param>
/// <param name="action">需移除的委托</param>
public void RemoveEventListener<T>(string name, Action<T> action)
{
if (m_EventDictionary.ContainsKey(name))
{
(m_EventDictionary[name] as EventInfo<T>).m_Actions -= action;
if (null == m_EventDictionary[name])
m_EventDictionary.Remove(name);
}
}
/// <summary>
/// 触发事件
/// </summary>
/// <param name="name">事件名</param>
public void EventTrigger(string name)
{
if (null == (m_EventDictionary[name] as EventInfo))
{
Debug.LogError("调用了不同参数的委托");
}
else if ((m_EventDictionary[name] as EventInfo).m_Actions != null)
(m_EventDictionary[name] as EventInfo).m_Actions.Invoke();
}
/// <summary>
/// 触发事件(有参)
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="name">事件名</param>
/// <param name="info">事件传参</param>
public void EventTrigger<T>(string name, T info)
{
if (null == (m_EventDictionary[name] as EventInfo<T>))
{
Debug.LogError("调用了不同参数的委托");
}
else if ((m_EventDictionary[name] as EventInfo<T>).m_Actions != null)
(m_EventDictionary[name] as EventInfo<T>).m_Actions.Invoke(info);
}
/// <summary>
/// 清空所有事件
/// </summary>
public void Clear()
{
m_EventDictionary.Clear();
}
/// <summary>
/// 删除事件及其所有监听
/// </summary>
/// <param name="name">事件名</param>
public void DeleteEvent(string name)
{
if (m_EventDictionary.ContainsKey(name))
{
m_EventDictionary.Remove(name);
}
}
}
}
mono工具,虽然更像工具类,但是架构中有很多需要调用Mono对象来实现协程,因此MonoUtil先整理过来。其他工具我们在架构整理完成后再整合,工具和架构区分开,需要的时候取用。
namespace EeveeFramework
{
using System;
using System.Collections;
using UnityEngine;
/// <summary>
/// Mono工具类
/// </summary>
public class MonoUtil : MonoSingleton<MonoUtil>
{
/// <summary>
/// 创建委托事件
/// </summary>
private event Action m_UpdateAction;
void Start()
{
//不让该对象移除
DontDestroyOnLoad(this.gameObject);
}
void Update()
{
if (m_UpdateAction != null)
m_UpdateAction();
}
/// <summary>
/// 添加帧更新监听
/// </summary>
/// <param name="func">事件</param>
public void AddUpdateListener(Action action)
{
m_UpdateAction += action;
}
/// <summary>
/// 移除帧更新监听
/// </summary>
/// <param name="func">事件</param>
public void RemoveUpdateListener(Action action)
{
m_UpdateAction -= action;
}
/// <summary>
/// 延迟几秒执行
/// </summary>
/// <param name="seconds">秒数</param>
/// <param name="onFinished">回调</param>
public void Delay(float seconds, Action action)
{
StartCoroutine(DelayCoroutine(seconds, action));
}
//延迟几秒回调协程
private static IEnumerator DelayCoroutine(float seconds, Action action)
{
yield return new WaitForSeconds(seconds);
action();
}
}
}
场景切换,其实用的非常少,我们暂时把它归类到工具里,不整合进来。
Resources文件夹加载,在有了Addressables之后这玩意显得苍白无力,连工具都不需要,抛弃掉。
UI框架,之前整理了一个版本,但里面包含了示例,这里单独拿出来,并且作一下规范化:
namespace EeveeFramework
{
using System;
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using Object = UnityEngine.Object;
#region define
/// <summary>
/// UI层级,根据不同层级设置父物体,如果展示层不够可自行扩展,需要创建对应的物体并调节相对位置
/// </summary>
public enum UIHierarchy
{
/// <summary>
/// 背景图
/// </summary>
BackGround,
/// <summary>
/// 主界面
/// </summary>
MainMenu,
/// <summary>
/// 二级界面
/// </summary>
SecondaryMenu,
/// <summary>
/// 三级界面
/// </summary>
ThirdMenu,
/// <summary>
/// 普通弹出框
/// </summary>
Popup,
/// <summary>
/// 提示类弹出框
/// </summary>
AlwaysFront
}
/// <summary>
/// UICollider碰撞遮挡设置
/// </summary>
public enum UICollider
{
/// <summary>
/// 显示该界面不包含碰撞背景
/// </summary>
None,
/// <summary>
/// 碰撞透明背景
/// </summary>
Normal,
/// <summary>
/// 碰撞非透明背景
/// </summary>
WithBg,
}
#endregion
/// <summary>
/// 每个UI界面为一个Page,需设计层级关系但不区分管理,可自行从命名上区分,这里建议按照功能命名区分
/// </summary>
public abstract class UIPage
{
#region 变量
//默认名为空字符串
public string m_Name = string.Empty;
//Page对应的UIRoot
public UIRoot m_UIRoot = default(UIRoot);
//page的id
public int m_id = -1;
//生成UI的类型,根据类型设定父物体
public UIHierarchy m_UIHierarchy = UIHierarchy.MainMenu;
//背景触发类型
public UICollider m_Collider = UICollider.None;
//加载UI的路径
public string m_UIPath = string.Empty;
//UI的物体
public GameObject m_GameObject;
public Transform m_Transform;
//所有Pages的Dictionary
private static Dictionary<string, UIPage> m_AllPages;
public static Dictionary<string, UIPage> AllPages
{ get { return m_AllPages; } }
//记录此ui加载模式,异步或者同步
private bool m_IsAsyncUI = false;
//Page是否打开
protected bool m_IsActived = false;
//刷新Page的数据
private object m_Data = null;
protected object Data { get { return m_Data; } }
//加载ui的委托
public static Func<string, Object> m_DelegateSyncLoadUI = null;
public static Action<string, Action<Object>> m_DelegateAsyncLoadUI = null;
#endregion
#region virtual api
/// <summary>
/// 实例化Page
/// </summary>
/// <param name="go"></param>
public virtual void Awake(GameObject go) { }
/// <summary>
/// 刷新Page
/// </summary>
public virtual void Refresh() { }
/// <summary>
/// 打开该Page
/// </summary>
public virtual void Active()
{
m_IsActived = true;
this.m_GameObject.SetActive(m_IsActived);
}
/// <summary>
/// 隐藏Page,不会清除数据
/// </summary>
public virtual void Hide()
{
this.m_GameObject.SetActive(false);
m_IsActived = false;
//隐藏时将此页的数据设为空
this.m_Data = null;
}
#endregion
#region internal api
/// <summary>
/// 构造函数
/// </summary>
private UIPage() { }
/// <summary>
/// 构造函数
/// </summary>
/// <param name="uIRoot">设置对应的UIRoot</param>
/// <param name="type">设置要放的显示层级</param>
/// <param name="uICollider">设置碰撞</param>
public UIPage(UIRoot uIRoot, UIHierarchy type, UICollider uICollider)
{
if (uIRoot == null)
{
Debug.LogError("UIRoot is null");
return;
}
this.m_UIRoot = uIRoot;
this.m_UIHierarchy = type;
this.m_Collider = uICollider;
this.m_Name = this.GetType().ToString();
//创建时执行Bind
UIBind.Bind();
}
/// <summary>
/// 同步ShowPage
/// </summary>
protected void Show()
{
if (this.m_GameObject == null && string.IsNullOrEmpty(m_UIPath) == false)
{
GameObject go = null;
if (m_DelegateSyncLoadUI != null)
{
Object o = m_DelegateSyncLoadUI(m_UIPath);
go = o != null ? GameObject.Instantiate(o) as GameObject : null;
}
else
{
go = GameObject.Instantiate(Resources.Load(m_UIPath)) as GameObject;
}
if (go == null)
{
Debug.LogError("[UI] Cant sync load your ui prefab.");
return;
}
go.name = go.name.Replace("(Clone)", "");
AnchorUIGameObject(go);
Awake(go);
m_IsAsyncUI = false;
}
AnchorUIGameObject(this.m_GameObject);
Active();
Refresh();
}
/// <summary>
/// 异步ShowPage
/// </summary>
protected void Show(Action callback)
{
MonoUtil.Instance.StartCoroutine(AsyncShow(callback));
}
IEnumerator AsyncShow(Action callback)
{
if (this.m_GameObject == null && string.IsNullOrEmpty(m_UIPath) == false)
{
GameObject go = null;
bool _loading = true;
m_DelegateAsyncLoadUI(m_UIPath, (o) =>
{
go = o != null ? GameObject.Instantiate(o) as GameObject : null;
AnchorUIGameObject(go);
Awake(go);
m_IsAsyncUI = true;
_loading = false;
Active();
Refresh();
if (callback != null) callback();
});
float _t0 = Time.realtimeSinceStartup;
while (_loading)
{
if (Time.realtimeSinceStartup - _t0 >= 10.0f)
{
Debug.LogError("[UI] WTF async load your ui prefab timeout!");
yield break;
}
yield return null;
}
}
else
{
AnchorUIGameObject(this.m_GameObject);
Active();
Refresh();
if (callback != null) callback();
}
}
/// <summary>
/// 设置Page的父级
/// </summary>
/// <param name="ui"></param>
protected void AnchorUIGameObject(GameObject ui)
{
if (m_UIRoot == null)
{
Debug.LogError("UIRoot is null");
return;
}
if (ui == null) return;
this.m_GameObject = ui;
this.m_Transform = ui.transform;
ui.transform.SetParent(null, false);
switch (m_UIHierarchy)
{
case UIHierarchy.BackGround:
ui.transform.SetParent(m_UIRoot.BackGround, false);
break;
case UIHierarchy.MainMenu:
ui.transform.SetParent(m_UIRoot.MainMenu, false);
break;
case UIHierarchy.SecondaryMenu:
ui.transform.SetParent(m_UIRoot.SecondaryMenu, false);
break;
case UIHierarchy.ThirdMenu:
ui.transform.SetParent(m_UIRoot.ThirdMenu, false);
break;
case UIHierarchy.Popup:
ui.transform.SetParent(m_UIRoot.Popup, false);
break;
case UIHierarchy.AlwaysFront:
ui.transform.SetParent(m_UIRoot.AlwaysFront, false);
break;
}
}
/// <summary>
/// ToString
/// </summary>
/// <returns></returns>
public override string ToString()
{
return ">Name:" + m_Name + ",ID:" + m_id + ",Type:" + m_UIHierarchy.ToString() + ",Collider:" + m_Collider.ToString();
}
public bool IsActive => CheckIsActive();
/// <summary>
/// 是否开启
/// </summary>
/// <returns></returns>
private bool CheckIsActive()
{
bool ret = m_GameObject != null && m_GameObject.activeSelf;
return ret || m_IsActived;
}
#endregion
#region static api
#region ShowPage的重载
/// <summary>
/// 显示Page
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="pageData">Page数据</param>
/// <param name="isAsync">是否异步</param>
/// <param name="action">回调</param>
private static void ShowPage<T>(object pageData, bool isAsync, Action action) where T : UIPage, new()
{
Type t = typeof(T);
string pageName = t.ToString();
if (m_AllPages != null && m_AllPages.ContainsKey(pageName))
{
ShowPage(pageName, m_AllPages[pageName], pageData, isAsync, action);
}
else
{
T instance = new T();
ShowPage(pageName, instance, pageData, isAsync, action);
}
}
/// <summary>
/// 打开已实例化过的Page
/// </summary>
/// <param name="pageName">pageName</param>
/// <param name="pageInstance">page对象</param>
/// <param name="pageData">page数据</param>
/// <param name="isAsync">是否异步</param>
/// <param name="action">回调</param>
private static void ShowPage(string pageName, UIPage pageInstance, object pageData, bool isAsync, Action action)
{
if (string.IsNullOrEmpty(pageName) || pageInstance == null)
{
Debug.LogError("[UI] show page error with :" + pageName + " maybe null instance.");
return;
}
if (m_AllPages == null)
{
m_AllPages = new Dictionary<string, UIPage>();
}
UIPage page = null;
if (m_AllPages.ContainsKey(pageName))
{
page = m_AllPages[pageName];
}
else
{
m_AllPages.Add(pageName, pageInstance);
page = pageInstance;
}
page.m_Data = pageData;
if (isAsync)
page.Show(action);
else
page.Show();
}
/// <summary>
/// 同步Show Page
/// </summary>
public static void ShowPage<T>() where T : UIPage, new()
{
ShowPage<T>(null, false, null);
}
/// <summary>
/// 同步Show Page携带Page Data输入
/// </summary>
/// <typeparam name="T">T</typeparam>
/// <param name="pageData">pageData</param>
public static void ShowPage<T>(object pageData) where T : UIPage, new()
{
ShowPage<T>(pageData, false, null);
}
/// <summary>
/// 同步Show Page
/// </summary>
/// <param name="pageName">pageName</param>
/// <param name="pageInstance">实例对象</param>
public static void ShowPage(string pageName, UIPage pageInstance)
{
ShowPage(pageName, pageInstance, null, false, null);
}
/// <summary>
/// 同步Show Page
/// </summary>
/// <param name="pageName">pageName</param>
/// <param name="pageInstance">实例对象</param>
/// <param name="pageData">pageData</param>
public static void ShowPage(string pageName, UIPage pageInstance, object pageData)
{
ShowPage(pageName, pageInstance, pageData, false, null);
}
/// <summary>
/// 异步Show Page
/// </summary>
/// <typeparam name="T">T</typeparam>
/// <param name="action">回调</param>
public static void ShowPage<T>(Action action) where T : UIPage, new()
{
ShowPage<T>(null, true, action);
}
/// <summary>
/// 异步Show Page
/// </summary>
/// <typeparam name="T">T</typeparam>
/// <param name="action">回调</param>
/// <param name="pageData">pageData</param>
public static void ShowPage<T>(object pageData, Action action) where T : UIPage, new()
{
ShowPage<T>(pageData, true, action);
}
/// <summary>
/// 异步Show Page
/// </summary>
/// <param name="pageName">pageName</param>
/// <param name="pageInstance">实例对象</param>
/// <param name="action"></param>
public static void ShowPage(string pageName, UIPage pageInstance, Action action)
{
ShowPage(pageName, pageInstance, null, true, action);
}
/// <summary>
/// 异步Show Page
/// </summary>
/// <param name="pageName">pageName</param>
/// <param name="pageInstance">实例对象</param>
/// <param name="pageData">pageData</param>
/// <param name="action">回调</param>
public static void ShowPage(string pageName, UIPage pageInstance, object pageData, Action action)
{
ShowPage(pageName, pageInstance, pageData, true, action);
}
#endregion
#region ClosePage的重载
/// <summary>
/// 关闭Page
/// </summary>
/// <param name="target">实例对象</param>
public static void ClosePage(UIPage target)
{
if (target == null) return;
target.Hide();
}
/// <summary>
/// 关闭Page
/// </summary>
/// <typeparam name="T">T</typeparam>
public static void ClosePage<T>() where T : UIPage
{
Type t = typeof(T);
string pageName = t.ToString();
if (m_AllPages != null && m_AllPages.ContainsKey(pageName))
{
ClosePage(m_AllPages[pageName]);
}
else
{
Debug.LogError(pageName + "havnt show yet!");
}
}
/// <summary>
/// 关闭Page
/// </summary>
/// <param name="pageName">pageName</param>
public static void ClosePage(string pageName)
{
if (m_AllPages != null && m_AllPages.ContainsKey(pageName))
{
ClosePage(m_AllPages[pageName]);
}
else
{
Debug.LogError(pageName + " havnt show yet!");
}
}
#endregion
#endregion
}
}
namespace EeveeFramework
{
using UnityEngine;
/// <summary>
/// UI Root
/// </summary>
public abstract class UIRoot : MonoBehaviour
{
protected static UIRoot m_Instance = null;
public static UIRoot Instance
{
get
{
return m_Instance;
}
}
[SerializeField]
private Transform m_BackGround;
/// <summary>
/// 背景图
/// </summary>
public Transform BackGround
{
get { return m_BackGround; }
}
[SerializeField]
private Transform m_MainMenu;
/// <summary>
/// 主界面
/// </summary>
public Transform MainMenu
{
get { return m_MainMenu; }
}
[SerializeField]
private Transform m_SecondaryMenu;
/// <summary>
/// 二级界面
/// </summary>
public Transform SecondaryMenu
{
get { return m_SecondaryMenu; }
}
[SerializeField]
private Transform m_ThirdMenu;
/// <summary>
/// 三级界面
/// </summary>
public Transform ThirdMenu
{
get { return m_ThirdMenu; }
}
[SerializeField]
private Transform m_Popup;
/// <summary>
/// 普通弹出框
/// </summary>
public Transform Popup
{
get { return m_Popup; }
}
[SerializeField]
private Transform m_AlwaysFront;
/// <summary>
/// 提示类弹出框
/// </summary>
public Transform AlwaysFront
{
get { return m_AlwaysFront; }
}
[ContextMenu("自动添加绑定关系")]
void SetParameters()
{
m_BackGround = transform.Find("BackGroundRoot背景图");
m_MainMenu = transform.Find("MainMenuRoot主界面");
m_SecondaryMenu = transform.Find("SecondaryMenuRoot二级界面");
m_ThirdMenu = transform.Find("ThirdMenuRoot三级界面");
m_Popup = transform.Find("PopupRoot普通弹出框");
m_AlwaysFront = transform.Find("AlwaysFrontRoot提示类弹出框");
}
void Reset()
{
Debug.Log("脚本自动添加绑定关系");
SetParameters();
}
}
}
namespace EeveeFramework
{
using UnityEngine;
using System.Collections;
/// <summary>
/// 用于扩展自己的UI加载逻辑
/// </summary>
public class UIBind : MonoBehaviour
{
//是否绑定加载逻辑
static bool isBind = false;
/// <summary>
/// 绑定加载逻辑
/// </summary>
public static void Bind()
{
if (!isBind)
{
isBind = true;
//此处演示使用Resources.Load
UIPage.m_DelegateSyncLoadUI = Resources.Load;
}
}
}
}
UIBind的话我们在加入Addressables后再改写一下。
Json处理,因为依赖性很低,我们把他当作工具。
Addressables,由于Addressables是持续更新的,所以我们这里不直接导入,而是根据自己的Unity版本去官方包导入~。
至此,我们的框架基本完成了,以UI框架为主,辅以单例、事件系统,实现了核心的架构,代码量非常少,耦合度也很低~。
框架基础内容
如果项目中用不到资源加载的,可以Addressables都不添加。下一篇我们再去整合工具吧,本篇先整理出来EeveeFrameworkBase版本:
链接: https://pan.baidu.com/s/1z5LXdZo7j_nr5V40Hl2bAQ
提取码: sp8r