ui unity3d 框架 - CSDN
  • Unity3D之搭建简易有效的UI框架

    千次阅读 2018-04-11 13:21:29
    引言:为了小生的三个粉丝,坚持更新。...一、什么是UI框架UI框架用于管理场景中的所有面板,控制面板之间的切换,可以加快开发进度、提高代码质量。二、实现思路根据用户界面调用情况,分析有如下四种状态...
    引言:为了小生的三个粉丝,坚持更新。今日分享的是如何搭建自己的UI框架,从而有效管理用户界面。这里主要讲解实现的思想及主要步骤。在此,感谢Siki老师。
    开发版本:Unity 2017.1.1f1、VS 2017
    适合人群:适合有一定基础的童鞋!

    一、什么是UI框架

    UI框架用于管理场景中的所有面板,控制面板之间的切换,可以加快开发进度、提高代码质量。

    二、实现思路

    根据用户界面调用情况,分析有如下四种状态:
    1. 进入状态:界面第一次被动态加载使用的时候
    2. 暂停状态:切换到其他界面的时候
    3. 继续状态:重新回到界面的时候
    4. 退出状态:界面不显示的时候
    实现步骤:
    1. 使用JSON保存面板路径,枚举保存面板类型
    2. 根据界面共有的四种状态,创建UI基类BasePanel,场景中的界面继承该基类,并将四种状态写成虚方法,依次分别为OnEnter(),OnPause(),OnResume(),OnExit(),提供给子类重写。
    3. 通过管理类UIManager,解析JSON,管理UI界面的加载和切换。为了方便调用,做成单例模式。分别用两个字典保存从JSON读取的面板信息和已动态加载实例化的面板。通过栈来管理场景中所有面板之间的切换。
    缺点也比较明显,因为用栈来存储场景中依次打开的界面,也只能依次从栈顶界面开始关闭。

    页面状态流程图如下所示:

    UI框架类图如下:

    三、代码实现

    UI界面基类
    BasePanel.cs
    public class BasePanel : MonoBehaviour
    {
        /// <summary>
        /// 界面显示出来
        /// </summary>
        public virtual void OnEnter() { }
    
        /// <summary>
        /// 界面暂停(弹出了其他界面)
        /// </summary>
        public virtual void OnPause() { }
    
        /// <summary>
        /// 界面继续(其他界面移除,回复本来的界面交互)
        /// </summary>
        public virtual void OnResume() { }
    
        /// <summary>
        /// 界面不显示,退出这个界面,界面被关闭
        /// </summary>
        public virtual void OnExit() { }
    }

    UIManager.cs

    public class UIManager
    {
        // 单例模式:定义一个静态的对象,构造方法私有化,内部构造,用于外部访问
        private static UIManager _instance;
        public static UIManager Instance
        {
            get
            {
                if (_instance == null)
                {
                    _instance = new UIManager();
                }
                return _instance;
            }
        }
    
        //字典存储所有面板的Prefabs路径
        private Dictionary<UIPanelType, string> panelPathDict = new Dictionary<UIPanelType, string>();
        //保存所有已实例化面板的游戏物体身上的BasePanel组件
        private Dictionary<UIPanelType, BasePanel> panelDict = new Dictionary<UIPanelType, BasePanel>();
        //存储当前场景中的界面
        private Stack<BasePanel> panelStack = new Stack<BasePanel>();
    
        private Transform canvasTransform;
        private Transform CanvasTransform
        {
            get
            {
                if (canvasTransform == null)
                {
                    canvasTransform = GameObject.Find("Canvas").transform;
                }
                return canvasTransform;
            }
        }
    
        private UIManager()
        {
            ParseUIPanelTypeJson();
        }
    
        /// <summary>
        /// 解析JSON,获取所有面板的路径信息
        /// </summary>
        private void ParseUIPanelTypeJson()
        {
            TextAsset ta = Resources.Load<TextAsset>("UIPanelType");
            JsonData jsonDataArray = JsonMapper.ToObject(ta.text);
            foreach (JsonData item in jsonDataArray)
            {
                UIPanelType panelType = (UIPanelType)Enum.Parse(typeof(UIPanelType), item["panelType"].ToString());
                string path = item["path"].ToString();
                panelPathDict.Add(panelType, path);
            }
        }
    
        /// <summary>
        /// 根据面板类型,返回对应的BasePanel组件
        /// </summary>
        /// <param name="panelType">需要返回的面板类型</param>
        /// <returns>返回该面板组件</returns>
        private BasePanel GetPanel(UIPanelType panelType)
        {
            BasePanel basePanel = panelDict.GetValue(panelType);
            //如果panel为空,根据该面板prefab的路径,实例化该面板
            if (basePanel == null)
            {
                string path = panelPathDict.GetValue(panelType);
                GameObject newPanel = GameObject.Instantiate(Resources.Load<GameObject>(path)) as GameObject;
                newPanel.transform.SetParent(CanvasTransform, false); 
                //第一次实例化的面板需要保存在字典中
                panelDict.Add(panelType, newPanel.GetComponent<BasePanel>());
                return newPanel.GetComponent<BasePanel>();
            }
            else
            {
                return basePanel;
            }
        }
    
        /// <summary>
        /// 设置默认的栈顶元素
        /// </summary>
        /// <param name="panelType">界面类型</param>
        /// <param name="basePanel">组件</param>
        public void SetDefaultPopPanel(UIPanelType panelType,BasePanel basePanel)  
        {
            panelDict.Add(panelType, basePanel);
            panelStack.Push(basePanel);
        }
    
        /// <summary>
        /// 把该页面显示在场景中
        /// </summary>
        /// <param name="panelType">需要显示界面的类型</param>
        public void PushPanel(UIPanelType panelType)
        {
            //判断一下栈里面是否有页面
            if (panelStack.Count > 0)
            {
                panelStack.Peek().OnPause();//原栈顶界面暂停
            }
            BasePanel panel = GetPanel(panelType);
            panel.OnEnter();//调用进入动作
            panelStack.Push(panel);//页面入栈
        }
    
        /// <summary>
        /// 关闭栈顶界面显示
        /// </summary>
        public void PopPanel()
        {
            //当前栈内为空,则直接返回
            if (panelStack.Count <= 0) return;
            panelStack.Pop().OnExit();//Pop删除栈顶元素,并关闭栈顶界面的显示,
            if (panelStack.Count <= 0) return;
            panelStack.Peek().OnResume();//获取现在栈顶界面,并调用界面恢复动作
        }
    }

    四、案例讲解

    点击主菜单按钮,实现各个面板之间的切换效果,源文件在末尾。
    效果展示如下:

    将制作好的面板做成预制体放在Resources文件中,如下所示:

    将状态面板类型记录在枚举中,如下所示:
    public enum UIPanelType
    {
        ItemMessagePanel, 
        CharacterPanel,
        KnapsackPanel,
        MainMenuPanel,
        ShopPanel,
        SkillPanel,
        SystemPanel,
        TaskPanel
    }
    根据面板和其路径,记录在JSON中,如下所示:
    [
        {
            "panelType": "ItemMessagePanel",
            "path": "UIPanel/ItemMessagePanel"
        },
        {
            "panelType": "CharacterPanel",
            "path": "UIPanel/CharacterPanel"
        },
    
        {
            "panelType": "KnapsackPanel",
            "path": "UIPanel/KnapsackPanel"
        },
    
        {
            "panelType": "ShopPanel",
            "path": "UIPanel/ShopPanel"
        },
    
        {
            "panelType": "SkillPanel",
            "path": "UIPanel/SkillPanel"
        },
    
        {
            "panelType": "SystemPanel",
            "path": "UIPanel/SystemPanel"
        },
        {
            "panelType": "TaskPanel",
            "path": "UIPanel/TaskPanel"
        }
    ]

    各个界面分别创建BasePanel的子类,用于实现界面的四种状态。为了方便实现界面的动画,使用DOTween插件,如果不会的话,可以看我之前的学习博客。 https://blog.csdn.net/qq_35361471/article/details/79353071
    为各个界面添加CanvasGroup组件,如下:

    Alpha可以方便实现隐藏和显示,取消勾选Blocks Raycasts,可以实现屏蔽UI射线检测
    主菜单界面需要实现一个提供给各个界面按钮的方法接口,如下:
    public void OnPushPanel(string panelTypeString)
     {
            UIPanelType panelType = (UIPanelType)System.Enum.Parse(typeof(UIPanelType), panelTypeString);
            UIManager.Instance.PushPanel(panelType);
    }
    给对应按钮添加监听方法的时候,需要注意,这里的字符串要和枚举UIPanelType里的值写的一致。

    以CharacterPanel.cs为例,继承BasePanel,并重写四种方法,使用DOTween实现动画,代码如下:
    using UnityEngine;
    using DG.Tweening;
    
    public class CharacterPanel : BasePanel
    {
        private CanvasGroup canvasGroup;
    
        void Start()
        {
            if (canvasGroup == null) canvasGroup = GetComponent<CanvasGroup>();
        }
    
        public override void OnEnter()
        {
            if (canvasGroup == null) canvasGroup = GetComponent<CanvasGroup>();
            canvasGroup.alpha = 1;
            canvasGroup.blocksRaycasts = true;
    
            Vector3 temp = transform.localPosition;
            temp.x = -800;
            transform.localPosition = temp;
            transform.DOLocalMoveX(0, 0.5f);
        }
    
        public override void OnPause()
        {
            canvasGroup.blocksRaycasts = false;
        }
    
        public override void OnResume()
        {
            canvasGroup.blocksRaycasts = true;
        }
    
        public override void OnExit()
        {
            transform.DOLocalMoveX(-800, .5f).OnComplete(() => canvasGroup.alpha = 0);
        }
    
        public void OnClosePanel()
        {
            UIManager.Instance.PopPanel();
        }
    }
    
    
    

    其他界面代码实现类似,可以自己实现不同的切换效果。觉得我分享的学习博客有用的话,记得点赞关注我哦!

    源文件百度云链接:链接:https://pan.baidu.com/s/1r-4AKfeO1WxUiENuo1lLLQ 密码:vrs1

     
    展开全文
  • Unity3D 搭建优雅的UI框架

    万次阅读 2017-04-19 11:13:08
    我相信很多小白,包括我在刚学习Unity3D UI的时候都这样想过。 我的第一款款Unity2D游戏《山地赛车》,使用的就是NGUI搭载界面。 弱联网手游一般都没什么复杂的界面,我也是很轻松花一天就把界面搭载好了,看起来...

    为什么要使用UI框架?直接使用NGUI或UGUI一拖一拉直接搭载出界面不就行了?

    我相信很多小白,包括我在刚学习Unity3D UI的时候都这样想过。

    我的第一款款Unity2D游戏《山地赛车》,使用的就是NGUI搭载界面。

    弱联网手游一般都没什么复杂的界面,我也是很轻松花一天就把界面搭载好了,看起来好挺好看的,还花了不少时间做动态效果。

    界面搭载好后,开始开发游戏内容,这下问题开始来了:

    1.如何实现界面间的沟通?例如点击返回按钮,返回上一个界面,点击背包系统,弹出背包。

    2.如何实现界面与游戏数据的沟通?例如点击排行榜,能列出最新的排名,点击购买车辆,能扣钱并买入新的车辆。

    一开始我的做法(我相信也是大部分新手最喜欢的做法)就是为每个要触发功能的UI添加一个脚本,然后添加一个public gameobject,

    然后拖入触发UI时要控制的object。在脚本的OnClick等函数里实现逻辑功能。

    这样做是挺容易。很快我也花了半天时间拖来拖去,把UI要关联的各种物件绑定好。

    但是接下来的各种问题让我非常头疼:

    1.随着游戏系统的复杂,UI控件越来越多,各个UI直接的通讯,以及UI与GameObject之间的通讯形成一张复杂的蜘蛛网,

    拖着拖着,有时候我都忘了哪个对象跟哪个对象关联了。如果是别人要看我的程序,我估计他找半天都找不到UI逻辑的入口。

    2.耦合性非常严重,如果要改变需求,更改某个UI或者更改某个游戏对象,那么你需要再手动全部与该对象关联的物件重新更改一次。

    3.作为强迫症的我,最受不了程序代码的混乱。这种组织方式十分“不优雅”,看着很乱。

     

    鉴于以上各种情况,我开始寻找一种新的,科学的,高效的UI管理方式,

    最开始想到的就是大名鼎鼎的MVC模式,我想过用它来管理我的UI,不过由于我对MVC模式不是很熟悉,尝试了下,效果并不是很好。

    在网上搜到几个不错的UI框架,都写得很不错,各位可以参考参考:

    1.Unity-UI-Framework的设计与实现 By:王选易 Github

    2.简单、强大的TTUIFramework By:chiuan 游戏蛮牛 (这位的设计思路是参考上面那位作者的)

    3.【设计和开发一套自动化UI框架】 By:NPC燕 游戏蛮牛

     

    他们的设计思路都很清晰,做出来的效果也都很强大,都是些游戏界的大牛,但是都有点复杂。

    作为一名菜鸟,我当然要设计出只有菜鸟能看得懂的东西啦。

    好了,废话不多说,以下是我设计的两个主要方向:

    1.设计一套用于管理UI的框架,各个UI的生成,销毁,切换,都是通过这个Manager单例来实现。各个UI间不直接联系。

    2.所有UI都基于一个UIBase基类,每个UI的预设对应一个UIBase子类脚本,UI的基本逻辑在该类中实现,例如:_MenuView.prefab 对应 _MenuView.cs

    3.重新实现Unity3D的消息通讯,原有SendMessage效率较低,利用订阅-发布(即观察者模式)重新设计一套通讯中心,所有UI间的通讯,

    以及UI和游戏层的通讯,皆间接通过MessageCenter来管理。实现解耦。

    4.以栈发方式管理UI,每次打开一个新的UI,都将它堆入栈,关闭时出栈。这个栈是一个特殊的栈,例如它可以实现,某个不在栈顶的UI,可以“TOP”到栈顶。

     

    几个不错的UI框架

    1.Unity-UI-Framework的设计与实现 By:王选易 Github

    http://www.manew.com/thread-42748-1-1.html

    https://github.com/MrNerverDie/Unity-UI-Framework

    2.简单、强大的TTUIFramework By:chiuan 游戏蛮牛 (这位的设计思路是参考上面那位作者的)

    http://www.manew.com/thread-42929-1-1.html?_dsign=8c9bee85

    3.【设计和开发一套自动化UI框架】 By:NPC燕 游戏蛮牛

    http://www.manew.com/thread-41889-1-1.html?_dsign=57fa0b1a

    未完待续。。。

    展开全文
  • unity3DUI框架

    2020-07-17 17:51:13
    代码简洁明了,层次分明,后期容易扩展,高度解耦,适合初学者进行项目开发
  • Unity3D基础UI框架,Unity MVVM

    万次阅读 2020-03-06 14:20:17
    之前学了刘国柱老师的UI框架加上我自己的理解做了一个UI框架正好可以使用 这里附上刘国柱老师的博客http://www.cnblogs.com/LiuGuozhu/ 受益颇深 基本UI框架的类图就是这样了 大体是根据MVC加上我的魔改而成...

    最新:

    参考公司lua的mvvm写了一个c#版的,https://github.com/so-sos-so/SoF,欢迎提建议

    后续会新开一篇文章补充开发思路

    ------------------------------弃用---------------------------------

    之前学了刘国柱老师的UI框架加上我自己的理解做了一个UI框架正好可以使用

    这里附上刘国柱老师的博客 http://www.cnblogs.com/LiuGuozhu/ 受益颇深

    基本UI框架的类图就是这样了

    大体是根据MVC加上我的魔改而成的框架,至少我自己暂时觉得还是思路挺清晰的

    Panel的切换是使用的Canvas Group的更改透明度,在加上使用

    此方法来更改显示顺序和显隐的

    其中的界面反向切换是使用的栈后入先出的特性

    反向切换是指一个窗口弹出小窗口后,父窗口的Raycast需要取消,下面贴上核心代码

    using System.Collections;
    using System;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.UI;
    
    public class UIManager :MonoBehaviour {
    
        /// <summary>
        /// 单例模式:
        /// 1.定义一个静态的对象 在外界访问 内部构造
        /// 2.构造方法私有化
        /// </summary>
        #region 单例模式
    
        private static UIManager _instance;
    
        public static UIManager Instance
        {
            get
            {
                if (_instance == null)
                {
                    UIManager[] instances = FindObjectsOfType<UIManager>();
                    if (instances != null)
                    {
                        _instance = instances[0];
                        for (var i = 1; i < instances.Length; i++)
                        {
                            Destroy(instances[i].gameObject);
                        }
                    }
                }
                return _instance;
            }
        }
    
        #endregion
    
    
        private Transform _canvas;
        public Transform Canvas
        {
            get
            {
                if (_canvas == null)
                    _canvas = GameObject.Find("Canvas").transform;
                return _canvas;
            }
        }
        //存储所有面板prefab的地址
        private Dictionary<UIPanelType, string> _DicPanelPath;
        //存储所有实例化的面板身上的BasePanel组件
        private Dictionary<UIPanelType, BasePanel> _DicPanel;
        //存储所有显示的面板上的BasePanel组件
        private Dictionary<UIPanelType, BasePanel> _DicShowPanel;
        //表示显示面板的栈
        private Stack<BasePanel> _StackPanel;
        //全屏显示的节点
        private Transform _TraFullScreen = null;
        //固定显示的节点
        private Transform _TraFixed = null;
        //弹出节点
        private Transform _TraPopUp = null;
        脚本节点
        //private Transform _TraScript = null;
    
    
        private void Awake()
        {
            ParseUIPanelPathJson();
            _StackPanel = new Stack<BasePanel>();
            _DicPanel = new Dictionary<UIPanelType, BasePanel>();
            _DicShowPanel = new Dictionary<UIPanelType, BasePanel>();
            _TraFullScreen = Canvas.Find("FullScreen");
            _TraFixed = Canvas.Find("Fixed");
            _TraPopUp = Canvas.Find("PopUp");
            DontDestroyOnLoad(Canvas.gameObject);
            //_TraScript = Canvas.Find("Script");
        }
    
    
    
        /// <summary>
        /// 把类型为反向切换的窗体入栈
        /// </summary>
        /// <param name="type"></param>
        private void PushPanel(UIPanelType type)
        {
            BasePanel basePanel = GetPanel(type);
            if (_StackPanel.Count >= 1)
            {
                BasePanel topPanel = _StackPanel.Peek();
                topPanel.OnPause();
            }
            _StackPanel.Push(basePanel);
            basePanel.OnEnter();
        }
    
        /// <summary>
        /// 把类型为反向切换的窗体出栈
        /// </summary>
        /// <param name="type"></param>
        private void PopPanel(UIPanelType type)
        {
            //如果栈里面为空,则返回
            if (_StackPanel.Count <= 0) return;
            BasePanel basePanel = GetPanel(type);
            BasePanel topPanel = _StackPanel.Peek();
            //如果当前要出栈的界面跟栈顶界面相同时 才会出栈
            if(basePanel.gameObject == topPanel.gameObject)
            {
                _StackPanel.Pop();
                topPanel.OnExit();
                //如果出栈后栈里面还有界面则恢复
                if(_StackPanel.Count >= 1)
                {
                    _StackPanel.Peek().OnResume();
                }
            }
            else
            {
                Debug.LogError("栈顶界面不是此界面,请检查进出栈!!");
            }
            
        }
    
        /// <summary>
        /// 显示界面
        /// </summary>
        /// <param name="type"></param>
        public void ShowUIForms(UIPanelType type)
        {
            BasePanel basePanel = GetPanel(type);
            switch (basePanel.uiType.uiFormShowMode)
            {
                case UIFormShowMode.Normal:
                    EnterUIFormsCache(type);
                    break;
                //如果是反切换类型则入栈
                case UIFormShowMode.ReverseChange:
                    PushPanel(type);
                    break;
                //如果是隐藏其他的类型,清空显示的界面
                case UIFormShowMode.HideOther:
                    HideOtherPanel(type);
                    break;
            }
        }
    
        /// <summary>
        /// 隐藏界面 如果显示界面字典里面存在此界面
        /// </summary>
        /// <param name="type"></param>
        public void HideUIForms(UIPanelType type)
        {
            BasePanel basePanel = GetPanel(type);
    
            switch (basePanel.uiType.uiFormShowMode)
            {
                case UIFormShowMode.Normal:
                    HideUIFormsCache(type);
                    break;
                //如果是反切换类型则出栈
                case UIFormShowMode.ReverseChange:
                    PopPanel(type);
                    break;
                case UIFormShowMode.HideOther:
                    HideUIFormsCache(type);
                    break;
            }
        }
    
        /// <summary>
        /// 隐藏除此之外的Panel,如果不赋值则清空所有
        /// </summary>
        /// <param name="type"></param>
        public void HideOtherPanel(UIPanelType type = UIPanelType.Null)
        {
            foreach (KeyValuePair<UIPanelType,BasePanel> item in _DicShowPanel)
            {
                item.Value.OnExit();
            }
            _DicShowPanel.Clear();
            if (type != UIPanelType.Null)
            {
                BasePanel basePanel = GetPanel(type);
                basePanel.OnEnter();
                _DicShowPanel.Add(type, GetPanel(type));
            }
                
        }
    
        /// <summary>
        /// 将UI加入到已显示面板的字典中
        /// </summary>
        /// <param name="type"></param>
        private void EnterUIFormsCache(UIPanelType type)
        {
            //如果显示面板字典里面有当前面板则返回
            if (_DicShowPanel.ContainsKey(type)) return;
            BasePanel basePanel = GetPanel(type);
            _DicShowPanel.Add(type, basePanel);
            basePanel.OnEnter();
        }
    
        /// <summary>
        /// 将UI隐藏
        /// </summary>
        /// <param name="type"></param>
        private void HideUIFormsCache(UIPanelType type)
        {
            //如果显示面板字典里面没有当前面板则返回
            if (!_DicShowPanel.ContainsKey(type)) return;
            BasePanel basePanel = GetPanel(type);
            _DicShowPanel.Remove(type);
            basePanel.OnExit();
        }
    
        /// <summary>
        /// 根据面板类型得到实例化的面板
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        private BasePanel GetPanel(UIPanelType type)
        {
            if (_DicPanel.ContainsKey(type))
            {
                return _DicPanel[type];
            }
            else
            {
                //如果找不到就找这个面板的prefab的路径,然后根据prefab去实例化面板
                BasePanel panel;
                string panelPath = _DicPanelPath.GetValue(type);
                GameObject insGo = GameObject.Instantiate(Resources.Load(panelPath),Canvas) as GameObject;
                insGo.name = type.ToString();
                panel = insGo.GetComponent<BasePanel>();
                switch (panel.uiType.uIFormParentType)
                {
                    case UIFormParentType.FUllScreen:
                        insGo.transform.SetParent(_TraFullScreen);
                        break;
                    case UIFormParentType.Fixed:
                        insGo.transform.SetParent(_TraFixed);
                        break;
                    case UIFormParentType.PopUp:
                        insGo.transform.SetParent(_TraPopUp);
                        break;
                }
                _DicPanel.Add(type, panel);
                return panel;
            }
        }
    
        #region 解析地址json 并存储到字典中
        
        private void ParseUIPanelPathJson()
        {
            TextAsset text = Resources.Load<TextAsset>("UIPanelType");
            JSONObject jSONObject = new JSONObject(text.text);
            if (_DicPanelPath == null)
                _DicPanelPath = new Dictionary<UIPanelType, string>();
            foreach (JSONObject item in jSONObject.list)
            {
                string panelTypeString = item["panelTypeString"].str;
                string path = item["path"].str;
                UIPanelType uIPanelType = (UIPanelType)System.Enum.Parse(typeof(UIPanelType), panelTypeString);
                _DicPanelPath.Add(uIPanelType, path);
            }
        }
    
        #endregion
    
    }
    
    using System.Collections;
    using System;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.UI;
    
    
    public class BasePanel : MonoBehaviour {
    
    
        protected CanvasGroup cg;
        public UIType uiType;
    
    
        private void Awake()
        {
            InitData();
        }
    
    
        public virtual void OnCloseBtnClick()
        {
            UIManager.Instance.HideUIForms(uiType.uiPanelType);
        }
    
    
        public virtual void InitData()
        {
            if (cg == null)
                cg = this.gameObject.GetAndAddComponent<CanvasGroup>();
            if (uiType == null)
                uiType = new UIType();
        }
    
    
    
    
        /// <summary>
        /// 界面被显示出来
        /// </summary>
        public virtual void OnEnter()
        {
            cg.alpha = 1;
            cg.blocksRaycasts = true;
            this.gameObject.transform.SetAsLastSibling();
        }
    
    
        /// <summary>
        /// 界面暂停 指的是当其他界面在此界面之上,此界面不是主界面的时候,此界面不接受鼠标检测,如果不需要此功能可以适当更改
        /// </summary>
        public virtual void OnPause()
        {
            cg.blocksRaycasts = false;
        }
    
    
        /// <summary>
        /// 界面继续
        /// </summary>
        public virtual void OnResume()
        {
            cg.blocksRaycasts = true;
        }
    
    
        /// <summary>
        /// 界面不显示,退出这个界面
        /// </summary>
        public virtual void OnExit()
        {
            cg.alpha = 0;
            cg.blocksRaycasts = false;
        }
    	
    }
    

     

    using System.Collections;
    using System;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class UIType  {
    
        /// <summary>
        /// panel类别
        /// </summary>
        public UIPanelType uiPanelType;
        /// <summary>
        /// Panel的显示方式,有正常显示,隐藏其他和反向切换
        /// </summary>
        public UIFormShowMode uiFormShowMode = UIFormShowMode.Normal;
        /// <summary>
        /// 这个透明度暂时没用
        /// </summary>
        public UIFormLucencyType uIFormLucencyType = UIFormLucencyType.Lucency;
        /// <summary>
        /// Panel的父物体类型
        /// </summary>
        public UIFormParentType uIFormParentType = UIFormParentType.PopUp;
    

     

    代码比较多,不过都有贴心的注释

     

    -------------------------------加了一个代码自动生成的工具-----------------------------------

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using System.Threading.Tasks;
    using System;
    using UnityEditor;
    using System.IO;
    using System.Text;
    using UnityEngine.UI;
    
    public class UIElement
    {
        public string Name;
        public string Path;
        public string ComponentName;
        public UIElement(string name,string path,string componentName)
        {
            Name = name;
            Path = path;
            ComponentName = componentName;
        }
    
        public override string ToString()
        {
            string str = string.Format("Name={0} || Path={1} || ComponentName={2}", Name, Path, ComponentName);
            return str;
        }
    }
    
    public class UICodeGenerator
    {
        [MenuItem ( "Assets/CreateCodeDeleteComponent" )]
        public static void CreateCodeDeleteComponent ()
        {
            GetPath ( true );
        }
        
        [MenuItem ( "Assets/OnlyCreateCode" )]
        public static void OnlyCreateCode ()
        {
            GetPath ( false );
        }
        
        public static void GetPath (bool isDeleteComponent)
        {
            var objs =
                Selection.GetFiltered ( typeof ( GameObject ), SelectionMode.Assets | SelectionMode.TopLevel );
            GameObject obj = objs[ 0 ] as GameObject;
            elements = new List<UIElement> ();
            GetPathAs ( obj.transform, isDeleteComponent );
    
            foreach ( var item in elements )
            {
                Debug.Log ( item );
            }
    
            GeneratePane ( "Assets/" + obj.name + ".cs", obj.name, elements );
            GenerateCtrl ( "Assets/" + obj.name + "Ctrl.cs", obj.name, elements );
    
        }
    
        public static List<UIElement> elements;
    
        static void GetPathAs ( Transform transform ,bool isDeleteComponent)
        {
            foreach ( Transform child in transform )
            {
                if ( child.gameObject.GetComponent<UIMark> () )
                {
                    elements.Add ( new UIElement ( child.name, GetPath ( child ),
                                                   child.gameObject.GetComponent<UIMark> ().ComponentName ) );
                    if ( isDeleteComponent )
                        GameObject.DestroyImmediate ( child.gameObject.GetComponent<UIMark> (), true );
                }
    
                if ( child.childCount != 0 )
                {
                    GetPathAs ( child, isDeleteComponent );
                }
            }
        }
    
    
        public static void GeneratePane ( string generateFilePath, string behaviourName, List<UIElement> elements )
        {
            var sw         = new StreamWriter ( generateFilePath, false, Encoding.UTF8 );
            var strBuilder = new StringBuilder ();
    
            strBuilder.AppendLine ( "using UnityEngine;" );
            strBuilder.AppendLine ( "using UnityEngine.UI;" );
            strBuilder.AppendLine ();
            strBuilder.AppendFormat ( "public class {0} : BasePanel ", behaviourName );
            strBuilder.AppendLine ();
            strBuilder.AppendLine ( "{" );
            foreach ( var item in elements )
            {
                strBuilder.AppendLine ( "\tpublic " + item.ComponentName + " " + item.Name + " { get; private set; }" );
            }
    
            strBuilder.AppendLine ();
            strBuilder.AppendLine ( "\tpublic override void InitData()" );
            strBuilder.AppendLine ( "\t{" );
            foreach ( var item in elements )
            {
                strBuilder.AppendFormat ( "\t\t{0} = transform.Find(\"{1}\").GetComponent<{2}>();", item.Name,
                                          item.Path.Replace ( behaviourName + "/", "" ), item.ComponentName );
                strBuilder.AppendLine ();
            }
    
            strBuilder.AppendLine ();
            strBuilder.AppendLine ( "\t\tuiType.uIFormParentType = UIFormParentType.PopUp;" );
            strBuilder.AppendLine ( "\t\tuiType.uiFormShowMode = UIFormShowMode.Normal;" );
            strBuilder.AppendLine ( "\t\tuiType.uiPanelType = UIPanelType.BoxPanel;" );
            strBuilder.AppendLine ( "\t}" );
            strBuilder.AppendLine ( "}" );
            sw.Write ( strBuilder );
            sw.Flush ();
            sw.Close ();
    
            AssetDatabase.SaveAssets ();
            AssetDatabase.Refresh ();
        }
    
        public static void GenerateCtrl ( string generateFilePath, string behaviourName , List<UIElement> elements)
        {
            var sw         = new StreamWriter ( generateFilePath, false, Encoding.UTF8 );
            var strBuilder = new StringBuilder ();
    
            List<UIElement> temp = new List<UIElement> ();
            
            foreach ( UIElement element in elements )
            {
                if(element.ComponentName.Equals("Button"))
                    temp.Add(element);
            }
            
            strBuilder.AppendLine ( "using UnityEngine;" );
            strBuilder.AppendLine ( "using UnityEngine.UI;" );
            strBuilder.AppendLine ();
            strBuilder.AppendFormat ( "public class {0}Ctrl : BaseCtrl ", behaviourName );
            strBuilder.AppendLine ( "{" );
            strBuilder.AppendLine ();
            strBuilder.AppendFormat ( "\tprivate {0} panel;", behaviourName );
            strBuilder.AppendLine ();
            strBuilder.AppendLine ();
            strBuilder.AppendLine ( "\tpublic override void InitPanel()" );
            strBuilder.AppendLine ( "\t{" );
            strBuilder.AppendFormat ( "\t\tpanel = GetComponent<{0}>();", behaviourName );
            strBuilder.AppendLine ();
            foreach ( UIElement element in temp )
            {
                strBuilder.AppendFormat ( "\t\tpanel.{0}.AddListenerGracefully( {1}Click );", element.Name, element.Name );
                strBuilder.AppendLine ();
            }
            strBuilder.AppendLine ( "\t}" );
            strBuilder.AppendLine ();
            foreach ( UIElement element in temp )
            {
                strBuilder.AppendFormat ("\tvoid {0}Click()",element.Name);
                strBuilder.AppendLine ();
                strBuilder.AppendLine ( "\t{" );
                strBuilder.AppendLine ();
                strBuilder.AppendLine ( "\t}" );
                strBuilder.AppendLine ();
            }
    
            strBuilder.AppendLine ( "}" );
            sw.Write ( strBuilder );
            sw.Flush ();
            sw.Close ();
    
            AssetDatabase.SaveAssets ();
            AssetDatabase.Refresh ();
        }
    
        public static string GetPath ( Transform transform )
        {
            var sb = new System.Text.StringBuilder ();
            var t  = transform;
            while ( true )
            {
                sb.Insert ( 0, t.name );
                t = t.parent;
                if ( t )
                {
                    sb.Insert ( 0, "/" );
                }
                else
                {
                    return sb.ToString ();
                }
            }
        }
    }
    

     

     

    using UnityEngine.UI;
    using UnityEngine;
    
    /// <inheritdoc />
    /// <summary>
    /// UI的标记
    /// </summary>
    public class UIMark : MonoBehaviour
    {
        //public UIMarkType MarkType = UIMarkType.DefaultUnityElement;
    
        public Transform Transform
        {
            get { return transform; }
        }
    
        public string CustomComponentName = "";
        
        public virtual string ComponentName
        {
            get
            {
                if ( !string.IsNullOrEmpty ( CustomComponentName ) )
                    return CustomComponentName;
                if (null != GetComponent("SkeletonAnimation"))
                    return "SkeletonAnimation";
                if (null != GetComponent<ScrollRect>())
                    return "ScrollRect";
                if (null != GetComponent<InputField>())
                    return "InputField";
                if (null != GetComponent<Text>())
                    return "Text";
                if (null != GetComponent("TMP.TextMeshProUGUI"))
                    return "TextMeshProUGUI";
                if (null != GetComponent<Button>())
                    return "Button";
                if (null != GetComponent<RawImage>())
                    return "RawImage";
                if (null != GetComponent<Toggle>())
                    return "Toggle";
                if (null != GetComponent<Slider>())
                    return "Slider";
                if (null != GetComponent<Scrollbar>())
                    return "Scrollbar";
                if (null != GetComponent<Image>())
                    return "Image";
                if (null != GetComponent<ToggleGroup>())
                    return "ToggleGroup";
                if (null != GetComponent<Animator>())
                    return "Animator";
                if (null != GetComponent<Canvas>())
                    return "Canvas";
                if (null != GetComponent("Empty4Raycast"))
                    return "Empty4Raycast";
                if (null != GetComponent<RectTransform>())
                    return "RectTransform";
    
                return "Transform";
    
    
            }
        }
    }
    

    使用方法,把需要使用的组件加上UIMark标记,拖成预设体,右键点击自动生成代码,就会自动生成视图和控制层

    -------------------------------弃用-------------------------------------

    思想可以借鉴,但是使用起来不是很方便

    最新版框架借鉴QFramework,喜欢的可以去看看

    展开全文
  • Unity3D问题之简单UI框架设计和实现

    千次阅读 2017-11-29 00:10:58
    目标:编写一个简单通用UI框架用于管理页面和完成导航跳转 框架具体实现的功能和需求 加载,显示,隐藏,关闭页面,根据标示获得相应界面实例 提供界面显示隐藏动画接口 单独界面层级,Collider,背景管理 根据...

    目标:编写一个简单通用UI框架用于管理页面和完成导航跳转

    框架具体实现的功能和需求

    加载,显示,隐藏,关闭页面,根据标示获得相应界面实例
    提供界面显示隐藏动画接口
    单独界面层级,Collider,背景管理
    根据存储的导航信息完成界面导航
    界面通用对话框管理(多类型Message Box)
    便于进行需求和功能扩展(比如,在跳出页面之前添加逻辑处理等)


    编写UI框架意义

    打开,关闭,层级,页面跳转等管理问题集中化,将外部切换等逻辑交给UIManager处理
    功能逻辑分散化,每个页面维护自身逻辑,依托于框架便于多人协同开发,不用关心跳转和显示关闭细节
    通用性框架能够做到简单的代码复用和"项目经验"沉淀


    步入正题,如何实现

    窗口类设计:基本窗口对象,维护自身逻辑维护
    窗口管理类:控制被管理窗口的打开和关闭等逻辑(具体设计请看下文)
    动画接口:提供打开和关闭动画接口,提供动画完成回调函数等
    层级,Collider和通用背景管理


    窗口基类设计

    框架中设计的窗口类型和框架所需定义如下,对于UIBaseWindow 基类,只给出一个界面基类需要提供的接口
    public enum UIWindowType
    {
        Normal,    // 可推出界面(UIMainMenu,UIRank等)
        Fixed,     // 固定窗口(UITopBar等)
        PopUp,     // 模式窗口
    }
    
    public enum UIWindowShowMode
    {
        DoNothing,
        HideOther,     // 闭其他界面
        NeedBack,      // 点击返回按钮关闭当前,不关闭其他界面(需要调整好层级关系)
        NoNeedBack,    // 关闭TopBar,关闭其他界面,不加入backSequence队列
    }
    
    public enum UIWindowColliderMode
    {
        None,      // 显示该界面不包含碰撞背景
        Normal,    // 碰撞透明背景
        WithBg,    // 碰撞非透明背景
    }

    public class UIBaseWindow : MonoBehaviour
    {
        protected UIPanel originPanel;
        // 如果需要可以添加一个BoxCollider屏蔽事件
        private bool isLock = false;
        protected bool isShown = false;
    
        // 当前界面ID
        protected WindowID windowID = WindowID.WindowID_Invaild;
    
        // 指向上一级界面ID(BackSequence无内容,返回上一级)
        protected WindowID preWindowID = WindowID.WindowID_Invaild;
        public WindowData windowData = new WindowData();
        // Return处理逻辑
        private event BoolDelegate returnPreLogic = null;
    
        protected Transform mTrs;
        protected virtual void Awake()
        {
            this.gameObject.SetActive(true);
            mTrs = this.gameObject.transform;
            InitWindowOnAwake();
        }
    
        private int minDepth = 1;
        public int MinDepth
        {
            get { return minDepth; }
            set { minDepth = value; }
        }
    
        /// 
        /// 能否添加到导航数据中
        /// 
    
        public bool CanAddedToBackSeq
        {
            get
            {
                if (this.windowData.windowType == UIWindowType.PopUp)
                    return false;
                if (this.windowData.windowType == UIWindowType.Fixed)
                    return false;
                if (this.windowData.showMode == UIWindowShowMode.NoNeedBack)
                    return false;
                return true;
            }
        }
    
        /// 
        /// 界面是否要刷新BackSequence数据
        /// 1.显示NoNeedBack或者从NoNeedBack显示新界面 不更新BackSequenceData(隐藏自身即可)
        /// 2.HideOther
        /// 3.NeedBack
        /// 
    
        public bool RefreshBackSeqData
        {
            get
            {
                if (this.windowData.showMode == UIWindowShowMode.HideOther
                    || this.windowData.showMode == UIWindowShowMode.NeedBack)
                    return true;
                return false;
            }
        }
    
        /// 
        /// 在Awake中调用,初始化界面(给界面元素赋值操作)
        /// 
    
        public virtual void InitWindowOnAwake()
        {
        }
    
        /// 
        /// 获得该窗口管理类
        /// 
        public UIManagerBase GetWindowManager
        {
            get
            {
                UIManagerBase baseManager = this.gameObject.GetComponent();
                return baseManager;
            }
            private set { }
        }
    
        /// 
        /// 重置窗口
        /// 
        public virtual void ResetWindow()
        {
        }
    
        /// 
        /// 初始化窗口数据
        /// 
        public virtual void InitWindowData()
        {
            if (windowData == null)
                windowData = new WindowData();
        }
    
        public virtual void ShowWindow()
        {
        }
    
        public virtual void HideWindow(Action action = null)
        {
        }
    
        public void HideWindowDirectly()
        {
        }
    
        public virtual void DestroyWindow()
        {
        }
    
        protected virtual void BeforeDestroyWindow()
        {
        }
    
        /// 
        /// 界面在退出或者用户点击返回之前都可以注册执行逻辑
        /// 
        protected void RegisterReturnLogic(BoolDelegate newLogic)
        {
            returnPreLogic = newLogic;
        }
    
        public bool ExecuteReturnLogic()
        {
            if (returnPreLogic == null)
                return false;
            else
                return returnPreLogic();
        }
    }


    动画接口设计

    界面可以继承该接口进行实现打开和关闭动画,设计成接口的目的是为了让动画功能更好的扩展,一个界面可以根据需求设置成多个编写的动画interface
    ///
    /// 窗口动画
    ///
    interface IWindowAnimation
    {
    ///
    /// 显示动画
    ///
    void EnterAnimation(EventDelegate.Callback onComplete);
    /// 
        /// 隐藏动画
        /// 
        void QuitAnimation(EventDelegate.Callback onComplete);
        
        /// 
        /// 重置动画
        /// 
        void ResetAnimation();
    }


    窗口管理和导航设计实现

    导航功能实现通过一个显示窗口堆栈实现,每次打开和关闭窗口通过判断窗口属性和类型更新处理BackSequence数据

    ·打开界面:将当前界面状态压入堆栈中更新BackSequence数据
    ·返回操作(主动关闭当前界面或者点击返回按钮):从堆栈中Pop出一个界面状态,将相应的界面重新打开
    ·怎么衔接:比如从一个界面没有回到上一个状态而是直接的跳转到其他的界面,这个时候需要将BackSequence清空因为当前的导航链已经被破坏,当BackSequence为空需要根据当前窗口指定的PreWindowId告知系统当从该界面返回,需要到达的指定页面,这样就能解决怎么衔接的问题,如果没断,继续执行导航,否则清空数据,根据PreWindowId进行导航


    导航系统中关键性设计:

    游戏中可以存在多个的Manager进行管理(一般在很少需求下才会使用),每个管理对象需要维护自己的导航信息BackSequence,每次退出一个界面需要检测当前退出的界面是否存在相应的Manager管理,如果存在则需要先执行Manager退出操作(退出过程分步进行)保证界面一层接着一层正确退出

    窗口层级,Collider,统一背景添加如何实现?

    有很多方式进行层级管理,该框架选择的方法如下

    ·设置三个常用层级Root,根据窗口类型在加载到游戏中时添加到对应的层级Root下面即可,每次添加重新计算设置层级(通过UIPanel的depth实现)保证每次打开一个新窗口层级显示正确,每次窗口内通过depth的大小区分层级关系
    ·根据窗口Collider和背景类型,在窗口的最小Panel上面添加Collider或者带有碰撞体的BackGround即可
    Scene结构


    分层Depth设置

    存在相应的Manager管理,如果存在则需要先执行Manager退出操作(退出过程分步进行)保证界面一层接着一层正确退出
    private void AdjustBaseWindowDepth(UIBaseWindow baseWindow)
    {
    // 不同类型窗口添加不同的父节点下面
    // 根据当前父节点下面合法窗口的depth设置新打开窗口depth
    // 根据当前窗口背景和Collider模式,自动添加背景和碰撞
    UIWindowType windowType = baseWindow.windowData.windowType;
    int needDepth = 1;
    if (windowType == UIWindowType.Normal)
    {
    needDepth = Mathf.Clamp(GameUtility.GetMaxTargetDepth(UINormalWindowRoot.gameObject, false) + 1, normalWindowDepth, int.MaxValue);
    Debug.Log("[UIWindowType.Normal] maxDepth is " + needDepth + baseWindow.GetID);
    }
    else if (windowType == UIWindowType.PopUp)
    {
    needDepth = Mathf.Clamp(GameUtility.GetMaxTargetDepth(UIPopUpWindowRoot.gameObject) + 1, popUpWindowDepth, int.MaxValue);
    Debug.Log("[UIWindowType.PopUp] maxDepth is " + needDepth);
    }
    else if (windowType == UIWindowType.Fixed)
    {
    needDepth = Mathf.Clamp(GameUtility.GetMaxTargetDepth(UIFixedWidowRoot.gameObject) + 1, fixedWindowDepth, int.MaxValue);
    Debug.Log("[UIWindowType.Fixed] max depth is " + needDepth);
    }
    if(baseWindow.MinDepth != needDepth)
    GameUtility.SetTargetMinPanel(baseWindow.gameObject, needDepth);
    baseWindow.MinDepth = needDepth;
    }
    
    ///
    /// 窗口背景碰撞体处理
    ///
    private void AddColliderBgForWindow(UIBaseWindow baseWindow)
    {
    UIWindowColliderMode colliderMode = baseWindow.windowData.colliderMode;
    if (colliderMode == UIWindowColliderMode.None)
    return;
    
    if (colliderMode == UIWindowColliderMode.Normal)
        GameUtility.AddColliderBgToTarget(baseWindow.gameObject, "Mask02", maskAtlas, true);
    if (colliderMode == UIWindowColliderMode.WithBg)
        GameUtility.AddColliderBgToTarget(baseWindow.gameObject, "Mask02", maskAtlas, false);
    }

    多形态功能MessageBox实现

    这个应该是项目中一定会用到的功能,说下该框架简单的实现
    ·三个按钮三种回调逻辑:左中右三个按钮,提供设置内容,设置回调函数的接口即可
    ·提供接口设置核心Content
    ·不同作用下不同的按钮不会隐藏和显示
    // 设置中间按钮信息和回调函数
    public void SetCenterBtnCallBack(string msg, UIEventListener.VoidDelegate callBack)
    {
    lbCenter.text = msg;
    NGUITools.SetActive(btnCenter, true);
    UIEventListener.Get(btnCenter).onClick = callBack;
    }
    // 设置左侧按钮信息和回调函数
    public void SetLeftBtnCallBack(string msg, UIEventListener.VoidDelegate callBack)
    {
    lbLeft.text = msg;
    NGUITools.SetActive(btnLeft, true);
    UIEventListener.Get(btnLeft).onClick = callBack;
    }
    // 设置右侧按钮信息和回调函数
    public void SetRightBtnCallBack(string msg, UIEventListener.VoidDelegate callBack)
    {
    lbRight.text = msg;
    NGUITools.SetActive(btnRight, true);
    UIEventListener.Get(btnRight).onClick = callBack;
    }

    后续需要改进和增强计划

    1、图集管理,针对大中型游戏对游戏内存要求苛刻的项目,一般都会对UI图集贴图资源进行动态管理,加载和卸载图集,保证UI贴图占用较少内存
    2、增加一些通用处理:变灰操作,Mask遮罩(一般用于新手教程中)等
    3、在进行切换的过程可以需要Load新场景需求,虽然这个也可以在UI框架外实现
    4、对话系统也算是UI框架的功能,新手引导系统也可以加入到UI框架中,统一管理和处理新手引导逻辑

    展开全文
  • 基于Unity3D简单UI框架

    千次下载 热门讨论 2020-07-30 23:30:27
    基于Unity3D和NGUI实现的简单的UI框架,实现内容如下 1.加载,显示,隐藏,关闭页面,根据标示获得相应界面实例 2.提供界面显示隐藏动画接口 3.单独界面层级,Collider,背景管理 4.根据存储的导航信息完成界面导航...
  • Unity3D框架学习_基于FairyGUI的UI框架

    千次阅读 2019-04-03 01:34:31
    Unity3D框架学习_基于FairyGUI的UI框架 目录 1、博客介绍 2、FGUI介绍 3、框架介绍 (1)Core (2)结构 (3)FGUI工程目录 (4)命名规范 4、推送 5、结语 1、博客介绍 最近一段时间一直都在使用FGUI...
  • 一套完整的UnityUI框架、可用于实际开发。经过本人开发的考验
  • Unity3d游戏开发框架-UI管理类 UIManager

    万次阅读 2019-04-03 09:35:29
    新建一个文件夹:UIMgr 新建2个C#脚本:BaseUI.cs UIMgr.csusing UnityEngine; using System.Collections;public class BaseUI : MonoBehaviour { /// /// 当前界面名称 /// [HideInInspector]
  • 笔者介绍:姜雪伟,IT公司技术合伙人,IT高级讲师,CSDN社区专家,特邀编辑,畅销书作者,已出版书籍:《手把手教你架构3D游戏引擎》电子工业出版社和《Unity3D实战核心技术详解》电子工业出版社等。CSDN视频网址:...
  • Unity3D简单的UI系统

    千次阅读 2019-04-11 12:20:14
    Unity3d的ugui如果用得不好,非常的影响性能,可能很多人都不知道,其实ugui是基于网格模型渲染的,一个ugui组件就是一个Mesh,ui组件的Material为空,其实他是用了默认的Material,我们在运行游戏的时候Canvas回把...
  • 这是我自己总结的用于unity3d中的UGUI的代码框架,里面还有许多我总结的工具类,动态库
  • Unity3D通用UI框架

    2019-09-24 20:19:13
    目标:编写一个简单通用UI框架用于管理页面和完成导航跳转最终的实现效果请拉到最下方查看框架具体实现的功能和需求 加载,显示,隐藏,关闭页面,根据标示获得相应界面实例 提供界面显示隐藏动画接口 单独界面...
  • Unity3D游戏框架设计

    万次阅读 2018-07-03 20:00:09
    框架部分提供项目中使用的基础设施,包括资源管理、网络通信、UI框架、消息管理、场景管理、数据解析及存取等。1. 资源管理资源管理模块负责按照划分场景的颗粒度将所有游戏资源均打包至AssetBundle并在...
  • unity3d 基于mvc的ui框架实现

    千次阅读 2018-07-25 17:08:47
    https://blog.csdn.net/frank901/article/details/72847352 文章很简单,但我个人觉得框架逻辑上有些不合理的地方,文章中使用委托作为事件的触发,个人感觉不使用一样可以达到效果,所以没有使用委托。 大致内容...
  • UI组件基类:所有UI组件从这里继承 UI容器基类:当一个UI组件持有其它UI组件时,它就是一个容器类,它要负责调度其它UI组件的相关函数 UI控制层基类:发送网络请求(网络数据)、操作游戏逻辑、修改模型数据(本地...
  • Unity UI层级管理框架

    千次阅读 2017-11-07 22:15:56
    1.为什么要使用UI层级管理框架? 根据我个人写的UI层级总结出:在一般UI界面的 Canvas 下有多个Panel,每个Panel下又有很多个需要操作的控件(Image、Text、inputField、Button)等, 因此要去管理如此多的事件处理...
  • Unity3D技巧】一个简单的Unity-UI框架的实现 https://www.cnblogs.com/neverdie/p/unity_ui_framework.html 如何使用 请直接导入UnityUIFramework这个UnityPackage,然后进入名为Test的Scene即可开始体验各种...
  • unity基于MVC的ui框架

    热门讨论 2020-07-29 14:18:26
    unity基于MVC的ui框架》博客的资源,请到这里下载。自己写的框架用了几年了,也做了几个游戏,应该是经受住项目考验了吧,现在分享下简本。
  • unity框架系列——UI框架(一)

    千次阅读 2018-10-22 22:21:36
    当游戏界面很多时,各个界面通讯就会很复杂, 而界面之间又有先后顺序之分,必须先关闭当前窗体 ,才能返回之前打开的窗体,...游戏上线了,总会又改动,unity改图片?image 界面动画也时需要处理,隐藏setactive...
1 2 3 4 5 ... 20
收藏数 3,727
精华内容 1,490
热门标签
关键字:

ui unity3d 框架