unity 系统架构 unity的ui管理框架_UI


UI分为三个类型,正常显示,显示后隐藏其他所有UI,和反向切换,我用模板设计出这三个类型接口,将公共的显示与隐藏,释放,初始化,更新等公共接口抽象于父类IUserInterface,UISystem是管理容器,对所有生成的UIFrm进行资源管理,UITool提供帮助方法,UIMaskMgr控制弹窗的遮罩。UIFactory是创建UIFrm工厂,通常都是创建出来建造然后交给System管理。

可扩展,若有不同类型的UIFrm,可继承IUserInterface重写逻辑,避免修改其他类。

内存释放,交给UISystem管理类来单独释放某个UIFrm的资源或者全部释放,释放某个UIFrm的时候会销毁3d成像和代码引用。同时强转垃圾回收。

使用:利用泛型创建panel,同时传递动态参数,将显示与UI界面分离,即UI界面不挂任何一个脚本,UISystem内控制显示Panel的更新,可以自己改写一个游戏循环框架,配合UI。

文章目录

  • IUserInterface
  • INormalUserInterface
  • IHideOtherUserInterface
  • IReverseChangeUserInterface
  • UIMaskMgr
  • UIFactory
  • UISystem
  • UITool
  • CanvasScript
  • 结果展示
  • 使用


IUserInterface

using ABFW;
using System.Dynamic;
using UnityEngine;

namespace UIFW
{
    /// <summary>
    /// UI界面
    /// 模板设计模式:接口控制流程
    /// </summary>
    public abstract class IUserInterface
    {
        public string ResName; //资源名称
        protected UISystem _UISystem; //UIContext
        protected Vector3 _SpawnPosition = Vector3.zero; //生成位置
        //3D成像
        protected GameObject _GameObject;
        public IUserInterface()
        {

        }
        /// <summary>
        /// UI界面初始化
        /// </summary>
        public virtual void Initialize()
        {
        }
        /// <summary>
        /// UI逻辑更新
        /// </summary>
        public virtual void Update()
        {

        }
        /// <summary>
        /// UI释放
        /// </summary>
        public virtual void Release()
        {
            //删除3D成像
            GameObject.Destroy(_GameObject);
            //释放prefab的内存镜像资源。
            GameManager.Instance.ReleaseResource(ResName);
        }
        /// <summary>
        /// 设置3D成像
        /// </summary>
        /// <param name="_gameobject"></param>
        public virtual void SetObject(GameObject _gameObject)
        {
            _GameObject = _gameObject;
        }
        /// <summary>
        /// 设置管理上下文
        /// </summary>
        /// <param name="uiSystem"></param>
        public void AddContext(UISystem uiSystem)
        {
            _UISystem = uiSystem;
        }
        /// <summary>
        /// 窗体的显示
        /// </summary>
        public virtual void Display()
        {
            _GameObject.SetActive(true);
        }
        /// <summary>
        /// 窗体的隐藏
        /// </summary>
        public virtual void Hide()
        {
            _GameObject.SetActive(false);
        }
        /// <summary>
        /// 窗体的打开
        /// </summary>
        public abstract void Open();
        /// <summary>
        /// 窗体的关闭
        /// </summary>
        public abstract void Close();
        /// <summary>
        /// 窗体是否打开
        /// </summary>
        /// <returns></returns>
        public virtual bool IsOpen()
        {
            return _GameObject.activeSelf;
        }
    }
}

INormalUserInterface

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;

namespace UIFW
{
    public class INormalUserInterface : IUserInterface
    {
        /// <summary>
        /// Normal的父节点:可能为Fixed或者Normal
        /// </summary>
        protected Transform TraParent;
        /// <summary>
        /// 设置3D成像
        /// </summary>
        /// <param name="_gameObject"></param>
        public override void SetObject(GameObject _gameObject)
        {
            base.SetObject(_gameObject);
            _GameObject.transform.SetParent(TraParent, false);
        }
        public override void Close()
        {
            Hide();
            if (base._UISystem.DicCurShowUIFrm.ContainsKey(ResName))
                _UISystem.DicCurShowUIFrm.Remove(ResName);
        }

