unity文件工程资源依赖关系生成树状菜单视图插件

写这个插件的时间也接近一年了,很多开发过程中的过程也忘记得差不多。在很多时候ms的时候就是说不出自己做了什么。之前一直有个心态,就是自己做过了就不屑去看,结果到了面试的时候很多自己做的都答不上。对于这个问题是要值得重视的所以现在决心重看代码,再次熟悉这个插件,写个技术文档博客。


---------------------------------此插件1.0完成时间于2019.08.23 - Author: hsb And zjj------------------------------------

功能:此插件的功能有:
1.选中Asserts中的某个资源,点击鼠标右键,选择Query be Depenced Graphical Display会出现一个自定义窗口,或选择Assets/Query be Depenced Graphical Display。
也会有这样的效果。

2.显示窗口的内容包括:
->被依赖 视图:可以看到选中的文件被依赖文件的情况。
    在此视图基础上加了过滤功能,可以通过选择unity和Resource单选框把unity资源和Resource文件下的资源过滤出来。
    * Name:  文件名
    * Path:  路径
    * State:  状态(Normal-正常, Changed-该文件已经被修改过,-No Data-此文件已经被删除)
              注:此功能是根据svn status查询本地仓库与远程仓库的区别,并把符合按照题意显示出来的,可以扩展。

->依赖 视图:可以看到选中的文件依赖文件的情况。
    * Name:  文件名
    * Path:  路径
    * State:  状态(Normal-正常, Changed-该文件已经被修改过,-No Data-此文件已经被删除)
              注:此功能是根据svn status查询本地仓库与远程仓库的区别,并把符合按照题意显示出来的,可以扩展。

->配置:  使用前需要配置访问neo4j的地址和密码
         public: 公有地址配置 - 配置的是公有地址,只提供查询。
            use it to query单选框:就是选择此地址进行查询。
         personal: 个人地址配置 - 配置的是本地地址,提供查询和提交数据。
            use it to query单选框:就是选择此地址进行查询。
            Modify: 修改查询的个人地址
            reflesh: 刷新neo4j数据库
            stop: 停止提交

->展开:  可以选中资源树中的某个节点,点击,把节点的所有子节点展开。

->收缩:  可以选中资源树的某个节点,点击,把节点的所有子节点收缩;如果该节点是叶子节点,则把它的前一个父节点的孩子节点搜索。

3.使用配置:
因为要查看树状菜单个节点的状态,我们使用的是cmd命令访问,所以需要提供路径。这个路径应该是当前工程Assets文件夹的前一个文件夹。注:再向前的文件夹也没问题,只不过是遍历的范围变大了,最好就是前一个文件夹。
修改的变量名称是:currentSvnPath。 
而saveCmdRstTxt变量保存的是cmd命令查询下来的返回值,可以根据你的使用情况修改。

The plug-in 1.0 completion time is 2019.08.23 - Author: HSB And ZJJ

Functions: The functions of this plug-in include:
1. Select a resource in Asserts, right-click on the mouse, select Query be Depenced Graphical Display and a custom window will appear, or select Assets/Query be Depenced Graphical Display.
It will also have this effect.

2. The contents of the display window include:

-> Dependent View: You can see that the selected file is dependent on the file.
On the basis of this view, the filtering function is added, which can filter the resources under the unit resource and resource file by selecting the unit and resource radio boxes.
    * Name: File name
    * Path:Path:Path
    * State: State (Normal - Normal, Changed - This file has been modified, - No Data - This file has been deleted)
    Note: This function is based on SVN status query local warehouse and remote warehouse differences, and according to the title shows, can be extended.

-> Dependency View: You can see how the selected file depends on the file.
    * Name: File name
    * Path:Path:Path
    * State: State (Normal - Normal, Changed - This file has been modified, - No Data - This file has been deleted)
    Note: This function is based on SVN status query local warehouse and remote warehouse differences, and according to the title shows, can be extended.

-> Configuration: You need to configure the address and password to access neo4j before using it
    Public: Public Address Configuration - Public Address Configuration provides only queries.
        Use it to query: Select this address for query.
    Personal: Personal Address Configuration - Configuring local addresses to provide queries and submit data.
        Use it to query: Select this address for query.
        Modify: Modify the personal address of the query
        Reflesh: refresh neo4j database
        Stop: Stop submitting

