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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值