        public override void Open()
        {
            Display();
            if (!base._UISystem.DicCurShowUIFrm.ContainsKey(ResName))
            {
                _UISystem.DicCurShowUIFrm[ResName] = this;
            }
        }
        public override void Release()
        {
            if (base._UISystem.DicCurShowUIFrm.ContainsKey(ResName))
                _UISystem.DicCurShowUIFrm.Remove(ResName);
            base.Release();
        }
    }
}

IHideOtherUserInterface

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UIFW;
using UnityEngine;

namespace UIFW
{
    public class IHideOtherUserInterface : IUserInterface
    {
        /// <summary>
        /// 设置3D成像
        /// </summary>
        /// <param name="_gameObject"></param>
        public override void SetObject(GameObject _gameObject)
        {
            base.SetObject(_gameObject);
            _GameObject.transform.SetParent(_UISystem.TraNormal, false);
        }
        public override void Close()
        {
            IUserInterface ui;
            Hide();
            if (_UISystem.DicCurShowUIFrm.ContainsKey(ResName))
                _UISystem.DicCurShowUIFrm.Remove(ResName);
            //显示Normal
            foreach (var item in _UISystem.DicCurShowUIFrm)
            {
                item.Value.Display();
            }
            //显示PopUp
            ui = _UISystem.StackUIFrm.Peek();
            ui.Display();
        }

        public override void Open()
        {
            IUserInterface ui;
            //隐藏Normal
            foreach (var item in _UISystem.DicCurShowUIFrm)
            {
                item.Value.Hide();
            }
            //隐藏PopUp
            ui = _UISystem.StackUIFrm.Peek();
            ui.Hide();
            //显示自身
            Display();
            if (!_UISystem.DicCurShowUIFrm.ContainsKey(ResName))
                _UISystem.DicCurShowUIFrm.Add(ResName, this);
        }

        public override void Release()
        {
            if (!_UISystem.DicCurShowUIFrm.ContainsKey(ResName))
                _UISystem.DicCurShowUIFrm.Remove(ResName);
            base.Release();
        }
    }
}

IReverseChangeUserInterface

using System.Collections.Generic;
using System.Linq;
using UnityEngine;

namespace UIFW
{
    public class IReverseChangeUserInterface : IUserInterface
    {
        /// <summary>
        /// 设置3D成像
        /// </summary>
        /// <param name="_gameObject"></param>
        public override void SetObject(GameObject _gameObject)
        {
            base.SetObject(_gameObject);
            _GameObject.transform.SetParent(_UISystem.TraPopUp, false);
        }
        /// <summary>
        /// 关闭
        /// </summary>
        public override void Close()
        {
            if (!IsOpen())
            {
                Debug.LogWarning($"这个窗体还没有打开,不需要关闭!{this.GetType().Name}");
                return;
            }
            //关闭遮罩
            _UISystem.UIMaskMgr.CancelMaskWindow();
            //清除栈顶元素
            IUserInterface userInterface = base._UISystem.StackUIFrm.Pop();
            if (userInterface != this)
            {
                Debug.LogError("当前PopUp不是栈顶元素无法关闭!Close");
                return;
            }
            userInterface.Hide();
            //重绘栈顶元素
            if (base._UISystem.StackUIFrm.Count > 0)
            {
                userInterface = base._UISystem.StackUIFrm.Peek();
                userInterface.Display();
            }
        }
        /// <summary>
        /// 打开
        /// </summary>
        public override void Open()
        {
            IUserInterface ui;
            //需要显示的场景
            if (_UISystem.StackUIFrm.Count() > 0)
            {
                ui = _UISystem.StackUIFrm.Peek();
                ui.Hide();
            }
            else
            {
                //打开遮罩
                _UISystem.UIMaskMgr.SetMaskWindow(_GameObject);
            }
            //把栈顶元素隐藏
            if (_UISystem.StackUIFrm.Contains(this))
            {
                Stack<IUserInterface> tmpS = new Stack<IUserInterface>();
                while (this != _UISystem.StackUIFrm.Peek())
                {
                    tmpS.Push(_UISystem.StackUIFrm.Pop());
                }
                //找到自身
                _UISystem.StackUIFrm.Pop();
                //把tmps中的元素重新加会stack中
                while (tmpS.Count() > 0)
                {
                    ui = tmpS.Pop();
                    _UISystem.StackUIFrm.Push(ui);
                }
                //把自身换到最顶层
                _UISystem.StackUIFrm.Push(this);
            }
            else
            {
                //加入栈顶元素
                _UISystem.StackUIFrm.Push(this);
            }
            //显示自身元素
            Display();
        }

