unity节点Transform属性导出工具——TransformExporter

Posted 程序员茶馆

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了unity节点Transform属性导出工具——TransformExporter相关的知识,希望对你有一定的参考价值。

一.应用背景:

在unity开发过程中,有时需要按照某种规则导出指定场景中的所有节点的Transform属性,比如按照节点的Tag或者Layer导出指定Tag或者Layer的节点。为此,笔者设计并制作了一款节点Transform属性导出插件。

二.设计要点:

1.当前场景的Tag列表获取:InternalEditorUtility.tags;

2.当前场景的Layer列表获取:InternalEditorUtility.layers;

3.节点的遍历方式:广度优先遍历或深度优先遍历

4.节点路径获取:从根节点到当前节点的完整路径

5.节点Transform属性存储方式:采用了EPPlus三方库,存储为Excel格式。

三.核心源码:

using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.IO;
using OfficeOpenXml;
using System;
using System.Linq;
using UnityEditorInternal;
public class TransformExporter : EditorWindow

    //是否导出未激活的对象
    private bool includeUnenbaledObj = true;
    //节点搜索方式
    private SearchType searchType;
    //保存到处对象参数
    private Dictionary<string, bool> stateDic = new Dictionary<string, bool>();
    //Tag、Layer单选按钮名称
    private string[] m_toolbarStr =  "Tag", "Layer" ;
    //Tag、Layer单选按钮标记
    private int radioBtnValue, currentValue = -1;
    //导出的1xls文件存储路径
    private string path = null;
    [MenuItem("Tools/TransformExporter(xls)")]
    public static void ShowExportWindows()
    
        EditorWindow.GetWindow<TransformExporter>("Transform Exporter");
    

    //对话框中的各种内容通过OnGUI函数来设置
    void OnGUI()
    
        //绘制标题
        GUILayout.BeginVertical();
        GUILayout.Space(10);
        var lastSize = GUI.skin.label.fontSize;
        var lastalignment = GUI.skin.label.alignment;
        GUI.skin.label.fontSize = 24;
        GUI.skin.label.alignment = TextAnchor.MiddleCenter;
        GUILayout.Label("Transform Exporter");
        GUI.skin.label.fontSize = lastSize;
        GUI.skin.label.alignment = lastalignment;

        GUILayout.Space(10);

        includeUnenbaledObj = GUILayout.Toggle(includeUnenbaledObj, "是否包含未激活的对象?");
        GUILayout.Space(10);

        searchType = (SearchType)EditorGUILayout.EnumPopup("节点搜索方式", searchType);
        GUILayout.Space(10);

        GUILayout.Label("请选择保存路径", EditorStyles.boldLabel);
        EditorGUILayout.BeginHorizontal();
        EditorGUILayout.TextField(path, GUILayout.ExpandWidth(false));
        if (GUILayout.Button("Browse", GUILayout.ExpandWidth(false)))
            path = EditorUtility.SaveFolderPanel("Path to Save data", path, Application.dataPath);   //打开保存文件夹面板
        EditorGUILayout.EndHorizontal();
        GUILayout.Space(10);

        GUILayout.Label("请选择按Tag输出还是按Layer输出?", EditorStyles.boldLabel);
        radioBtnValue = GUILayout.Toolbar(radioBtnValue, m_toolbarStr, EditorStyles.radioButton);
        GUILayout.Space(10);
        switch (radioBtnValue)
        
            case 0:
                if (currentValue != 0)//切换Tag,Layer标签时重置标记并清空当前状态
                
                    stateDic.Clear();
                    currentValue = 0;
                
                //Tag选择框  
                foreach (var tag in InternalEditorUtility.tags)
                
                    if (!stateDic.ContainsKey(tag))
                        stateDic.Add(tag, false);
                    stateDic[tag] = EditorGUILayout.Toggle(tag, stateDic[tag]);
                
                break;
            case 1:
                if (currentValue != 1)
                
                    stateDic.Clear();
                    currentValue = 1;
                
                //Layer选择框                                                                          
                foreach (var layer in InternalEditorUtility.layers)
                
                    if (!stateDic.ContainsKey(layer))
                        stateDic.Add(layer, false);
                    stateDic[layer] = EditorGUILayout.Toggle(layer, stateDic[layer]);
                
                break;
        
        GUILayout.Space(10);
        if (GUILayout.Button("导出数据"))
        
            ExportData(currentValue, stateDic);
        
    



    private void ExportData(int index, Dictionary<string, bool> stateDic)
    
        var name = "Scene_" + UnityEngine.SceneManagement.SceneManager.GetActiveScene().name + "_" + DateTime.Now.Ticks + ".xlsx";

        if (!Directory.Exists(path))
        
            Directory.CreateDirectory("ExcelDir");
            path = "ExcelDir";
        
        var fullpath = Path.Combine(path, name);
        //获取当前激活场景的根节点对象
        var rootObjects = UnityEngine.SceneManagement.SceneManager.GetActiveScene().GetRootGameObjects();

        var validStates = stateDic.Keys.Where(key => stateDic[key]).ToArray();
        //如果不包含未激活的对象,则先把根节点中未激活的对象去除掉
        if (!includeUnenbaledObj)
            rootObjects = rootObjects.Where(obj => obj.activeSelf == true).ToArray();
        //根据所选tag或layer来定义回调,index=0为Tag,index=1为Layer
        Func<Transform, bool> action = null;
        if (index == 0)//按Tag输出
        
            action = trans =>
            
                //如果有效状态里包含了该transform的tag标签,则通过筛选
                return validStates.Contains(trans.tag);
            ;
        
        else if (index == 1)//按Layer输出
        
            action = trans =>
            
                //如果有效状态里包含了transform.gameObject的layer名称,则通过筛选
                return validStates.Contains(LayerMask.LayerToName(trans.gameObject.layer));
            ;
        


        //操作excel表格
        using (var stream = new FileStream(fullpath, FileMode.CreateNew))
        
            //操作excel表格
            using (ExcelPackage package = new ExcelPackage(stream))
            
                //设置表名
                ExcelWorksheet worksheet = package.Workbook.Worksheets.Add("Transform信息");
                //设置表头
                worksheet.Cells[1, 1].Value = "name";
                worksheet.Cells[1, 2].Value = "position";
                worksheet.Cells[1, 3].Value = "rotation";
                worksheet.Cells[1, 4].Value = "eulerAngles";
                worksheet.Cells[1, 5].Value = "localScale";
                worksheet.Cells[1, 6].Value = "activeself";
                worksheet.Cells[1, 7].Value = "path";
                worksheet.Cells[1, 8].Value = "tag";
                worksheet.Cells[1, 9].Value = "layer";

                int i = 1;
                var list = new List<Transform>();
                //遍历子节点时的操作
                TransformExtension.TransformAction searchAction = item =>
                
                    var need = !includeUnenbaledObj || includeUnenbaledObj && item.gameObject.activeSelf;
                    if (need)
                    
                        if (action(item))
                        
                            list.Add(item);
                        
                    
                    return need;
                ;
                //循环写入每个根节点下通过筛选的数据
                foreach (var obj in rootObjects)
                
                    list.Clear();
                    if (action(obj.transform))
                        list.Add(obj.transform);//先放入根节点
                    switch (searchType)
                    
                        case SearchType.BFS:
                            obj.transform.BFSGetChildren(searchAction);
                            break;
                        case SearchType.DFS:
                            obj.transform.DFSGetChildren(searchAction);
                            break;
                    
                    //写入筛选出的数据
                    foreach (var item in list)
                    
                        i++;
                        worksheet.Cells[i, 1].Value = item.name;
                        worksheet.Cells[i, 2].Value = item.position;
                        worksheet.Cells[i, 3].Value = item.rotation;
                        worksheet.Cells[i, 4].Value = item.eulerAngles;
                        worksheet.Cells[i, 5].Value = item.localScale;
                        worksheet.Cells[i, 6].Value = item.gameObject.activeSelf;
                        worksheet.Cells[i, 7].Value = item.GetRoute();
                        worksheet.Cells[i, 8].Value = item.tag;
                        worksheet.Cells[i, 9].Value = LayerMask.LayerToName(item.gameObject.layer);
                    
                
                //保存excel表格
                package.Save();
            
        
        //提示导出成功
        this.ShowNotification(new GUIContent("导出成功!路径为:" + path));
    

public enum SearchType

    DFS,//深度优先遍历
    BFS//广度优先遍历

四.工具界面:

工具界面如下图:

实验结果如下图:

五.工程源码:

详见本人github:https://github.com/xinjay/TransformExporter

以上是关于unity节点Transform属性导出工具——TransformExporter的主要内容,如果未能解决你的问题,请参考以下文章

lua代码设置unity对象的基础属性

Unity 3D之进阶篇Transform的迭代器

unity3d transform.find能找到孙子节点么

关于Unity中的transform组件

如何理解Unity组件化开发模式

Unity 自动生成组件索引类工具