-> Expand: You can select a node in the resource tree, click, and expand all the child nodes of the node.

-> Collapse: You can select a node in the resource tree, click on it, and shrink all the child nodes of the node; if the node is a leaf node, search for the child nodes of its previous parent node.

3. Use configuration:

Because to view the status of tree menu nodes, we use CMD command access, so we need to provide a path. This path should be the first folder of the current Engineering Assets folder. Note: Forward folder is no problem, but the scope of traversal has increased, preferably the former folder.
The modified variable name is currentSvnPath.
The saveCmdRstTxt variable saves the returned value from the CMD command query, which can be modified according to your usage.
 


关键点1:

        public class SimpleTreeView : TreeView
        {
            const float kIconWidth = 18f;
            const float kRowHeights = 20f;

            Queue<Node> nodeQueue = new Queue<Node>();

            private GUIStyle stateGUIStyle = new GUIStyle { richText = true, alignment = TextAnchor.MiddleCenter }; //菜单状态样式

            enum MyColumns
            {
                Name,
                Path,
                State,
            }

            public static MultiColumnHeaderState CreateDefaultMultiColumnHeaderState()
            {
                var state = new MultiColumnHeaderState(columns);
                return state;
            }

            private Texture2D GetIcon(string path)
            {
                Object obj = AssetDatabase.LoadAssetAtPath(path, typeof(UnityEngine.Object));
                if (obj != null)
                {
                    Texture2D icon = AssetPreview.GetMiniThumbnail(obj);
                    if (icon == null)
                        icon = AssetPreview.GetMiniTypeThumbnail(obj.GetType());
                    return icon;
                }
                return null;
            }

            protected override void RowGUI(RowGUIArgs args)
            {
                var item = (TreeViewItem)args.item;
                for (int i = 0; i < args.GetNumVisibleColumns(); ++i)
                {
                    CellGUI(args.GetCellRect(i), item, (MyColumns)args.GetColumn(i), ref args);
                }
            }

            void CellGUI(Rect cellRect, TreeViewItem item, MyColumns column, ref RowGUIArgs args)
            {
                CenterRectUsingSingleLineHeight(ref cellRect);
                switch (column)
                {
                    case MyColumns.Name:
                        {
                            var iconRect = cellRect;
                            iconRect.x += GetContentIndent(item);
                            iconRect.width = kIconWidth;
                            if (iconRect.x < cellRect.xMax)
                            {
                                string strValue;
                                saveTreeViewItem.TryGetValue(item.id, out strValue);
                                var icon = GetIcon(strValue);
                                if (icon != null)
                                {
                                    GUI.DrawTexture(iconRect, icon, ScaleMode.ScaleToFit);
                                }
                                else
                                {
                                    UnityEngine.Debug.Log("icon为空!!!");
                                }
                            }
                            args.rowRect = cellRect;
                            base.RowGUI(args);
                        }
                        break;
                    case MyColumns.Path:
                        {
                            string strValue;
                            saveTreeViewItem.TryGetValue(item.id, out strValue);
                            GUI.Label(cellRect, strValue);
                        }
                        break;
                    case MyColumns.State:
                        {
                            showState(stateGUIStyle, cellRect, item, 1);
                        }
                        break;
                }
            }

            public Boolean getChildren(int id)
            {
                IList<int> childList = this.GetDescendantsThatHaveChildren(id);
                if (childList.Count == 0)
                {
                    return false;
                }
                else
                {
                    return true;
                }
            }

            public int getParent(int id)
            {
                IList<int> childList = GetAncestors(id);

                int maxId = 0;
                foreach (int i in childList)
                {
                    if (maxId < i)
                    {
                        maxId = i;
                    }
                }
                return maxId;
            }

            public SimpleTreeView(TreeViewState treeViewState, MultiColumnHeader multicolumnHeader)
                : base(treeViewState, multicolumnHeader)
            {
                rowHeight = kRowHeights;
                columnIndexForTreeFoldouts = 0;
                showAlternatingRowBackgrounds = true;
                showBorder = false;
                customFoldoutYOffset = (kRowHeights - EditorGUIUtility.singleLineHeight) * 0.5f;
                extraSpaceBeforeIconAndLabel = kIconWidth;
                Reload();
            }

            protected override TreeViewItem BuildRoot()
            {
                var root = new TreeViewItem { id = 0, depth = -1, displayName = "Root" };

                n1 = 1;
                foreach (var dir in MyEditor.guidsAndPathsDir)
                {
                    var node1 = new TreeViewItem { id = n1, displayName = Path.GetFileName(AssetDatabase.GUIDToAssetPath(dir.Key)) };
                    saveTreeViewItem.Add(n1, AssetDatabase.GUIDToAssetPath(dir.Key));
                    MyEditor.setTreeViewItem(1, n1);
                    n1++;
                    root.AddChild(node1);
                    List<string> tempGuidList = QueryDepencesByGuid(dir.Key);

                    List<string> assistGuidList = new List<string>();
                    IterationBuilTreeViewAsync(tempGuidList, node1);
                }

                guidsAndPathsDir = null;

                SetupDepthsFromParentsAndChildren(root);

                return root;
            }

            //建立节点
            async private Task<int> BuildNode()
            {
                while (nodeQueue.Count > 0)
                {

                    Node tempNode = nodeQueue.Dequeue();
                    saveTreeViewItem.Add(tempNode.num, AssetDatabase.GUIDToAssetPath(tempNode.guid));
                    MyEditor.setTreeViewItem(1, tempNode.num);
                    var tempTreeNode = new TreeViewItem { id = tempNode.num, displayName = Path.GetFileName(AssetDatabase.GUIDToAssetPath(tempNode.guid)) };

                    tempNode.parentTreeViewItem.AddChild(tempTreeNode);
                    List<string> temptempGuidList = QueryDepencesByGuid(tempNode.guid);
                    foreach (string tempGuid in temptempGuidList)
                    {
                        nodeQueue.Enqueue(new Node(tempTreeNode, tempGuid, n1));
                        n1++;
                    }
                }

                canSeeDepencedByView = true;
                return 0;
            }

            public async Task IterationBuilTreeViewAsync(List<string> tempGuidList, TreeViewItem node)
            {
                foreach (string tempGuid in tempGuidList)
                {
                    nodeQueue.Enqueue(new Node(node, tempGuid, n1));
                    n1++;
                }
                int tmp = await BuildNode();
            }

            protected override IList<TreeViewItem> BuildRows(TreeViewItem root)
            {
                return base.BuildRows(root);
            }
        }

 

        public static List<string> QueryDepencesByGuid(string key)
        {
            List<string> tempList = new List<string>();
            foreach (StartFollowRelation startFollowRelations in startFollowRelationList)
            {
                if (startFollowRelations.follow.Equals(key))
                {
                    tempList.Add(startFollowRelations.start);
                }
            }
            return tempList;
        }