        public override void Release()
        {
            //删除缓存中元素
            if (base._UISystem.StackUIFrm.Contains(this))
            {
                Stack<IUserInterface> tmps = new Stack<IUserInterface>();
                while (base._UISystem.StackUIFrm.Peek() != this)
                {
                    tmps.Push(base._UISystem.StackUIFrm.Pop());
                }
                base._UISystem.StackUIFrm.Pop();
                while (tmps.Count() > 0)
                {
                    _UISystem.StackUIFrm.Push(tmps.Pop());
                }
            }
            base.Release();
        }
    }
}

UIMaskMgr

using System;
using System.Collections.Generic;
using System.Text;
using UIFW;
using UnityEngine;
using UnityEngine.UI;

/***
*	Title:
        UI遮罩管理器
*	Description:
*	    控制启用遮罩和关闭遮罩的功能
*	    
*/
namespace UIFW
{
    public class UIMaskMgr
    {
        /// <summary>
        /// 节点
        /// </summary>
        GameObject UIMask; //遮罩的物体

        public UIMaskMgr(GameObject uiMask)
        {
            //获得UIMask
            UIMask = uiMask;
        }
        public void SetMaskWindow(GameObject UIForm)
        {
            UIMask.SetActive(true);
            //PopUp UIForm下移
            UIForm.transform.SetAsLastSibling();
        }
        public void CancelMaskWindow()
        {
            //隐藏UiMask
            if (UIMask.activeInHierarchy)
                UIMask.SetActive(false);
        }
    }
}

UIFactory

using ABFW;
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;

namespace UIFW
{
    /// <summary>
    /// 创建UI Panel对象
    /// </summary>
    public class UIFactory
    {
        private UISystem _UISystem;
        public UIFactory(UISystem uisystem)
        {
            this._UISystem = uisystem;
        }
        /// <summary>
        /// 创建一个UI窗体
        /// </summary>
        /// <typeparam name="T">UI窗体类型</typeparam>
        /// <param name="obj">Model</param>
        /// <returns></returns>
        public IUserInterface GetUIPanel<T>() where T:IUserInterface,new()
        {
            T panel = new T();
            //建造3D成像
            GameObject prefab = GameManager.Instance.Asset.LoadAsset<GameObject>(panel.ResName);
            Debug.Log($"加载UI Prefab={prefab.name}");
            //设置上下文
            panel.AddContext(_UISystem);
            //实例化
            GameObject RootUI = GameObject.Instantiate(prefab);
            panel.SetObject(RootUI);
            //新UI的初始化
            panel.Initialize();
            return panel;
        }
    }
}

UISystem

using ABFW;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UIFW;
using UnityEngine;

namespace UIFW
{
    /// <summary>
    /// UI框架管理容器
    /// 管理所有已加载UIFrm
    /// </summary>
    public class UISystem
    {
        private static UISystem _Instance;
        public static UISystem Instance
        {
            get
            {
                if (_Instance == null)
                    _Instance = new UISystem();
                return _Instance;
            }
        }
        //工厂
        private UIFactory _Factory;
        //Canvas
        public Transform TraCanvas;
        //Normal
        public Transform TraNormal;
        //Fixed
        public Transform TraFixed;
        //PopUp
        public Transform TraPopUp;
        //UIMask
        public UIMaskMgr UIMaskMgr;
        //是否处于更新UI逻辑之中
        public bool IsUpdate;
        //所有UIFrm
        public Dictionary<string, IUserInterface> DicAllUIFrm;
        //用于添加新的UIFrm,确保在UI逻辑更新完成时添加进DicAllUIFrm中
        public Dictionary<string, IUserInterface> DicAllUIFrmTmp;
        //用于删除UIFrm的缓存容器
        public Dictionary<string, IUserInterface> DicDelUIFrmTmp;
        //当前显示在游戏中的UIFrm
        public Dictionary<string, IUserInterface> DicCurShowUIFrm;
        //堆栈中的UIFrm
        public Stack<IUserInterface> StackUIFrm;

