unity3d层级控制_unity3d控制ui的层级关系 - CSDN
精华内容
参与话题
  • Unity3D ugui和模型粒子的层级管理

    千次阅读 2017-08-05 22:10:51
    直接看代码简单明了:   public class UIDepth : MonoBehaviour { public int order; public bool isUI = true; void Start() { if (isUI) ... Canvas canvas = this.GetComponent();...

        直接看代码简单明了:

         

    	public class UIDepth : MonoBehaviour
    	{
    		public int  order;
    		public bool isUI = true;
    
    		void Start() 
    		{
    			if (isUI)
    			{
    				Canvas canvas = this.GetComponent<Canvas>();
    				
    				if( canvas == null){
    					canvas = this.gameObject.AddComponent<Canvas>();
    					this.gameObject.AddComponent<GraphicRaycaster>();
    				}
    				
    				canvas.overrideSorting = true;
    				canvas.sortingOrder    = order;
    			}
    			else
    			{
    				Renderer[] renders = this.GetComponentsInChildren<Renderer>(true);
    				
    				foreach (Renderer renderer in renders)
    				{
    					renderer.sortingOrder = order;
    				}
    			}
    		}
        

        挂载到GameObject上,运行时根据order控制绘制次序。


        如果是针对ui控制次序,我们就动态添加Canvas并且设置sortingOrder和GraphicRaycaster。一个是控制绘制次序,一个是让这个canvas组件可以接受事件。ui默认在default layer的order都是0。


        如果是模型或是粒子,统一获得Renderer对象来设置sortingOrder。粒子系统的renderer默认是在default layer的order也是0。


       首先是layer的绘制顺序,同layer就是sortingOrder的绘制顺序,同sortingOrder然后就是ugui自己的绘制顺序就是节点在场景里的顺序,粒子和模型的自己顺序是依靠position z来控制的。这里设置的order会直接影响sortingOrder,组件的绘制顺序。


        ui我们最好以窗口作为单位,来设定一个整体的order,然后内部依然利用ugui自己的规则排序。一个窗口内部的粒子和模型需要和这个窗口order一致才能正确显示。不同的窗口不同的次序,这样可以正确的区分每个窗口的层级和里面包含的粒子与模型顺序。

        

        

    展开全文
  • UI框架中UI窗体的“层级管理”,最核心的问题是如何进行窗体的显示管理。窗体(预设)的显示我们前面定义了三种类型: 普通、隐藏其他、反向切换。 “普通显示”模式允许多个窗体同时显示,这种类型应用最多。 ...

    UI框架中UI窗体的“层级管理”,最核心的问题是如何进行窗体的显示管理。窗体(预设)的显示我们前面定义了三种类型: 普通、隐藏其他、反向切换。

    “普通显示”模式允许多个窗体同时显示,这种类型应用最多。

    “隐藏其他界面” 模式一般应用于全局性的窗体。我们在开发此类窗体时,为了减少UI渲染压力、提高Unity渲染效率,则设置被覆盖的窗体为“不可见”状态。(即: this.gameObject.SetActive(false))。例如一般的登录窗体、选择英雄窗体等。

    “反向切换”模式类型,一般都大量引用于“弹出窗体”中。此类窗体的特点是:显示弹出窗体时不完全覆盖底层窗体,一般在屏幕的四周会露出底层窗体。之所以命名“反向切换”是因为: 程序员要维护一种“后进先出”的“栈”的数据结构特点,即我们一般要求玩家必须先关闭弹出的顶层窗体,再依次关闭下一级窗体。

    一般我们都要求玩家先处理弹出窗体中的信息,然后关闭此窗体。一般不允许在没有关闭子窗体的情况下,直接点击父窗体。以上说了这么多了,对于“层级管理”的核心代码实现,基本都体现在“UI管理器脚本” (UIManager.cs )中。

    public class UiManager : MonoBehaviour
    {
        private static UiManager _instance;
        /// <summary>
        /// 得到实例
        /// </summary>
        public static UiManager Instance()
        {
            if (_instance == null)
            {
                _instance = new GameObject("UiManager").AddComponent<UiManager>();
            }
            return _instance;
        }
        private UiManager() { }
    
        //UI窗体预设路径(参数1:窗体预设名称,2:表示窗体预设路径)
        private Dictionary<UiWind, string> _dicFormsPaths;
        //缓存所有UI窗体
        private Dictionary<string, BaseUiFrame> _dicAllUiForms;
        //当前显示的UI窗体
        private Dictionary<string, BaseUiFrame> _dicCurrentShowUiForms;
        //定义“栈”集合,存储显示当前所有[反向切换]的窗体类型
        private Stack<BaseUiFrame> _staCurrentUiForms;
    
        //UI根节点
        private Transform CanvasTrans;
        //全屏幕显示的节点
        private Transform _normalTrans;
        //固定显示的节点
        private Transform _fixedTrans;
        //弹出节点
        private Transform _popUpTrans;
    
        //UI管理脚本的节点
        private Transform _uiScriptsTrans;
    
        //初始化核心数据,加载“UI窗体路径”到集合中。
        public void Awake()
        {
            _dicAllUiForms = new Dictionary<string, BaseUiFrame>();
            _dicCurrentShowUiForms = new Dictionary<string, BaseUiFrame>();
            _dicFormsPaths = new Dictionary<UiWind, string>();
            _staCurrentUiForms = new Stack<BaseUiFrame>();
            //初始化加载(根UI窗体)Canvas预设
            InitRootCanvasLoading();
            //得到UI根节点、全屏节点、固定节点、弹出节点
            CanvasTrans = GameObject.FindGameObjectWithTag("Canvas").transform;
            _normalTrans = CanvasTrans.transform.Find("Normal");
            _fixedTrans = CanvasTrans.transform.Find("Fixed");
            _popUpTrans = CanvasTrans.transform.Find("PopUp");
            _uiScriptsTrans = CanvasTrans.transform.Find("Scripts");
    
            //把本脚本作为“根UI窗体”的子节点。
            gameObject.transform.SetParent(_uiScriptsTrans, false);
    
            DontDestroyOnLoad(CanvasTrans);//"根UI窗体"在场景转换的时候,不允许销毁
            //初始化“UI窗体预设”路径数据
            InitUiFormsPathData();
        }
        /// <summary>
        /// 初始化“UI窗体预设”路径数据
        /// </summary>
        private void InitUiFormsPathData()
        {
            TextAsset textAsset = Resources.Load<TextAsset>("ResCfgs/UiWind");
            if (!textAsset)
            {
                Debug.Log("Xml file:" + "ResCfgs / UiWind" + "not exist");
            }
            else
            {
                XmlDocument xmlDocument = new XmlDocument();
                xmlDocument.LoadXml(textAsset.text);
                XmlNodeList xmlNodeList = xmlDocument.SelectSingleNode("root")?.ChildNodes;
                if (xmlNodeList != null)
                    for (int i = 0; i < xmlNodeList.Count; i++)
                    {
                        XmlElement xmlElement = (XmlElement)xmlNodeList[i];
                        if (xmlElement != null && xmlElement.GetAttributeNode("ID") == null)
                        {
                            continue;
                        }
                        int id = Convert.ToInt32(xmlElement?.GetAttributeNode("ID")?.InnerText);
                        UiWindCfg uiWindCfgData = new UiWindCfg
                        {
                            Id = id,
                        };
                        foreach (XmlElement element in xmlNodeList[i].ChildNodes)
                        {
                            switch (element.Name)
                            {
                                case "key":
                                    uiWindCfgData.Key = (UiWind)Enum.Parse(typeof(UiWind), element.InnerText);
                                    break;
                                case "path":
                                    uiWindCfgData.Value = element.InnerText;
                                    break;
                            }
                        }
                        _dicFormsPaths.Add(uiWindCfgData.Key, uiWindCfgData.Value);
                    }
            }
        }
        //初始化加载(根UI窗体)Canvas预设
        private void InitRootCanvasLoading()
        {
            ResourcesMgr.Instance().LoadAsset("ResUi/Canvas", false);
        }
    
        /// <summary>
        /// 显示(打开)UI窗体
        /// 功能:
        /// 1: 根据UI窗体的名称,加载到“所有UI窗体”缓存集合中
        /// 2: 根据不同的UI窗体的“显示模式”,分别作不同的加载处理
        /// </summary>
        /// <param name="uiFormName">UI窗体预设的名称</param>
        public void ShowUiForms(string uiFormName)
        {
            //参数的检查
            if (uiFormName == null) return;
            //根据UI窗体的名称,加载到“所有UI窗体”缓存集合中
            var baseUiForms = LoadFormsToAllUiFormsCatch(uiFormName);
    
            if (baseUiForms == null) return;
            //根据不同的UI窗体的显示模式,分别作不同的加载处理
            switch (baseUiForms.CurrentUiType.UiShowMode)
            {
                case UiShowMode.Normal:                 //“普通显示”窗口模式
                    //把当前窗体加载到“当前窗体”集合中。
                    LoadUiToCurrentCache(uiFormName);
                    break;
                case UiShowMode.ReverseChange:          //需要“反向切换”窗口模式
                    PushUiFormToStack(uiFormName);
                    break;
                case UiShowMode.HideOther:              //“隐藏其他”窗口模式
                    EnterUiFormsAndHideOther(uiFormName);
                    break;
            }
        }
    
        /// <summary>
        /// 关闭(返回上一个)窗体
        /// </summary>
        /// <param name="uiFormName"></param>
        public void CloseUiForms(string uiFormName)
        {
            //参数检查
            //“所有UI窗体”集合中,如果没有记录,则直接返回
            if (uiFormName == null)
            {
                return;
            }
            _dicAllUiForms.TryGetValue(uiFormName, out var baseUiForm);
            if (baseUiForm != null)
            {
                switch (baseUiForm.CurrentUiType.UiShowMode)
                {
                    case UiShowMode.Normal:
                        //普通窗体的关闭
                        ExitUiForms(uiFormName);
                        break;
                    case UiShowMode.ReverseChange:
                        //反向切换窗体的关闭
                        PopUiForms();
                        break;
                    case UiShowMode.HideOther:
                        //隐藏其他窗体关闭
                        ExitUiFormsAndDisplayOther(uiFormName);
                        break;
                }
            }
            if (baseUiForm == null)
            {
                return;
            }
            //根据窗体不同的显示类型,分别作不同的关闭处理
    
        }
        /// <summary>
        /// 根据UI窗体的名称,加载到“所有UI窗体”缓存集合中
        /// 功能: 检查“所有UI窗体”集合中,是否已经加载过,否则才加载。
        /// </summary>
        /// <param name="uiFormsName">UI窗体(预设)的名称</param>
        private BaseUiFrame LoadFormsToAllUiFormsCatch(string uiFormsName)
        {
            _dicAllUiForms.TryGetValue(uiFormsName, out BaseUiFrame baseUiResult);
            if (baseUiResult == null)
            {
                //加载指定名称的“UI窗体”
                baseUiResult = LoadUiForm(uiFormsName);
            }
    
            return baseUiResult;
        }
        /// <summary>
        /// 加载指定名称的“UI窗体”
        /// 功能:
        ///    1:根据“UI窗体名称”,加载预设克隆体。
        ///    2:根据不同预设克隆体中带的脚本中不同的“位置信息”,加载到“根窗体”下不同的节点。
        ///    3:隐藏刚创建的UI克隆体。
        ///    4:把克隆体,加入到“所有UI窗体”(缓存)集合中。
        /// </summary>
        /// <param name="uiFormName">UI窗体名称</param>
        private BaseUiFrame LoadUiForm(string uiFormName)
        {
            GameObject cloneUiPrefabs = null;             //创建的UI克隆体预设
            UiWind uiWind = (UiWind)Enum.Parse(typeof(UiWind), uiFormName);
            //根据UI窗体名称,得到对应的加载路径
            _dicFormsPaths.TryGetValue(uiWind, out string strUiFormPaths);
            //根据“UI窗体名称”,加载“预设克隆体”
            if (strUiFormPaths != null)
            {
                cloneUiPrefabs = ResourcesMgr.Instance().LoadAsset(strUiFormPaths, false);
            }
            //设置“UI克隆体”的父节点(根据克隆体中带的脚本中不同的“位置信息”)
            if (CanvasTrans != null && cloneUiPrefabs != null)
            {
                BaseUiFrame baseUiForm = cloneUiPrefabs.GetComponent<BaseUiFrame>();   //窗体基类
                if (baseUiForm == null)
                {
                    Debug.Log("baseUiForm==null! ,请先确认窗体预设对象上是否加载了baseUIForm的子类脚本! 参数 uiFormName=" + uiFormName);
                    return null;
                }
                switch (baseUiForm.CurrentUiType.UiWindType)
                {
                    case UiWindType.Normal:                 //普通窗体节点
                        cloneUiPrefabs.transform.SetParent(_normalTrans, false);
                        break;
                    case UiWindType.Fixed:                  //固定窗体节点
                        cloneUiPrefabs.transform.SetParent(_fixedTrans, false);
                        break;
                    case UiWindType.PopUp:                  //弹出窗体节点
                        cloneUiPrefabs.transform.SetParent(_popUpTrans, false);
                        break;
                }
                //设置隐藏
                cloneUiPrefabs.SetActive(false);
                //把克隆体,加入到“所有UI窗体”(缓存)集合中。
                _dicAllUiForms.Add(uiFormName, baseUiForm);
                return baseUiForm;
            }
            else
            {
                Debug.Log("_TraCanvasTransfrom==null Or goCloneUIPrefabs==null!! ,Plese Check!, 参数uiFormName=" + uiFormName);
            }
    
            Debug.Log("出现不可以预估的错误,请检查,参数 uiFormName=" + uiFormName);
            return null;
        }
        /// <summary>
        /// 把当前窗体加载到“当前窗体”集合中
        /// </summary>
        /// <param name="uiFormName">窗体预设的名称</param>
        private void LoadUiToCurrentCache(string uiFormName)
        {
            //如果“正在显示”的集合中,存在整个UI窗体,则直接返回
            _dicCurrentShowUiForms.TryGetValue(uiFormName, out BaseUiFrame baseWind);
            if (baseWind != null) return;
            //把当前窗体,加载到“正在显示”集合中
            _dicAllUiForms.TryGetValue(uiFormName, out var baseUiWindAllCache);
            if (baseUiWindAllCache != null)
            {
                _dicCurrentShowUiForms.Add(uiFormName, baseUiWindAllCache);
                baseUiWindAllCache.Display();           //显示当前窗体
            }
        }
    
        /// <summary>
        /// UI窗体入栈
        /// </summary>
        /// <param name="uiFormName">窗体的名称</param>
        private void PushUiFormToStack(string uiFormName)
        {
            //判断“栈”集合中,是否有其他的窗体,有则“冻结”处理。
            if (_staCurrentUiForms.Count > 0)
            {
                BaseUiFrame topUiForm = _staCurrentUiForms.Peek();
                //栈顶元素作冻结处理
                topUiForm.Freeze();
            }
            //判断“UI所有窗体”集合是否有指定的UI窗体,有则处理。
            _dicAllUiForms.TryGetValue(uiFormName, out BaseUiFrame baseWind);
            if (baseWind != null)
            {
                baseWind.Display();//当前窗口显示状态
                //把指定的UI窗体,入栈操作。
                _staCurrentUiForms.Push(baseWind);
            }
            else
            {
                Debug.Log("baseUIForm==null,Please Check, 参数 uiFormName=" + uiFormName);
            }
        }
        /// <summary>
        /// 退出指定UI窗体
        /// </summary>
        /// <param name="strUiFormName"></param>
        private void ExitUiForms(string strUiFormName)
        {
            //"正在显示集合"中如果没有记录,则直接返回。
            _dicCurrentShowUiForms.TryGetValue(strUiFormName, out BaseUiFrame baseUiForm);
            if (baseUiForm == null) return;
            //指定窗体,标记为“隐藏状态”,且从"正在显示集合"中移除。
            baseUiForm.Hiding();
            _dicCurrentShowUiForms.Remove(strUiFormName);
        }
    
        //(“反向切换”属性)窗体的出栈逻辑
        private void PopUiForms()
        {
            if (_staCurrentUiForms.Count >= 2)
            {
                //出栈处理
                BaseUiFrame topUiForms = _staCurrentUiForms.Pop();
                //做隐藏处理
                topUiForms.Hiding();
                //出栈后,下一个窗体做“重新显示”处理。
                BaseUiFrame nextUiForms = _staCurrentUiForms.Peek();
                nextUiForms.Redisplay();
            }
            else if (_staCurrentUiForms.Count == 1)
            {
                //出栈处理
                BaseUiFrame topUiForms = _staCurrentUiForms.Pop();
                //做隐藏处理
                topUiForms.Hiding();
            }
        }
        /// <summary>
        /// (“隐藏其他”属性)打开窗体,且隐藏其他窗体
        /// </summary>
        /// <param name="strUiName">打开的指定窗体名称</param>
        private void EnterUiFormsAndHideOther(string strUiName)
        {
            //参数检查
            if (strUiName == null) return;
    
            _dicCurrentShowUiForms.TryGetValue(strUiName, out BaseUiFrame baseWind);
            if (baseWind != null) return;
    
            //把“正在显示集合”与“栈集合”中所有窗体都隐藏。
            foreach (BaseUiFrame baseUi in _dicCurrentShowUiForms.Values)
            {
                baseUi.Hiding();
            }
            foreach (BaseUiFrame staUi in _staCurrentUiForms)
            {
                staUi.Hiding();
            }
    
            //把当前窗体加入到“正在显示窗体”集合中,且做显示处理。
            _dicAllUiForms.TryGetValue(strUiName, out BaseUiFrame baseUiWindAllCache);
            if (baseUiWindAllCache != null)
            {
                _dicCurrentShowUiForms.Add(strUiName, baseUiWindAllCache);
                //窗体显示
                baseUiWindAllCache.Display();
            }
        }
    
        /// <summary>
        /// (“隐藏其他”属性)关闭窗体,且显示其他窗体
        /// </summary>
        /// <param name="strUiName">打开的指定窗体名称</param>
        private void ExitUiFormsAndDisplayOther(string strUiName)
        {
            //参数检查
            if (strUiName == null) return;
    
            _dicCurrentShowUiForms.TryGetValue(strUiName, out var baseUiForm);
            if (baseUiForm == null) return;
    
            //当前窗体隐藏状态,且“正在显示”集合中,移除本窗体
            baseUiForm.Hiding();
            _dicCurrentShowUiForms.Remove(strUiName);
    
            //把“正在显示集合”与“栈集合”中所有窗体都定义重新显示状态。
            foreach (BaseUiFrame baseUi in _dicCurrentShowUiForms.Values)
            {
                baseUi.Redisplay();
            }
            foreach (BaseUiFrame staUi in _staCurrentUiForms)
            {
                staUi.Redisplay();
            }
        }
    }

    以上代码解释:

    一:上述代码中重要字段的解释如下:
        1:  “_DicFormsPaths” 表示“UI窗体预设路径”集合,负责缓存所有UI窗体预设的名称与对应资源路径的关系。
      2: “ _DicALLUIForms” 表示“所有UI窗体”集合,负责缓存已经加载过的所有UI窗体名称以及与之对应的UI窗体。
      3: “_DicCurrentShowUIForms”表示“当前正在显示”集合,负责控制正在显示UI窗体的内部逻辑。
      4: UI管理器脚本中的“_TraCanvasTransfrom”、“_TraNormal”、“_TraFixed”、“_TraPopUp”、“_TraUIScripts”,分别表示Unity层级视图中的根结点、普通节点、固定节点、弹出节点、管理脚本节点,这些节点是加载UI窗体的不同类型的父节点,用于各种UI窗体的管理工作。

    二:上述代码中重要方法的解释如下:

      1: ShowUIForms()  是外部程序调用本框架的对外公共方法,负责加载、缓存、打开与显示制定窗体名称的UI窗体预设。
      2: LoadFormsToAllUIFormsCatch() 是根据UI窗体的名称,加载到“所有UI窗体”缓存集合中。
      3: LoadUIToCurrentCache() 是把当前窗体加载到“当前窗体”集合中。

        1: UIManager.cs  中定义的新的字段 ,“_StaCurrentUIForms” 就是一个“栈”数据类型,用于维护一种后进先出的数据结构。常见的方法如下:

          C#语言中提供 Stack<T> 泛型集合,来直接实现这种结构。 
    常用属性与方法:

    •  Count 属性  查询栈内元素数量
    •  Push()      压栈
    •  Pop()       出栈
    •  Peek()      查询栈顶元素
    •  GetEnumerator() 遍历栈中所有元素

    UIManager.cs 中的“ShowUIForms()”方法中,就是专门处理“反向切换”与“隐藏其他”窗体特性的实现方法。

    项目源码工程下载地址:https://github.com/AnderTroy/UIFrameWork

    展开全文
  • Unity3D】UGUI实现层级菜单

    千次阅读 2020-05-25 08:25:41
    一、前言 层级菜单在Unity中用到的并不多,主要是做分类的时候用的比较多,今天就给大家分享几个层级代码,扩充一下,写成插件也是不错的。 首先看一下效果吧: ...【Unity3D】UGUI实现层级菜单 第一种实...

    一、前言

    层级菜单在Unity中用到的并不多,主要是做分类的时候用的比较多,今天就给大家分享几个层级代码,扩充一下,写成插件也是不错的。

    首先看一下效果吧:
    1.
    在这里插入图片描述
    2.
    在这里插入图片描述
    3.
    在这里插入图片描述
    在这里插入图片描述
    4.
    在这里插入图片描述
    5.
    在这里插入图片描述

    二、资源下载

    源文件:
    源文件下载
    Github下载地址:
    LayerMenu.unitypackage

    三、正文

    第一种实现效果

    在这里插入图片描述
    实现原理:这个是用系统自带的UGUI Scroll View组件,脚本控制创建父物体,父物体身上挂载有初始化子物体的脚本
    优缺点:
    优点是实现简单,不需要多与的插件,代码量也不大,控制比较方便
    缺点是只能实现两个层级的显示
    实现过程:
    1、新建一个Scrpll View
    在这里插入图片描述
    2、制作预制体
    界面就这么设计就行:
    在这里插入图片描述

    名字改一下
    Content:父节点容器
    ParentMenu:父节点
    TextParent:父节点文本
    ChildMenu:子节点容器
    item:子节点
    TextChild:子节点文本
    在这里插入图片描述
    然后将父节点改名字叫parentMenu,做成预制体:
    在这里插入图片描述
    预制体放到Resources文件夹中:
    在这里插入图片描述
    将子物体也制作成预制体:
    在这里插入图片描述
    3、编写脚本ParentMenu.cs
    这个脚本主要是作用是创建子物体:

    using System.Collections;
    using UnityEngine;
    using UnityEngine.UI;
    
    public class ParentMenu : MonoBehaviour
    {
        private GameObject childMenu;//子菜单的parent
        private RectTransform[] childs;//所有子菜单的rect
        private RectTransform itemRect;//子菜单的prefab
        private Vector3 offset;//单个子菜单的高度
        private int count;//子菜单的个数
        public bool isOpening { get; private set; }//父菜单是否展开
        public bool isCanClick { get; set; }//父菜单是否可以点击
    
        public void Init(RectTransform rect, int count)
        {
            //找到子节点
            childMenu = transform.Find("childMenu").gameObject;
            itemRect = rect;
            this.count = count;
            childs = new RectTransform[this.count];
            offset = new Vector3(0, itemRect.rect.height);
            for (int i = 0; i < this.count; i++)
            {
                childs[i] = Instantiate(itemRect, childMenu.transform);
            }
            childMenu.gameObject.SetActive(false);
            isOpening = false;
            isCanClick = true;
            GetComponent<Button>().onClick.AddListener(OnButtonClick);
        }
    
        void OnButtonClick()
        {
            if (!isCanClick) return;
            if (!isOpening)
                StartCoroutine(ShowChildMenu());
            else
                StartCoroutine(HideChildMenu());
        }
    
        IEnumerator ShowChildMenu()
        {
            childMenu.gameObject.SetActive(true);
            for (int i = 0; i < count; i++)
            {
                childs[i].localPosition -= i * offset;
                yield return new WaitForSeconds(0.1f);
            }
            isCanClick = true;
            isOpening = true;
        }
    
        IEnumerator HideChildMenu()
        {
            for (int i = count - 1; i >= 0; i--)
            {
                childs[i].localPosition += i * offset;
                yield return new WaitForSeconds(0.1f);
            }
            childMenu.gameObject.SetActive(false);
            isCanClick = true;
            isOpening = false;
        }
    }
    
    

    4、编写脚本FoldableMenu.cs
    这个脚本主要是为了创建父物体,以及控制折叠菜单

    using System.Collections;
    using UnityEngine;
    using UnityEngine.UI;
    
    public class FoldableMenu : MonoBehaviour
    {
        private RectTransform content;//父物体的parent
        private TextAsset textAsset;//所有菜单信息
        private RectTransform parentRect;//父菜单的prefab
        private RectTransform[] parentArr;//所有父菜单的数组
        private RectTransform childRect;//子菜单的prefab
        private Vector3 parentOffset;//单个父菜单的高度
        private Vector3 childOffset;//单个父菜单的高度
        private int[] cntArr;//所有父菜单拥有的子菜单个数
    
        void Awake()
        {
            Init();
        }
    
        void Init()
        {
            //获取到父节点
            content = transform.Find("Viewport/Content").GetComponent<RectTransform>();
            //获取到menuinfo里面的信息  3 3 4 4 5 5
            textAsset = Resources.Load<TextAsset>("menuInfo");
            //获取到父物体 设置父物体的高度
            parentRect = Resources.Load<RectTransform>("parentMenu");
            parentOffset = new Vector3(0, parentRect.rect.height);
            //获取到子物体 设置子物体的高度
            childRect = Resources.Load<RectTransform>("item");
            childOffset = new Vector3(0, childRect.rect.height);
            //分割字符串 info = 3 3 4 4 5 5
            var info = textAsset.text.Split(',');//获取子菜单个数信息
            //数组的长度
            cntArr = new int[info.Length];
            //父菜单的数组
            parentArr = new RectTransform[info.Length];
            //初始化content高度 宽度不变 高度是所有父物体的总长
            content.sizeDelta = new Vector2(content.rect.width, parentArr.Length * parentRect.rect.height);
            //i = 6
            for (int i = 0; i < cntArr.Length; i++)
            {
                //创建服务器
                parentArr[i] = Instantiate(parentRect, content.transform);
                //坐标为 上一个父物体的宽度
                parentArr[i].localPosition -= i * parentOffset;
                //赋值
                cntArr[i] = int.Parse(info[i]);
                //父物体上面加载子物体 子物体数量为3 3 4 4 5 5
                parentArr[i].GetComponent<ParentMenu>().Init(childRect, cntArr[i]);
                int j = i;
                //父物体上面的button绑定事件
                parentArr[i].GetComponent<Button>().onClick.AddListener(() => { OnButtonClick(j); });
            }
        }
    
        void OnButtonClick(int i)
        {
            //如果iscanclick为false则return  因为已经点击过了 不能再点击了 除非升起来的时候将isCanClick改为true
            if (!parentArr[i].GetComponent<ParentMenu>().isCanClick)
                return;
            parentArr[i].GetComponent<ParentMenu>().isCanClick = false;
            //isopening 为true 执行 menuup 为flase执行menuDown
            if (!parentArr[i].GetComponent<ParentMenu>().isOpening)
                StartCoroutine(MenuDown(i));
            else
                StartCoroutine(MenuUp(i));
        }
    
        IEnumerator MenuDown(int index)
        {       
            for (int i = 0; i < cntArr[index]; i++)
            {
                //更新content高度
                content.sizeDelta = new Vector2(content.rect.width,
                    content.rect.height + childOffset.y);
                for (int j = index + 1; j < parentArr.Length; j++)
                {
                    parentArr[j].localPosition -= childOffset;
                }
                yield return new WaitForSeconds(0.1f);
            }     
        }
    
        IEnumerator MenuUp(int index)
        {
            for (int i = 0; i < cntArr[index]; i++)
            {
                //更新content高度
                content.sizeDelta = new Vector2(content.rect.width,
                    content.rect.height - childOffset.y);
                for (int j = index + 1; j < parentArr.Length; j++)
                {
                    parentArr[j].localPosition += childOffset;
                }
                yield return new WaitForSeconds(0.1f);
            }
        }
    }
    
    

    将这个脚本挂载到Scroll View上面:
    在这里插入图片描述
    运行,搞定!

    第二种实现效果

    在这里插入图片描述
    实现原理:这个也是用UGUI做的,不一样的是不需要容器组件,主要是寻找父节点,然后保存父节点的信息,下一个节点以父节点为目标进行偏移,或者以父节点为目标做子节点
    优缺点:
    优点是代码清晰,可扩展性强,不需要设计UGUI
    缺点结构比较简单,没有实现多层级的功能
    实现过程:
    1、创建预制体
    在这里插入图片描述
    结构比较简单,两个Image,箭头的图片带Button组件(可以下拉和合并)
    然后将预制体放到Resources文件夹中:
    在这里插入图片描述
    2、编写PublicData.cs脚本
    一个数据类

    using UnityEngine;
    
    public class PublicData
    {
        public static Transform parent;
        public static Vector2 currentPosition;
    }
    
    

    3、编写脚本Options_Triangle.cs
    初始化子节点的函数

    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.UI;
    
    public class Options_Triangle : MonoBehaviour
    {
        public Button triangle;
        public Image content;
        public List<Transform> children = new List<Transform>();
        void Start()
        {
            triangle.onClick.AddListener(() =>
            {
                if (triangle.transform.eulerAngles.z == 270)
                {
                    triangle.transform.eulerAngles = Vector3.zero;
                    for (int i = 0; i < children.Count; i++)
                    {
                        children[i].gameObject.SetActive(false);
                    }
                }
                else
                {
                    triangle.transform.eulerAngles = new Vector3(0, 0, -90);
                    for (int i = 0; i < children.Count; i++)
                    {
                        children[i].gameObject.SetActive(true);
                    }
                }
            });
    
        }
    }
    
    

    4、编写ClickEvent.cs
    这是一个层级菜单的编辑功能的脚本

    using UnityEngine;
    using UnityEngine.UI;
    
    public class ClickEvent : MonoBehaviour
    {
        public Button add_options_t;
        public Button add_options;
        public Transform canvasParent;
    
        void Start()
        {
            add_options_t.onClick.AddListener(() =>
            {
                if (PublicData.parent == null)
                {
                    PublicData.parent = Instantiate(Resources.Load<Transform>("Options_triangle"));
                    PublicData.parent.transform.SetParent(canvasParent, false);
                    PublicData.currentPosition = new Vector2(PublicData.parent.position.x, PublicData.parent.position.y);
                }
                else
                {
                    Transform option = Instantiate(Resources.Load<Transform>("Options_triangle"));
                    option.transform.SetParent(PublicData.parent, false);
                    option.transform.position = new Vector3(PublicData.currentPosition.x + 30, PublicData.currentPosition.y - 32, 0);
                    PublicData.parent.GetComponent<Options_Triangle>().triangle.transform.eulerAngles = new Vector3(0, 0, -90);
                    PublicData.parent.GetComponent<Options_Triangle>().children.Add(option);
                    PublicData.parent = option;
                    PublicData.currentPosition = new Vector2(PublicData.parent.position.x, PublicData.parent.position.y);
                }
    
            });
            add_options.onClick.AddListener(() =>
            {
                if (PublicData.parent == null)
                {
                    return;
                }
                PublicData.parent.GetComponent<Options_Triangle>().triangle.transform.eulerAngles = new Vector3(0, 0, -90);
                Transform content = Instantiate(Resources.Load<Transform>("Options"));
                content.transform.SetParent(PublicData.parent, false);
                content.transform.position = new Vector3(PublicData.currentPosition.x + 16, PublicData.currentPosition.y - 32, 0);
                PublicData.parent.GetComponent<Options_Triangle>().children.Add(content);
                PublicData.currentPosition = new Vector2(content.position.x - 16, content.position.y);
    
            });
        }
    }
    
    

    OK。将ClickEvent脚本挂载在场景中的任一物体身上就可以了

    第三种实现效果

    在这里插入图片描述
    实现原理:这个也是用UGUI做的,比较有特点的地方是没有使用一行代码,使用VerticalLayoutGroup和ContentSizeFitter组件的自动排序功能和Button的OnClick组件控制子物体的显示与隐藏来实现层级菜单的功能。
    优缺点:
    优点是不需要代码控制,简单易用
    缺点是需要提前堆砌UI,工作量比较大,然后改动的时候耗费精力大
    实现过程:
    1、新建Scroll View
    在这里插入图片描述
    2、父节点
    在这里插入图片描述
    在这里插入图片描述
    父节点有一个Button和一个隐藏的Image

    Button
    在这里插入图片描述
    有两个功能
    在这里插入图片描述
    第一个就是显示跟自己位置坐标一样的子物体BtnSelecteStyle,因为BtnSelecteStyle是Button的子物体,所以BtnSelecteStyle就会挡住Button,为啥要挡住呢,因为还需要BtnSelecteStyle的OnClick将子节点收起来
    BtnSelecteStyle的OnClick挂载的功能:
    在这里插入图片描述
    第二个就是显示子节点的容器也就是ImgBtnParentLayout

    这个ImgBtnParentLayout就是子节点的容器
    在这里插入图片描述
    下面是几个子节点
    在这里插入图片描述

    OK。可以了

    第四种实现效果

    在这里插入图片描述
    实现原理:这个是用代码动态生成的,其中一个脚本主要用来创建父物体和子物体,以及父级关系,另一个脚本是设置位置,箭头变化,按钮功能的初始化
    优缺点:
    优点是代码清晰,结构明了,可以实现层级多级显示、多级目录的设置、树级菜单等
    缺点是没有判断最后一个节点的代码,最后一个节点无法设置图片,最后一个节点的功能没有添加
    实现过程:
    1、首先也是制作预制体
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    ArrowButton和ArrowButton2都是为了控制子节点的关闭和显示,不同的是ArrowButton是左边的小按钮,还有一个图片显示的功能,ArrowButton2是整体的按钮,不显示,但是点击整体都可以实现显示和隐藏子节点的功能

    资源:
    在这里述
    在这里插入图片描述
    在这里插入图片描述
    图片是白的,仔细看一下还是能看到的 - -,然后保存下载,放到项目中去

    2、编写脚本ItemPanelBase.cs
    这个脚本是设置节点的位置,以及箭头切换的脚本

    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.UI;
    
    public enum NameSort
    {
        Default,
        Active,
        Passive
    }
    public class ItemPanelBase : MonoBehaviour
    {
        private List<ItemPanelBase> childList;//子物体集合
        [HideInInspector]
        public Button downArrow;//下箭头按钮
        public Button downArrow2;//下箭头按钮
        public Sprite down, right, dot;
        public bool isOpen { get; set; }//子物体开启状态
        private Vector2 startSize;//起始大小
    
        NameSort m_NameSore = NameSort.Default;
        string m_Name;
    
        private void Awake()
        {
            childList = new List<ItemPanelBase>();
            downArrow = this.transform.Find("ContentPanel/ArrowButton").GetComponent<Button>();
            downArrow.onClick.AddListener(() =>
            {
                if (isOpen)
                {
                    CloseChild();
                    isOpen = false;
                }
                else
                {
                    OpenChild();
                    isOpen = true;
                }
            });
            downArrow2 = this.transform.Find("ContentPanel/ArrowButton2").GetComponent<Button>();
            downArrow2.onClick.AddListener(() =>
            {
                if (isOpen)
                {
                    CloseChild();
                    isOpen = false;
                }
                else
                {
                    OpenChild();
                    isOpen = true;
                }
            });
            startSize = this.GetComponent<RectTransform>().sizeDelta;
            isOpen = false;
        }
    
        //添加子物体到集合
        private void AddChild(ItemPanelBase parentItemPanelBase)
        {
            childList.Add(parentItemPanelBase);
            if (childList.Count >= 1)
            {
                downArrow.GetComponent<Image>().sprite = right;
            }
        }
    
        //设置当前对象的名字值
        public void SetName(string _name,NameSort m_sort)
        {
            m_Name = _name;
            m_NameSore = m_sort;
        }
    
        /// <summary>
        /// 设置父物体,父物体不为一级菜单
        /// </summary>
        /// <param name="parentItemPanelBase"></param>
        public void SetItemParent(ItemPanelBase parentItemPanelBase)
        {
            this.transform.parent = parentItemPanelBase.transform;
            parentItemPanelBase.AddChild(this);
            this.GetComponent<VerticalLayoutGroup>().padding = new RectOffset((int)parentItemPanelBase.downArrow.GetComponent<RectTransform>().sizeDelta.x, 0, 0, 0);
            if (parentItemPanelBase.isOpen)
            {
    
                this.GetComponent<ItemPanelBase>().AddParentSize((int)this.gameObject.GetComponent<RectTransform>().sizeDelta.y);
            }
            else
            {
                this.transform.gameObject.SetActive(false);
            }
        }
    
        /// <summary>
        /// 设置父物体,父物体为一级菜单
        /// </summary>
        /// <param name="tran"></param>
        public void SetBaseParent(Transform tran)
        {
            this.transform.parent = tran;
        }
    
        /// <summary>
        /// 填充Item数据
        /// </summary>
        /// <param name="_name">名字</param>
        public void InitPanelContent(string _name)
        {
            transform.Find("ContentPanel/Text").GetComponent<Text>().text = _name;
        }
    
        /// <summary>
        /// 增加一个子物体后更新Panel大小
        /// </summary>
        /// <param name="change"></param>
        public void UpdateRectTranSize(int change)
        {
            this.gameObject.GetComponent<RectTransform>().sizeDelta = new Vector2(startSize.x, this.gameObject.GetComponent<RectTransform>().sizeDelta.y + change);
        }
        /// <summary>
        /// 增加父物体高度
        /// </summary>
        /// <param name="parentItem"></param>
        /// <param name="change"></param>
        public void AddParentSize(int change)
        {
            if (this.transform.parent.GetComponent<ItemPanelBase>() != null)
            {
                this.transform.parent.GetComponent<ItemPanelBase>().UpdateRectTranSize(change);
                this.transform.parent.GetComponent<ItemPanelBase>().AddParentSize(change);
            }
        }
    
        /// <summary>
        /// 关闭子物体列表
        /// </summary>
        public void CloseChild()
        {
            if (childList.Count == 0) return;
            foreach (ItemPanelBase child in childList)
            {
                child.gameObject.SetActive(false);
                child.GetComponent<ItemPanelBase>().AddParentSize(-(int)child.gameObject.GetComponent<RectTransform>().sizeDelta.y);
            }
            downArrow.GetComponent<Image>().sprite = right;
        }
    
        /// <summary>
        /// 打开子物体列表
        /// </summary>
        public void OpenChild()
        {
            if (childList.Count == 0)
            {
                if (m_Name == "125K读卡器" && m_NameSore == NameSort.Active)
                {
                    Debug.Log("125k设备");
                }
                return;
            }
    
            foreach (ItemPanelBase child in childList)
            {
                child.gameObject.SetActive(true);
                child.GetComponent<ItemPanelBase>().AddParentSize((int)child.gameObject.GetComponent<RectTransform>().sizeDelta.y);
            }
            downArrow.GetComponent<Image>().sprite = down;
    
        }
    }
    
    

    3、编写脚本PullDownList.cs
    这个就是创建层级菜单的脚本:
    比较繁琐

    using System.Collections.Generic;
    using UnityEngine;
    
    public class PullDownList : MonoBehaviour
    {
        private List<GameObject> itemPanelList;
        public GameObject itemPanel;
    
        private void Awake()
        {
            itemPanelList = new List<GameObject>();
        }
    
        void Start()
        {
            for (int i = 0; i < 27; i++)
            {
                //一级菜单
                if(i == 0)
                {
                    GameObject newItemPanel = Instantiate(itemPanel);
                    itemPanelList.Add(newItemPanel);
                    newItemPanel.GetComponent<ItemPanelBase>().SetBaseParent(this.transform);
                    newItemPanel.GetComponent<ItemPanelBase>().InitPanelContent("层级一");
                }
                //二级菜单
                if (i == 1)
                {
                    GameObject newItemPanel2 = Instantiate(itemPanel);
                    itemPanelList.Add(newItemPanel2);
                    newItemPanel2.GetComponent<ItemPanelBase>().SetItemParent(itemPanelList[0].GetComponent<ItemPanelBase>());
                    newItemPanel2.GetComponent<ItemPanelBase>().InitPanelContent("层级二");
                }
                else if (i == 2)
                {
                    GameObject newItemPanel2 = Instantiate(itemPanel);
                    itemPanelList.Add(newItemPanel2);
                    newItemPanel2.GetComponent<ItemPanelBase>().SetItemParent(itemPanelList[0].GetComponent<ItemPanelBase>());
                    newItemPanel2.GetComponent<ItemPanelBase>().InitPanelContent("层级二");
                }
                //三级菜单
                else if (i == 3)
                {
                    GameObject newItemPanel2 = Instantiate(itemPanel);
                    itemPanelList.Add(newItemPanel2);
                    newItemPanel2.GetComponent<ItemPanelBase>().SetItemParent(itemPanelList[1].GetComponent<ItemPanelBase>());
                    newItemPanel2.GetComponent<ItemPanelBase>().InitPanelContent("层级三");
                }
                else if (i == 4)
                {
                    GameObject newItemPanel2 = Instantiate(itemPanel);
                    itemPanelList.Add(newItemPanel2);
                    newItemPanel2.GetComponent<ItemPanelBase>().SetItemParent(itemPanelList[1].GetComponent<ItemPanelBase>());
                    newItemPanel2.GetComponent<ItemPanelBase>().InitPanelContent("层级三");
                }
                else if (i == 5)
                {
                    GameObject newItemPanel2 = Instantiate(itemPanel);
                    itemPanelList.Add(newItemPanel2);
                    newItemPanel2.GetComponent<ItemPanelBase>().SetItemParent(itemPanelList[1].GetComponent<ItemPanelBase>());
                    newItemPanel2.GetComponent<ItemPanelBase>().InitPanelContent("层级三");
                }
                else if (i == 6)
                {
                    GameObject newItemPanel2 = Instantiate(itemPanel);
                    itemPanelList.Add(newItemPanel2);
                    newItemPanel2.GetComponent<ItemPanelBase>().SetItemParent(itemPanelList[1].GetComponent<ItemPanelBase>());
                    newItemPanel2.GetComponent<ItemPanelBase>().InitPanelContent("层级三");
                }
                else if (i == 7)
                {
                    GameObject newItemPanel2 = Instantiate(itemPanel);
                    itemPanelList.Add(newItemPanel2);
                    newItemPanel2.GetComponent<ItemPanelBase>().SetItemParent(itemPanelList[2].GetComponent<ItemPanelBase>());
                    newItemPanel2.GetComponent<ItemPanelBase>().InitPanelContent("层级三");
                }
                else if (i == 8)
                {
                    GameObject newItemPanel2 = Instantiate(itemPanel);
                    itemPanelList.Add(newItemPanel2);
                    newItemPanel2.GetComponent<ItemPanelBase>().SetItemParent(itemPanelList[2].GetComponent<ItemPanelBase>());
                    newItemPanel2.GetComponent<ItemPanelBase>().InitPanelContent("层级三");
                }
                else if (i == 9)
                {
                    GameObject newItemPanel2 = Instantiate(itemPanel);
                    itemPanelList.Add(newItemPanel2);
                    newItemPanel2.GetComponent<ItemPanelBase>().SetItemParent(itemPanelList[2].GetComponent<ItemPanelBase>());
                    newItemPanel2.GetComponent<ItemPanelBase>().InitPanelContent("层级三");
                }
                else if (i == 10)
                {
                    GameObject newItemPanel2 = Instantiate(itemPanel);
                    itemPanelList.Add(newItemPanel2);
                    newItemPanel2.GetComponent<ItemPanelBase>().SetItemParent(itemPanelList[2].GetComponent<ItemPanelBase>());
                    newItemPanel2.GetComponent<ItemPanelBase>().InitPanelContent("层级三");
                }
                //四级菜单
                else if (i == 11)
                {
                    GameObject newItemPanel2 = Instantiate(itemPanel);
                    itemPanelList.Add(newItemPanel2);
                    newItemPanel2.GetComponent<ItemPanelBase>().SetItemParent(itemPanelList[3].GetComponent<ItemPanelBase>());
                    newItemPanel2.GetComponent<ItemPanelBase>().InitPanelContent("层级四");
                    newItemPanel2.GetComponent<ItemPanelBase>().SetName("层级四", NameSort.Active);
                }
                else if (i == 12)
                {
                    GameObject newItemPanel2 = Instantiate(itemPanel);
                    itemPanelList.Add(newItemPanel2);
                    newItemPanel2.GetComponent<ItemPanelBase>().SetItemParent(itemPanelList[3].GetComponent<ItemPanelBase>());
                    newItemPanel2.GetComponent<ItemPanelBase>().InitPanelContent("层级四");
                    newItemPanel2.GetComponent<ItemPanelBase>().SetName("层级四", NameSort.Active);
                }
                else if (i == 13)
                {
                    GameObject newItemPanel2 = Instantiate(itemPanel);
                    itemPanelList.Add(newItemPanel2);
                    newItemPanel2.GetComponent<ItemPanelBase>().SetItemParent(itemPanelList[4].GetComponent<ItemPanelBase>());
                    newItemPanel2.GetComponent<ItemPanelBase>().InitPanelContent("层级四");
                    newItemPanel2.GetComponent<ItemPanelBase>().SetName("层级四", NameSort.Active);
                }
                else if (i == 14)
                {
                    GameObject newItemPanel2 = Instantiate(itemPanel);
                    itemPanelList.Add(newItemPanel2);
                    newItemPanel2.GetComponent<ItemPanelBase>().SetItemParent(itemPanelList[4].GetComponent<ItemPanelBase>());
                    newItemPanel2.GetComponent<ItemPanelBase>().InitPanelContent("层级四");
                    newItemPanel2.GetComponent<ItemPanelBase>().SetName("层级四", NameSort.Active);
                }
                else if (i == 15)
                {
                    GameObject newItemPanel2 = Instantiate(itemPanel);
                    itemPanelList.Add(newItemPanel2);
                    newItemPanel2.GetComponent<ItemPanelBase>().SetItemParent(itemPanelList[5].GetComponent<ItemPanelBase>());
                    newItemPanel2.GetComponent<ItemPanelBase>().InitPanelContent("层级四");
                    newItemPanel2.GetComponent<ItemPanelBase>().SetName("层级四", NameSort.Active);
                }
                else if (i == 16)
                {
                    GameObject newItemPanel2 = Instantiate(itemPanel);
                    itemPanelList.Add(newItemPanel2);
                    newItemPanel2.GetComponent<ItemPanelBase>().SetItemParent(itemPanelList[5].GetComponent<ItemPanelBase>());
                    newItemPanel2.GetComponent<ItemPanelBase>().InitPanelContent("层级四");
                    newItemPanel2.GetComponent<ItemPanelBase>().SetName("层级四", NameSort.Active);
                }
                else if (i == 17)
                {
                    GameObject newItemPanel2 = Instantiate(itemPanel);
                    itemPanelList.Add(newItemPanel2);
                    newItemPanel2.GetComponent<ItemPanelBase>().SetItemParent(itemPanelList[6].GetComponent<ItemPanelBase>());
                    newItemPanel2.GetComponent<ItemPanelBase>().InitPanelContent("层级四");
                    newItemPanel2.GetComponent<ItemPanelBase>().SetName("层级四", NameSort.Active);
                }
                else if (i == 18)
                {
                    GameObject newItemPanel2 = Instantiate(itemPanel);
                    itemPanelList.Add(newItemPanel2);
                    newItemPanel2.GetComponent<ItemPanelBase>().SetItemParent(itemPanelList[6].GetComponent<ItemPanelBase>());
                    newItemPanel2.GetComponent<ItemPanelBase>().InitPanelContent("层级四");
                    newItemPanel2.GetComponent<ItemPanelBase>().SetName("层级四", NameSort.Active);
                }
                else if (i == 19)
                {
                    GameObject newItemPanel2 = Instantiate(itemPanel);
                    itemPanelList.Add(newItemPanel2);
                    newItemPanel2.GetComponent<ItemPanelBase>().SetItemParent(itemPanelList[7].GetComponent<ItemPanelBase>());
                    newItemPanel2.GetComponent<ItemPanelBase>().InitPanelContent("层级四");
                    newItemPanel2.GetComponent<ItemPanelBase>().SetName("层级四", NameSort.Passive);
                }
                else if (i == 20)
                {
                    GameObject newItemPanel2 = Instantiate(itemPanel);
                    itemPanelList.Add(newItemPanel2);
                    newItemPanel2.GetComponent<ItemPanelBase>().SetItemParent(itemPanelList[7].GetComponent<ItemPanelBase>());
                    newItemPanel2.GetComponent<ItemPanelBase>().InitPanelContent("层级四");
                    newItemPanel2.GetComponent<ItemPanelBase>().SetName("层级四", NameSort.Passive);
                }
                else if (i == 21)
                {
                    GameObject newItemPanel2 = Instantiate(itemPanel);
                    itemPanelList.Add(newItemPanel2);
                    newItemPanel2.GetComponent<ItemPanelBase>().SetItemParent(itemPanelList[8].GetComponent<ItemPanelBase>());
                    newItemPanel2.GetComponent<ItemPanelBase>().InitPanelContent("层级四");
                    newItemPanel2.GetComponent<ItemPanelBase>().SetName("层级四", NameSort.Passive);
                }
                else if (i == 22)
                {
                    GameObject newItemPanel2 = Instantiate(itemPanel);
                    itemPanelList.Add(newItemPanel2);
                    newItemPanel2.GetComponent<ItemPanelBase>().SetItemParent(itemPanelList[8].GetComponent<ItemPanelBase>());
                    newItemPanel2.GetComponent<ItemPanelBase>().InitPanelContent("层级四");
                    newItemPanel2.GetComponent<ItemPanelBase>().SetName("层级四", NameSort.Passive);
                }
                else if (i == 23)
                {
                    GameObject newItemPanel2 = Instantiate(itemPanel);
                    itemPanelList.Add(newItemPanel2);
                    newItemPanel2.GetComponent<ItemPanelBase>().SetItemParent(itemPanelList[9].GetComponent<ItemPanelBase>());
                    newItemPanel2.GetComponent<ItemPanelBase>().InitPanelContent("层级四");
                    newItemPanel2.GetComponent<ItemPanelBase>().SetName("层级四", NameSort.Passive);
                }
                else if (i == 24)
                {
                    GameObject newItemPanel2 = Instantiate(itemPanel);
                    itemPanelList.Add(newItemPanel2);
                    newItemPanel2.GetComponent<ItemPanelBase>().SetItemParent(itemPanelList[9].GetComponent<ItemPanelBase>());
                    newItemPanel2.GetComponent<ItemPanelBase>().InitPanelContent("层级四");
                    newItemPanel2.GetComponent<ItemPanelBase>().SetName("层级四", NameSort.Passive);
                }
                else if (i == 25)
                {
                    GameObject newItemPanel2 = Instantiate(itemPanel);
                    itemPanelList.Add(newItemPanel2);
                    newItemPanel2.GetComponent<ItemPanelBase>().SetItemParent(itemPanelList[10].GetComponent<ItemPanelBase>());
                    newItemPanel2.GetComponent<ItemPanelBase>().InitPanelContent("层级四");
                    newItemPanel2.GetComponent<ItemPanelBase>().SetName("层级四", NameSort.Passive);
                }
                else if (i == 26)
                {
                    GameObject newItemPanel2 = Instantiate(itemPanel);
                    itemPanelList.Add(newItemPanel2);
                    newItemPanel2.GetComponent<ItemPanelBase>().SetItemParent(itemPanelList[10].GetComponent<ItemPanelBase>());
                    newItemPanel2.GetComponent<ItemPanelBase>().InitPanelContent("层级四");
                    newItemPanel2.GetComponent<ItemPanelBase>().SetName("层级四", NameSort.Passive);
                }
            }
        }
    }
    
    

    将脚本挂载在Panel上面
    在这里插入图片描述

    OK,大功告成

    第五种实现效果

    在这里插入图片描述
    实现原理:这个是用UI硬堆砌起来的层级菜单,然后通过代码控制对象的隐藏和显示,即可实现层级菜单的折叠与下拉功能,主要用到GridLayoutGroup组件来排序与更新
    优缺点:
    优点是操作简单,代码也简单,不需要太多的理解,然后可以显示多级菜单,多级内容,以及最后一个节点的功能与图片的设置功能
    缺点是需要提前堆砌UI,可扩容性差,前期工作量大,然后后期修改工作量大,最重要的是我觉得这种实现方式蛮low的
    实现过程:
    1、显示制作UI
    Panel上面挂载GridLayoutGroup组件
    在这里插入图片描述
    制作UI
    在这里插入图片描述
    在这里插入图片描述
    UI的话就很简单,一个Button下面两个子物体一个text一个Image,text是显示内容,image是显示箭头

    这时候就有人问了,那子物体怎么办,子物体也是同样的结构在这里插入图片描述
    就是把image往后拉了一下

    三级菜单也一样:
    在这里插入图片描述
    再加一个一级菜单:
    在这里插入图片描述

    是不是so easy…哈哈哈 真的好low

    脚本功能就很简单
    一级菜单控制它往下的所有子节点的隐藏于显示
    在这里插入图片描述
    二级菜单控制它往下的所有子节点的隐藏于显示
    在这里插入图片描述

    以此类推。。。。

    2、编辑代码PullDown.cs

    using UnityEngine;
    using UnityEngine.UI;
    
    public class PullDown : MonoBehaviour
    {
        public Button m_Btn1;//一级菜单按钮
        public Button m_Btn2;//二级菜单按钮
        public Button m_Btn3;//三级菜单按钮
    
        bool m_is1;//全局参数 控制开关
        bool m_is2;//全局参数 控制开关
    
        void Start()
        {
            m_is1 = true;
            m_is2 = true;
            m_Btn1.onClick.AddListener(Btn1_Event);
            m_Btn2.onClick.AddListener(Btn2_Event);
        }
    
        public void Btn1_Event()
        {
            if (m_is1)
            {
                m_Btn2.gameObject.SetActive(false);
                m_Btn3.gameObject.SetActive(false);
                m_is1 = false;
            }
            else
            {
                m_Btn2.gameObject.SetActive(true);
                m_Btn3.gameObject.SetActive(true);
                m_is1 = true;
            }
        }
    
        public void Btn2_Event()
        {
            if (m_is2)
            {
                m_Btn3.gameObject.SetActive(false);
                m_is2 = false;
            }
            else
            {
                m_Btn3.gameObject.SetActive(true);
                m_is1 = true;
            }
        }
    }
    

    OK了,快去试试吧

    展开全文
  • Unity UI层级管理框架

    千次阅读 2017-11-07 22:15:56
    1.为什么要使用UI层级管理框架? 根据我个人写的UI层级总结出:在一般UI界面的 Canvas 下有多个Panel,每个Panel下又有很多个需要操作的控件(Image、Text、inputField、Button)等, 因此要去管理如此多的事件处理...

    1.为什么要使用UI层级管理框架?

    根据我个人写的UI层级总结出:在一般UI界面的 Canvas 下有多个Panel,每个Panel下又有很多个需要操作的控件(Image、Text、inputField、Button)等,

    因此要去管理如此多的事件处理非常繁琐,UI的层级结构刚好适合用一套框架来管理 :Canvas--->Panel--->(控件)

    例:



    第一步:给需要监听鼠标事件的控件添加监听脚本

    用一个脚本代替 控件在Inspector中留下的OnClick方法(例:Button的OnClick需要去在脚本中寻找相对应的方法,如果脚本多了,寻找方法困难,而且不利于扩展)

    因此我用了一个 OnTriggerEvent.cs 的脚本来执行相应的点击事件

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.EventSystems;
    
    public class OnTriggerEvent : EventTrigger {
        //通过委托事件让UIScene来分配事件
        public delegate void ClickListener();
        public event ClickListener onBeginDrag;
        public event ClickListener onDrag;
        public event ClickListener onEndDrag;
        public event ClickListener onPointerClick;
        public event ClickListener onPointerUp;
        ///UI的相关监听//鼠标进入
    
        public override void OnBeginDrag(PointerEventData eventdata)
        {
            if (onBeginDrag != null)
                onBeginDrag();
        }
        public override void OnDrag(PointerEventData eventdata)
        {
            if (onDrag != null)
                onDrag();
        }
    
        public override void OnEndDrag(PointerEventData eventdata)
        {
            if (onEndDrag != null)
                onEndDrag();
        }
        public override void OnPointerClick(PointerEventData eventdata)
        {
            if (onPointerClick != null)
                onPointerClick();
        }
    
        public override void OnPointerUp(PointerEventData eventdata)
        {
            if (onPointerUp != null)
                onPointerUp();
        }
    
    }
    

    第二步:给Panel添加脚本管理其下面需要监听的各个控件

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class UIscene : MonoBehaviour {
    
        /// <summary>
        /// 管理所有需要监听的子物体
        /// </summary>
        Dictionary<string,OnTriggerEvent> items=new Dictionary<string, OnTriggerEvent>();
    	// Use this for initialization
        public void Start () {
            Init();
    	}
    	
        /// <summary>
        /// 根据名字在字典当中获取子物体
        /// </summary>
        public OnTriggerEvent GetTrigger(string name)
        {
            if (items.ContainsKey(name))
                return items[name];
            return null;
        }
    
        public void Init()
        {
            //调用Find方法找到这个Panel下面带有OnTriggerEvent组件的所有子物体
            Find(transform);
        } 
    
        //递归查找子物体
        public void Find(Transform t)
        {
            OnTriggerEvent item = t.GetComponent<OnTriggerEvent>();
            if (item != null)
            {
                string name = item.gameObject.name;
                if (!items.ContainsKey(name))
                {
                    items.Add(name, item);
                }
            }
            for (int i = 0; i < t.childCount; i++)
            {
                Find(t.GetChild(i));
            }
        }
    }
    

    第三步:给Canvas添加管理各个Panel的脚本 UIManager.cs

    可以创建一个单例作为全局管理的类

    如:

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class UIPanel<T> : MonoBehaviour  where T:Component{
    
        private static T target;
        public static T Intance
        {
            get{ 
                target = GameObject.FindObjectOfType(typeof(T)) as T;
                if (target == null)
                {
                    GameObject go = new GameObject();
                    target = go.AddComponent<T>();
                }
                return target;
            }
        }
    }

    UIManager继承这个单例,方便使用


    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    //因为有些Panel的名称复杂,所以通过静态类、字段存一些Panel的名称,方便我们索引
    public class  UISceneName
    {
        public const string Panel_login = "Panel_Login";
        public const string Panel_ChooseAndEnter = "Panel_ChooseAndEnter";
        public const string Panel_CreateCharacter = "Panel_CreateCharacter";
        public const string Panel_Main = "Panel_Main";
        public const string Panel_Hero="Panel_Hero";
    //    public const string Panel_Register="Panel_Register";
    //    public const string 
    }
    
    public class UIManager : UIPanel<UIManager> {
        //创建一个字典来保存所有 Panel
        Dictionary<string,UIscene> scenes = new Dictionary<string,UIscene>();
        public void Init()
        {
            UIscene[] items = GameObject.FindObjectsOfType<UIscene>();
            for (int i = 0; i < items.Length; i++)
            {
                UIscene go =items[i];
                if (!scenes.ContainsKey(go.name))
                {
                    scenes.Add(go.name, go);
                    go.gameObject.SetActive(false);
                }
            }
        }
        //获取UIscene
        public UIscene GetUIscene(string name)
        {
            if (scenes.ContainsKey(name))
            {
                return scenes[name];
            }
            return null;
        }
        //对UIScene组件的Panel是否激活
        public void IsActive(string name,bool isActive)
        {
            GameObject go = scenes[name].gameObject;
            if (go == null)
            {
                Debug.Log("你要查找的物体丢");
                return;
            }
            go.gameObject.SetActive(isActive);
        }
        //展现登录界面的UIScene
        public void ShowUI()
        {
            IsActive(UISceneName.Panel_login, true);
        }
    }
    

    第四步:前面三步都是管理思想,并提供了很多接口,

    初始化整个UI

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class Init : MonoBehaviour {
    
    	// Use this for initialization
    	void Start () {
            UIManager.Intance.Init();
    
            UIManager.Intance.ShowUI();
    	}
    
    }
    

    第五步:外部的使用:通过继承UIScene的到整个Panel下面的所有需要监听的控件

    如:

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class ToMain : UIscene {
        private OnTriggerEvent Button_ToMain;
    	// Use this for initialization
    	void Start () {
            base.Start();
            Button_ToMain = GetTrigger("Button_ToMain");
            //给返回按钮绑定事件
            Button_ToMain.onPointerClick += HeroToMain;
    
    	}
    	//返回到主界面
        private void HeroToMain()
        {
            //关闭英雄界面
            UIManager.Intance.IsActive(UISceneName.Panel_Hero, false);
            //打开主界面
            UIManager.Intance.IsActive(UISceneName.Panel_Main, true);
        }
    }
    




    展开全文
  • 今天呢,我们来一起学习在Unity3D中使用EasyTouch虚拟摇杆来控制人物移动。尽管Unity3D内置了一个Joystick组件(其实就是一个GUITexture和一个Js脚本文件啦),可是博主在实际使用的时候发现这个内置的Joystick存在...
  • 本文介绍了Unity3D界面及游戏对象基本操作。
  • Unity NGUI和UGUI与模型、特效的层级关系

    万次阅读 多人点赞 2017-03-16 13:47:22
    2、unity渲染顺序控制方式 3、NGUI的控制 4、UGUI的控制 5、模型深度的控制 6、粒子特效深度控制 7、NGUI与模型和粒子特效穿插层级管理 8、UGUI与模型和粒子特效穿插层级管理写在前面这篇笔记是整理了之前做的...
  • Unity3d虚拟摇杆控制物体移动及旋转

    千次阅读 2018-02-26 21:15:06
    王者荣耀在现在看来可能已经成为不少人每天都会打开一...此篇博客就unity中如何实现利用UI(虚拟摇杆)控制物体的移动和旋转。罗马的路很多,这里只是其中一条,还望多提出问题,共同学习。首先,创建一个场景物体如...
  • Unity3d 技巧(1)-摄像机层级设置

    千次阅读 2017-07-23 14:05:26
    Unity3d技巧
  • Unity3D学习笔记(2)Unity3D与3DMax结合开发注意事项 单位:比例统一 在建模型前先设置好单位,在同一场景中会用到的模型的单位设置必须一样,模型与模型之间的比例要正确,和程序的导入单位一致,即便到程序...
  • Unity3D由Unity Technologies开发的一个让玩家轻松创建诸如三维视频游戏、建筑可视化、实时三维动画等类型互动内容的多平台的综合型游戏开发工具,是一个全面整合的专业游戏引擎。过去Unity支持三种编程语言:...
  • 本文依然是 Unity3D 的入门篇。作为 Unity3D 的入门读者,你可能希望迅速让你能在游戏中操作你的视角,或者让角色移动。 本文内容创建脚本最简代码 创建脚本 作为入门篇,可能需要讲一下如何创建脚本。按下图,在 ...
  • Unity3D基本入门及功能介绍

    万次阅读 多人点赞 2017-06-10 22:44:17
    本章主要是 Unity3D 的基础快速入门篇,通过本章的学习,能让初学者们快速的掌握和 应用 Unity3D 软件。 本章导读 本章无论对于 Unity3D 初学者、或是以前从事建模工作的设计师、又或者是从事过 3D 编程的人群,...
  • unity中的层级问题

    千次阅读 2020-06-30 09:47:19
    1.当UI界面中有粒子特效时,给UI添加Canvas,不会影响粒子的层级。 2.UI中添加贴图特效时,UI中的Canvas的Order in Layer层级不能太高,太高会使使贴图的特效看不见,如果硬是要这样,那只能修改粒子的渲染队列数值...
  • unity3d TreeView Control

    热门讨论 2020-07-30 23:32:37
    unity3d中显示树形控件,代码可使用在Editor上,也可以使用在游戏场景中作为UI使用。
  • 使用Unity3D的50个技巧:Unity3D最佳实践

    万次阅读 多人点赞 2014-09-01 09:58:21
    关于使用Unity3D的50条最佳实践。
  • 今天有个同事问我如何在程序中修改子物体的层级关系来改变遮挡关系,我给他敲出来一句代码。 UI的层级关系是通过渲染表现出来的,在canvas下的物体,排序越靠前的越先被渲染,这样一来就会 被后来渲染的遮挡。总结...
  • Unity3d场景快速烘焙【2019】

    万次阅读 多人点赞 2019-12-26 19:09:28
    很多刚刚接触Unity3d的童鞋花了大量的时间自学,可总是把握不好Unity3d的烘焙,刚从一个坑里爬出来,又陷入另一个新的坑,每次烘焙一个场景少则几个小时,多则几十个小时,机器总是处于假死机状态,半天看不到结果,...
  • Unity3D UGUI修改Layer层级让摄像机看不到UI 最近测试才发现,改变UI自身的Layer层是无法让摄像机屏蔽UI的,不过可以通过改变其 Canvas(画布)的Layer层来影响摄像机。 ...
  • Unity3D -- 简单的UI管理结构

    千次阅读 2016-03-02 10:14:25
    Unity3D – 简单的UI管理结构使用Unity3D做一些小游戏原型的时候,虽然我们想做UI,但是又没有美术的支持,我们使用一个简单的UI模板,所有不需要做UI的小游戏直接使用就行了,也不必要每次都要自己写。首先写一个...
1 2 3 4 5 ... 20
收藏数 2,104
精华内容 841
关键字:

unity3d层级控制