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


這個技術博客更新是一個長期的任務,當時沒寫多少註釋,還要慢慢重看代碼~

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