        public UISystem()
        {
            //加载Canvas
            TraCanvas = LoadCanvas();
            Debug.Log("获得TraCanvas" + TraCanvas.name);
            //获得Normal
            TraNormal = UITool.FindChildNode(TraCanvas, "Normal");
            Debug.Log("获得TraNormal" + TraNormal.name);
            //获得Fixed
            TraFixed = UITool.FindChildNode(TraCanvas, "Fixed");
            Debug.Log("获得TraFixed" + TraFixed.name);
            //获得PopUp
            TraPopUp = UITool.FindChildNode(TraCanvas, "PopUp");
            Debug.Log("获得TraPopUp" + TraPopUp.name);
            //获得UIMask
            GameObject maskObj = UITool.FindChildNode(TraCanvas, "UIMask").gameObject;
            UIMaskMgr = new UIMaskMgr(maskObj);
            //获得工厂 
            _Factory = new UIFactory(this);

            //集合分配内存空间
            IsUpdate = false;
            DicAllUIFrm = new Dictionary<string, IUserInterface>();
            DicAllUIFrmTmp = new Dictionary<string, IUserInterface>();
            DicDelUIFrmTmp = new Dictionary<string, IUserInterface>();
            DicCurShowUIFrm = new Dictionary<string, IUserInterface>();
            StackUIFrm = new Stack<IUserInterface>();
        }
        /// <summary>
        /// 加载Canvas
        /// </summary>
        /// <returns></returns>
        public Transform LoadCanvas()
        {
            GameObject prefab = Asset.Instance.LoadAsset<GameObject>("Canvas");
            GameObject go = GameObject.Instantiate(prefab);
            Asset.Instance.ReleaseResources("Canvas");
            return go.transform;
        }
        /// <summary>
        /// 打开一个UI窗体
        /// </summary>
        /// <param name="uiFrmName"></param>
        /// <returns></returns>
        public IUserInterface OpenUIFrm<T>() where T:IUserInterface,new()
        {
            IUserInterface userInterface = null;
            //key
            string key = typeof(T).Name;
            Debug.Log("开启:"+key);
            //从缓存中查询
            if(DicAllUIFrm.ContainsKey(key))
            {
                userInterface = DicAllUIFrm[key];
                //UI资源已经加载在游戏中,只需要显示即可
                if(userInterface.IsOpen())
                {
                    Debug.LogWarning($"UI已经处于显示中...{key}");
                }
                else
                    userInterface.Open();
                Debug.Log("UI项目已经加载在游戏中了");
                return userInterface;
            }
            //获得Panel
            userInterface = _Factory.GetUIPanel<T>();
            //开启UIPanel
            userInterface.Open();
            //加入缓存
            if (!IsUpdate)
                DicAllUIFrm[key] = userInterface;
            else
                DicAllUIFrmTmp[key] = userInterface;
            return userInterface;
        }
        /// <summary>
        /// 关闭UIFrm
        /// </summary>
        /// <param name="userInterface"></param>
        public void CloseUIFrm<T>()
        {
            string key = typeof(T).Name;
            IUserInterface userInterface = null;
            Debug.Log($"关闭:{key}");

            if (DicAllUIFrm.ContainsKey(key))
                userInterface = DicAllUIFrm[key];
            else if (DicAllUIFrmTmp.ContainsKey(key))
                userInterface = DicAllUIFrmTmp[key];

            if(userInterface==null)
            {
                Debug.LogWarning($"这个资源并没有开启,未加载进缓存中,不需要关闭{key}");
                return;
            }

            if (!userInterface.IsOpen())
            {
                Debug.LogWarning("这个资源并没有开启,不需要关闭");
                return;
            }
            userInterface.Close();
        }
        /// <summary>
        /// 卸载某个指定的UIFrm
        /// </summary>
        /// <param name="userInterface"></param>
        public void ReleaseUIFrm<T>()
        {
            string key = typeof(T).Name;
            Debug.Log("释放:" + key);
            IUserInterface userInterface = null;

            if (DicAllUIFrm.ContainsKey(key))
            {
                userInterface = DicAllUIFrm[key];
                //加入删除缓存
                DicDelUIFrmTmp.Add(key, userInterface);
            }
            else if (DicAllUIFrmTmp.ContainsKey(key))
            {
                userInterface = DicAllUIFrmTmp[key];
                DicAllUIFrmTmp.Remove(key);
            }
            if (userInterface == null)
            {
                Debug.LogWarning($"这个资源未加载进缓存中,不需要释放{key}");
                return;
            }
            userInterface.Close();
            userInterface.Release();
        }
        /// <summary>
        /// 更新界面UI逻辑
        /// </summary>
        public void Update()
        {
            //以当前场景下还存在显示的UI物体来判断需要显示的UI,不能根据字典DicAllUIFrm来更新UI,字典会动态变化
            //更新场景下的UI
            IsUpdate = true;
            foreach (var kv in DicAllUIFrm)
            {
                if (kv.Value.IsOpen())
                    kv.Value.Update();
            }
            IsUpdate = false;
            //检查挂起容器内是否有新加入的UI,添加到当前UI中
            foreach (var kv in DicAllUIFrmTmp)
            {
                DicAllUIFrm.Add(kv.Key, kv.Value);
            }
            DicAllUIFrmTmp.Clear();
            //检查删除容器
            foreach (var kv in DicDelUIFrmTmp)
            {
                DicAllUIFrm.Remove(kv.Key);
            }
            DicDelUIFrmTmp.Clear();
        }
        /// <summary>
        /// 卸载全部资源
        /// </summary>
        public void ReleaseAll()
        {
            //清除场景引用
            foreach (var item in DicAllUIFrm)
            {
                DicDelUIFrmTmp.Add(item.Key, item.Value);
                item.Value.Release();
            }
            foreach (var item in DicAllUIFrmTmp)
            {
                item.Value.Release();
            }
            DicAllUIFrmTmp.Clear();
            //清除Canvas
            GameObject.Destroy(TraCanvas.gameObject);
            //清除代码引用
            DicAllUIFrmTmp.Clear();
            TraCanvas = null;
            TraNormal = null;
            TraFixed = null;
            TraPopUp = null;   
            //资源回收
            Resources.UnloadUnusedAssets();
        }
    }
}

