Unity3D利用VerticalLayoutGroup组件制作(折叠菜单、树状菜单)

本文介绍如何使用Unity3D的VerticalLayoutGroup组件动态生成树状菜单,通过脚本TreeMenuCheckBox实现目录结构的创建,配合UIMenuClick脚本处理菜单点击事件,无需额外设置即可实现菜单的折叠与展开。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

先上效果图在这里插入图片描述
本文利用VerticalLayoutGroup组件动态生成目录,目录数据由文件目录转换而成。无需其他设置。只需将TreeMenuCheckBox脚本挂载到某个对象上 然后指定属性面板的值既可。
在这里插入图片描述
直接上代码 一共两个脚本 TreeMenuCheckBox负责创建目录 UIMenuClick负责点击事件的监听

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
public class UIMenuClick : MonoBehaviour ,IPointerUpHandler,IPointerDownHandler,IDragHandler,IBeginDragHandler,IEndDragHandler{
    public GameObject Group = null;
    public GameObject Identifying = null;
    public System.Action<RectTransform> Refresh;
    private bool IsDrag = false;
    public void OnBeginDrag(PointerEventData eventData)
    {
        IsDrag = true;
        GetParntScrollRect().OnBeginDrag(eventData);
    }
    public void OnDrag(PointerEventData eventData)
    {
        GetParntScrollRect().OnDrag(eventData);
    }
    public void OnEndDrag(PointerEventData eventData)
    {
        IsDrag = false;
        GetParntScrollRect().OnEndDrag(eventData);
    }
    public void OnPointerDown(PointerEventData eventData)
    {
    }
    public void OnPointerUp(PointerEventData eventData)
    {
        if (IsDrag)
            return;
        if (Group!=null)
        {
            Group.SetActive(!Group.activeSelf);
            RefreshIdentifying();
            Refresh?.Invoke(Group.GetComponent<RectTransform>());
        }
    }
    public static UnityEngine.UI.ScrollRect scrollRect;
    public UnityEngine.UI.ScrollRect GetParntScrollRect()
    {
        if (scrollRect==null)
            scrollRect = GameObject.FindObjectOfType<TreeMenuCheckBox>().GetComponent<UnityEngine.UI.ScrollRect>();
        return scrollRect;
    }
    /// <summary>
    /// 刷新标识箭头
    /// </summary>
    public void RefreshIdentifying()
    {
        if (Identifying!=null)
            Identifying.transform.localEulerAngles = new Vector3(0, 0, Group ? Group.activeSelf ? -90 : 0 : 0);
    }
}
public class TreeMenuCheckBox : MonoBehaviour {
    [Header("字体")]
    public Font font;
    [Header("标识图")]
    public Sprite Ide;
    public Catalog data = new Catalog();
    [Header("窗口根节点")]
    public Transform Content;
    [Header("默认展开?")]
    public bool IsUnfold = false;
    void Start () {
       if (font==null)
        {
            Debug.Log("[请设置Font]");
            return;
        }
        #region  通过文件夹获取树状结构数据  也可以通过其他方式给 Catalog 赋值
        DirectoryInfo dir = new DirectoryInfo(Application.streamingAssetsPath+"/data");
        DirectoryInfo[] dirs = dir.GetDirectories();
        for (int i = 0; i < dirs.Length; i++)
        {
            GetMenus(dirs[i],data.Data);
        }
        #endregion
        Create();
        Refresh(Content.GetComponent<RectTransform>());
    }
     public void GetMenus(DirectoryInfo info, List<Catalog.CatalogData> data)
    {
        data.Add(new Catalog.CatalogData(info.Name));
        DirectoryInfo[] dirs = info.GetDirectories();
        for (int i = 0; i < dirs.Length; i++)
        {
            GetMenus(dirs[i], data[data.Count-1].Data);
        }
    }
     /// <summary>
    /// 创建树形目录
    /// </summary>
    public void Create()
    {
        CreateNode(Content,data.Data);
        Debug.Log("创建目录完毕");
    }
     /// <summary>
    /// 创建节点
    /// </summary>
    /// <param name="parnt"></param>
    /// <param name="data"></param>
    public void CreateNode(Transform parnt, List<Catalog.CatalogData> data)
    {
        for (int i = 0; i < data.Count; i++)
        {
            #region 创建菜单
            GameObject menu = new GameObject(data[i].MenuName);
            Image image= menu.AddComponent<Image>();
            image.color = Color.gray;
            menu.transform.SetParent(parnt);
            menu.transform.localScale = Vector3.one;
            menu.GetComponent<RectTransform>().sizeDelta = new Vector2(400,50);
             #endregion
             #region  创建菜单下的文字
            GameObject label = new GameObject(data[i].MenuName);
            label.transform.SetParent(menu.transform);
            label.transform.localScale = Vector3.one;
            Text text = label.AddComponent<Text>();
            text.font = font;
            text.text = data[i].MenuName;
            text.fontSize = 26;
            text.fontStyle = FontStyle.Normal;
            text.alignment = TextAnchor.MiddleCenter;
            RectTransform rect = label.GetComponent<RectTransform>();
            rect.sizeDelta = new Vector2(400, 50);   
            #endregion
            #region  添加菜单的监听事件
            UIMenuClick menuClick = menu.AddComponent<UIMenuClick>();
            menuClick.Group = null;
            menuClick.Refresh = Refresh;
            GameObject identifying = new GameObject("identifying");
            Image ima = identifying.AddComponent<Image>();
            ima.sprite= Ide;
            ima.SetNativeSize();
            identifying.transform.SetParent(menu.transform);
            identifying.transform.localScale = Vector3.one;
            rect = identifying.GetComponent<RectTransform>();
            rect.pivot = new Vector2(0.5f,0.5f);
            rect.localPosition = new Vector3(-(menu.GetComponent<RectTransform>().sizeDelta.x/2f)-10f- rect.sizeDelta.x/2f, 0);
            identifying.SetActive(false);
            menuClick.Identifying = identifying;
            #endregion
		 #region  创建当前菜单下的节点
	    if (data[i].Data.Count>0)
            {
                GameObject group = new GameObject(data[i].MenuName+ "_Group");
                menuClick.Group = group;
                group.transform.SetParent(parnt);
                group.transform.localScale = Vector3.one;
                group.AddComponent<RectTransform>().sizeDelta = new Vector2(400, 0);
                VerticalLayoutGroup layoutGroup = group.AddComponent<VerticalLayoutGroup>();
                layoutGroup.padding.left = 50;
                //group.padding.top = (int)a.GetComponent<RectTransform>().sizeDelta.y;
                layoutGroup.spacing = 10f;
                layoutGroup.childControlHeight = false;
                layoutGroup.childControlWidth = false;
                group.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
                CreateNode(group.transform, data[i].Data);
                group.SetActive(IsUnfold);
                identifying.SetActive(true);
                menuClick.RefreshIdentifying();
                LayoutRebuilder.ForceRebuildLayoutImmediate(group.GetComponent<RectTransform>());
            }
              #endregion
        }
    }
     /// <summary>
    /// 刷新UI布局
    /// </summary>
    /// <param name="rect"></param>
     public void Refresh(RectTransform rect)
     {
		 LayoutRebuilder.ForceRebuildLayoutImmediate(rect);
		 if (rect.parent!=null)
           {
           	 if (rect.parent.GetComponent<RectTransform>()!=null)
           	 {
               	 Refresh(rect.parent.GetComponent<RectTransform>());
           	 }
           }
     }
}

Content 根节点须手动添加VerticalLayoutGroup 组件和ContentSizeFitter组件
本人的ScrollRect宽度为450.
在这里插入图片描述
目录数据放在StreamingAssets目录下 大家也可以根据自己的需求做一些改变
在这里插入图片描述

补充:少了一点代码

public class Catalog
{
 	 public List<CatalogData> Data = new List<CatalogData>();  
	 public class CatalogData
 	 {
  		public string MenuName="";
      		  public List<CatalogData> Data = new List<CatalogData>();
       		 public CatalogData(string name)
       		 {
        		    MenuName = name;
       		 }
	}

}

Unity3D利用VerticalLayoutGroup组件制作(折叠菜单、树状菜单) 二

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值