注:因为生成树状菜单可能遇到一个问题:

就是a依赖于b和c,b和c依赖于d,b依赖于c。

通过常规的查询某个节点的所有依赖关系时,会出现:

生成树状菜单时:

a - b - d

   - c - d

   - c - b -d (重复路径) 

一开始使用1条路径到下一条路径生成。使用入栈的方法。

剪枝算法 :每生成路径时,保存该路径的叶子节点和之前的节点,出栈,基于之前的栈的top节点,生成下一级节点。直到生成到叶子节点,对比之前是否生成过相同的也子节点和从根节点下面是否两条路径是否存在相同的节点,相同,则剪枝,放回到之前交叉的节点位置,其余出栈。继续之前相同操作。时间复杂度太大。

后采用批量查询,既是把用通过常规的查询某个节点的所有依赖关系的查询语句找到改节点的所有依赖路径关系节点,让后清洗掉重复节点,然后把这些节点再使用批量查询,限制查询子节点,不再查询孙节点。在通过这些关系构建树状菜单。

批量查询的时间复杂度就很低了~

迭代:利用变量的原值推算出变量的一个新值.如果递归是自己调用自己的话,迭代就是A不停的调用B.

递归中一定有迭代,但是迭代中不一定有递归,大部分可以相互转换.能用迭代的不用递归,递归调用函数,浪费空间,并且递归太深容易造成堆栈的溢出.

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

namespace FSMEditor
{
    //依赖关系类
    public class StartFollowRelation
    {
        public string start;
        public string follow;

        public StartFollowRelation(string start, string follow)
        {
            this.start = start;
            this.follow = follow;
        }
    }
}

下载地址:https://download.csdn.net/download/guanshanyue96/12505060


这个技术博客更新是一个长期的任务,当时没写多少注释,还要慢慢重看代码~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章