UITool

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;

namespace UIFW
{
    /// <summary>
    /// 工具人
    /// </summary>
    public class UITool
    {
        /// <summary>
        /// 根据父节点和子节点名称来查找到子节点
        /// </summary>
        /// <param name="parent">父节点</param>
        /// <param name="childName">子节点名字</param>
        /// <returns></returns>
        public static Transform FindChildNode(Transform parent, string childName)
        {
            Transform target = null;
            target = parent.Find(childName);
            if (target == null)
            {
                foreach (Transform childTran in parent)
                {
                    if (childTran != parent)
                    {
                        target = FindChildNode(childTran, childName);
                        if (target != null) //这一句很关键,如果搜索出来的target等于null了,返回了就不会继续搜索了
                            return target;
                    }
                }
            }
            return target;
        }
        /// <summary>
        /// 根据父节点和子节点名字来返回T类型的脚本组件
        /// Button
        /// </summary>
        /// <typeparam name="T">需要返回的脚本类型</typeparam>
        /// <param name="parent">父节点</param>
        /// <param name="chlldName">子节点名字</param>
        /// <returns></returns>
        public static T GetComponentFromChildNode<T>(Transform parent, string childName) where T : Component
        {
            T res = null;
            Transform childTran = FindChildNode(parent, childName);
            if (childTran != null)
            {
                res = childTran.GetComponent<T>();
            }
            return res;
        }
    }
}

CanvasScript

unity 系统架构 unity的ui管理框架_unity 系统架构_02

结果展示

unity 系统架构 unity的ui管理框架_ide_03


关闭PopUp会重绘栈顶元素,关闭HideOther会重绘其他正常显示的元素。使用了我前面自己写的Assetbundle框架来加载元素。

使用

根据泛型来Open Panel同时可以传递动态Model参数供给View初始化的时候使用。

unity 系统架构 unity的ui管理框架_ide_04


unity 系统架构 unity的ui管理框架_ide_05


不同类型的UIFrm分别继承不同的接口即可,按需求更改3D成像的Prefab和显示位置和父亲结点。有需要传递额外模型数据的话,这样。

unity 系统架构 unity的ui管理框架_System_06