Unity TreeView 網(wǎng)絡(luò)收包顯示工具(Json版)

已經(jīng)記不清這是福州的第幾天高溫預(yù)警了钉跷,感覺自己在非洲尼斧,春困,秋乏,夏打盹堵泽,工作還是不能落下=_=Zzz...

想放假,想泡水

言歸正傳

咳咳咳父阻,來看看今天和大家要分享的一個東西:網(wǎng)絡(luò)收包顯示工具杜窄。

在項目開發(fā)過程中,每一個游戲的需求是千變?nèi)f化的蚌本,游戲種類也大相徑庭盔粹,有單機,聯(lián)網(wǎng)程癌,有單人舷嗡,多人。Unity雖然為我們提供了各種很方便的工具嵌莉,但是要解決如此多的需求进萄,還是有些力不從心,所以它開放了強大的編輯器擴展功能锐峭。用它的編輯器擴展中鼠,每一個游戲開發(fā)人員可以很方便的以Unity為平臺開發(fā)自己游戲的工具鏈。

網(wǎng)絡(luò)游戲開發(fā)過程中離不開和服務(wù)端的數(shù)據(jù)交互沿癞。之前我們項目服務(wù)端使用了Mina架構(gòu)援雇,游戲開發(fā)的匆忙(不要問我為什么匆忙,游戲公司的都懂/(ㄒoㄒ)/~~)椎扬,所有服務(wù)端下發(fā)的數(shù)據(jù)都在Console控制臺進行打颖共(你沒有看錯,直接打印...)蚕涤,控制臺還會打印很多不同的信息晶府,客戶端與服務(wù)端聯(lián)調(diào)的時候就十分費勁,需要不斷的尋找下發(fā)的數(shù)據(jù)钻趋,心累川陆,還是寫個工具吧。

最終效果

運行時有點藍~

知識點

1.Unity Editor使用
2.TreeView
3.LitJson 解析Json

動手擼代碼

1.創(chuàng)建一個Editor窗體

新建名為NetListenerWindow的cs文件 蛮位,放入Editor文件夾中(編輯器使用的代碼较沪,統(tǒng)一放Editor命名的文件夾,方便管理),使用MenuItem屬性在編輯器中創(chuàng)建菜單入口失仁。這樣就初始化彈出一個和Unity自帶的Scene和Game一樣的Window窗體尸曼,之后左右的內(nèi)容都需要顯示在里面。

public class NetListenerWindow : EditorWindow
{
    [MenuItem("Window/NetListener--消息包監(jiān)聽")]
    static void Init()
    {
        NetListenerWindow window = (NetListenerWindow)EditorWindow.GetWindow(typeof(NetListenerWindow), true, "網(wǎng)絡(luò)監(jiān)聽", true);
        window.Show();
    }
}
2.窗體布局

新彈出的窗體一片空白什么也沒有萄焦,為了操作和顯示的方便控轿,需要對他進行簡單的布局處理冤竹。Unity編輯器中布局使用的Unity最早的一套GUI系統(tǒng) Immediate Mode GUI (IMGUI),布局主要使用GUILayout和GUIStyle進行純代碼控制茬射,如果使用Unity較早的同學(xué)應(yīng)該很熟悉這套東西鹦蠕,新上手的也沒關(guān)系,可以先看看我的例子在抛,文章末尾的擴展閱讀也會有相關(guān)的資料钟病。

空空如也的Window

編輯器的UI繪制需要寫到OnGUI方法中,常用的方法有GUI控件的顯示刚梭,GUI布局處理肠阱,GUI樣式調(diào)整等,下面的代碼是當(dāng)前這個例子的GUI顯示代碼朴读,包含詳細的注解屹徘,歡迎食用~

void OnGUI()
    {
        if (GUILayout.Button("清空數(shù)據(jù)", EditorStyles.toolbarButton, GUILayout.Width(position.width)))
        {
          //繪制名為“清空數(shù)據(jù)”的按鈕控件
          //按鈕的樣式使用內(nèi)置的EditorStyles.toolbarButton樣式,也可以自己new GUIStyle進行設(shè)置
          //GUILayout.Width方法設(shè)置按鈕控件的絕對寬度為當(dāng)前窗口的寬度
        }
        
        //創(chuàng)建一個橫向流動布局衅金,在一行中顯示兩個按鈕
        GUILayout.BeginHorizontal();
        if (GUILayout.Button("展開全部", GUILayout.Width(position.width/2)))
        {
           //繪制名為“展開全部”的按鈕控件
           //GUILayout.Width方法設(shè)置按鈕控件的絕對寬度為當(dāng)前窗口寬度的1/2
        }
        if (GUILayout.Button("折疊全部",GUILayout.Width(position.width/2)))
        {
           //繪制名為“折疊全部”的按鈕控件
           //GUILayout.Width方法設(shè)置按鈕控件的絕對寬度為當(dāng)前窗口寬度的1/2
        }
        GUILayout.EndHorizontal();

        //開始繪制一個滾動視圖布局
        GUILayout.BeginScrollView(Vector2.zero, GUILayout.Width(position.width), GUILayout.Height(position.height));
        //繪制接收到的網(wǎng)絡(luò)消息TreeView控件
        GUILayout.EndScrollView();
    }

界面效果:

實用主意工具界面(⊙﹏⊙)b
3.定義接收網(wǎng)絡(luò)包的展示TreeView控件

本來還擔(dān)心自己要實現(xiàn)一個TreeView噪伊,查了下資料發(fā)現(xiàn)Unity已經(jīng)有了這個功能(我使用的是Unity 5.6版),只需要在已有的控件類上進行簡單的擴展就可以很方便的使用了典挑。老規(guī)矩酥宴,帶大量注釋的代碼。

//敲黑板您觉,記得繼承TreeView
class SimpleTreeView : TreeView
{
    public TreeViewItem root;//TreeView的根節(jié)點
    int itemIndex;//自增節(jié)點索引(每一個節(jié)點都需要一個id索引值拙寡,這里用自增型的)

    //構(gòu)造函數(shù)
    public SimpleTreeView(TreeViewState treeViewState)
        : base(treeViewState)
    {
        itemIndex = 1;

        //TreeView里,depth代表節(jié)點的深度琳水,最上層的根節(jié)點depth為-1
        root = new TreeViewItem { id = 0, depth = -1, displayName = "Root" };
        //初始化的時候需要在根節(jié)點下插入一個子節(jié)點肆糕,不然會報Children null的錯誤
        root.AddChild(new TreeViewItem { id = 1, depth = 0, displayName = "網(wǎng)絡(luò)包接收" });
    }

    /// <summary>
    /// 獲取自增的索引值
    /// </summary>
    /// <returns></returns>
    public int GetIndex()
    {
        itemIndex++;
        return itemIndex;
    }

    //重載BuildRoot方法
    protected override TreeViewItem BuildRoot()
    {
        SetupDepthsFromParentsAndChildren(root);
        return root;
    }
}

這里定義了一個簡單的TreeView,TreeView中的每一個節(jié)點都用原生的TreeViewItem類實現(xiàn)在孝,沒有進行額外的擴展诚啃,當(dāng)然你也可以根據(jù)實際的需要擴展這個類,用到自己的項目中私沮。

4.定義&初始化

TreeViewState這個字段需要用[SerializeField]修飾始赎,該字段是用來保存TreeView中的序列化后的信息(官方解釋:The TreeViewState contains serializable state information for the TreeView)。netDatas用來保存接收到的網(wǎng)絡(luò)消息仔燕,我這里是Json造垛,各位具體項目中可以替換成自己對于的數(shù)據(jù)。

    [SerializeField]
    TreeViewState m_TreeViewState;

    SimpleTreeView m_SimpleTreeView;
    List<JsonData> netDatas;//保存接收到的網(wǎng)絡(luò)消息

初始化數(shù)據(jù)的方法放到了OnEnable中晰搀,將m_TreeViewState傳入我們定義的SimpleTreeView進行初始化五辽。

    void OnEnable()
    {
        if (m_TreeViewState == null)
            m_TreeViewState = new TreeViewState();

        m_SimpleTreeView = new SimpleTreeView(m_TreeViewState);
        m_SimpleTreeView.Reload();
    }
5.獲取數(shù)據(jù),生成TreeView

這一部分主要是Json數(shù)據(jù)的解析外恕,并生成對應(yīng)的TreeView杆逗。對于Editor工具乡翅,游戲代碼無法訪問到Editor,需要Editor不斷的在游戲運行時去獲取接收到的數(shù)據(jù)罪郊。這是就需要的客戶端數(shù)據(jù)接收部分預(yù)留一個接口蠕蚜,獲取對應(yīng)的數(shù)據(jù),然后在Editor中調(diào)用這個接口排龄。在OnGUI中的方法做如下改動:

if (Mina.MinaClient.app != null)
{
     GetNetDatas();
}
GUILayout.BeginScrollView(Vector2.zero, GUILayout.Width(position.width), GUILayout.Height(position.height));
m_SimpleTreeView.OnGUI(new Rect(0, 0, position.width, position.height));
GUILayout.EndScrollView();

因為編輯器打開就在運行波势,而游戲只有在我們點擊運行后才會執(zhí)行翎朱,所以需要先判斷游戲是否啟動橄维,這里我直接判斷客戶端Mina接收數(shù)據(jù)服務(wù)是否存在,如果存在就獲取數(shù)據(jù)拴曲。

simpleTreeView的OnGUI需要在OnGUI中執(zhí)行争舞。

下面的代碼是獲取數(shù)據(jù)并生成TreeView的代碼,每次GetNetDatas獲取數(shù)據(jù)后澈灼,將緩存數(shù)據(jù)清除竞川,就不會一直重復(fù)獲取刷新,減少不必要的開銷叁熔。

Json的解析包含的Json Object和Json Array兩部分委乌,有需要的朋友可以看看,不是使用Json的朋友可以略過荣回。

生成TreeViewItem的時候要注意兩點:
1.要注意depth值遭贸,depth表示了當(dāng)前節(jié)點在樹種的層級關(guān)系;
2.生成完一個節(jié)點的全部子節(jié)點心软,再生成下一個同級節(jié)點壕吹,因為TreeView的樹結(jié)構(gòu)是從上到下遍歷生成的。

    /// <summary>
    /// 獲取網(wǎng)絡(luò)包數(shù)據(jù)
    /// </summary>
    void GetNetDatas()
    {
        if (Mina.MinaClient.netDatas.Count > 0)
        {
            netDatas = Mina.MinaClient.netDatas;

            TreeViewItem item;
            int eventid;//消息id
            int retcode;//消息狀態(tài)
            JsonData content;//消息內(nèi)容
            int index;

            for (int i = 0; i < netDatas.Count; i++)
            {
                eventid = (int)netDatas[i]["e"];
                retcode = (int)netDatas[i]["r"];
                if (netDatas[i].Keys.Contains("p"))
                {
                    content = netDatas[i]["p"];
                }
                else
                {
                    content = null;
                }
                index = m_SimpleTreeView.GetIndex();
                item = new TreeViewItem { id = index, depth = 0, displayName = "eventid:" + eventid + " retcode:" + retcode };
                m_SimpleTreeView.root.AddChild(item);
                AddContentItem(item, content, 1);
            }

            m_SimpleTreeView.Reload();
            netDatas.Clear();
            Mina.MinaClient.netDatas.Clear();
        }
    }

    /// <summary>
    /// 獲取內(nèi)容項目
    /// </summary>
    /// <param name="content"></param>
    /// <param name="depth"></param>
    void AddContentItem(TreeViewItem parentItem, JsonData content, int depth)
    {
        TreeViewItem item;
        JsonData subContent;
        int arrayIndex;
        if (content == null)
            return;
        if (content.GetJsonType() == JsonType.Object)
        {
            foreach (var key in content.Keys)
            {
                subContent = content[key];
                item = new TreeViewItem { id = m_SimpleTreeView.GetIndex(), depth = depth, displayName = key };
                parentItem.AddChild(item);
                AddContentItem(item, subContent, depth + 1);
            }
        }
        else if (content.GetJsonType() == JsonType.Array)
        {
            arrayIndex = 0;
            foreach (JsonData elem in content)
            {
                item = new TreeViewItem { id = m_SimpleTreeView.GetIndex(), depth = depth, displayName = arrayIndex.ToString() };
                parentItem.AddChild(item);
                arrayIndex++;
                AddContentItem(item, elem, depth + 1);
            }
        }
        else
        {
            item = new TreeViewItem { id = m_SimpleTreeView.GetIndex(), depth = depth, displayName = content.ToString() };
            parentItem.AddChild(item);
        }
    }

這里還有個問題删铃,如果大家只是做了這些會發(fā)現(xiàn)每次接收到數(shù)據(jù)的時候并不會實時刷新耳贬,因為當(dāng)你運行游戲的時候,網(wǎng)絡(luò)包接收窗口并不在Focus狀態(tài)猎唁,OnGUI方法并沒有激活執(zhí)行咒劲,這時候你需要點擊一下窗口才能收到數(shù)據(jù),這顯然不是我們想要的結(jié)果诫隅。這是我們就要在NetListenerWindow中加上下面這個方法腐魂,這個問題就完美解決啦。

void OnInspectorUpdate()
{
    //這里開啟窗口的重繪阎肝,不然窗口信息不會刷新
    this.Repaint();
}
6.補齊按鈕方法

按鈕方法補齊下挤渔,OnGUI完整版

 void OnGUI()
    {
        if (GUILayout.Button("清空數(shù)據(jù)", EditorStyles.toolbarButton, GUILayout.Width(position.width)))
        {
            m_SimpleTreeView.root.children.Clear();
            m_SimpleTreeView.Reload();
        }
        GUILayout.BeginHorizontal();
        if (GUILayout.Button("展開全部", GUILayout.Width(position.width/2)))
        {
            m_SimpleTreeView.ExpandAll();
        }
        if (GUILayout.Button("折疊全部",GUILayout.Width(position.width/2)))
        {
            m_SimpleTreeView.CollapseAll();
        }
        GUILayout.EndHorizontal();
        if (Mina.MinaClient.app != null)
        {
            GetNetDatas();
        }
        GUILayout.BeginScrollView(Vector2.zero, GUILayout.Width(position.width), GUILayout.Height(position.height));
        m_SimpleTreeView.OnGUI(new Rect(0, 0, position.width, position.height));
        GUILayout.EndScrollView();
    }

可以改進的幾個地方

上面這些代碼已經(jīng)可以實現(xiàn)一個基本的網(wǎng)絡(luò)收包顯示工具啦,在使用過程中發(fā)現(xiàn)還是有不少地方可以改進:
1.樹結(jié)構(gòu)需要展開和折疊的時候只能點擊左邊小小的箭頭风题,這樣操作不是很方便判导,需要支持雙擊展開嫉父、雙擊折疊功能;
2.網(wǎng)絡(luò)包結(jié)構(gòu)復(fù)雜的時候眼刃,一級一級進行展開不是很方便(雖然有清空绕辖、展開全部、折疊全部這些按鈕輔助)擂红,可以改進成拆分顯示的方式仪际,左邊顯示樹狀結(jié)構(gòu),點擊后右邊直接把網(wǎng)絡(luò)包用字符串打印出來(注意排版)昵骤;
3.恩树碱,樣子有點丑,我需要個美術(shù)同學(xué)~~

這些改進是都可以實現(xiàn)的变秦,具體怎么做嗎成榜,哎,看看有時間再寫加強版咯

擴展閱讀

Unity - Manual: TreeView
Unity - Scripting API: TreeView
Unity 折疊樹 EditorGUI.Foldout
UnityEditor.IMGUI中的TreeView
Immediate Mode GUI (IMGUI) GUIScriptingGuide

結(jié)語

結(jié)語
大夏天寫博客蹦玫,隨手點個贊咯赎婚,共同學(xué)習(xí),共同進步樱溉,共勉之挣输。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市福贞,隨后出現(xiàn)的幾起案子撩嚼,更是在濱河造成了極大的恐慌,老刑警劉巖肚医,帶你破解...
    沈念sama閱讀 217,509評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件绢馍,死亡現(xiàn)場離奇詭異,居然都是意外死亡肠套,警方通過查閱死者的電腦和手機舰涌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評論 3 394
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來你稚,“玉大人瓷耙,你說我怎么就攤上這事〉罄担” “怎么了搁痛?”我有些...
    開封第一講書人閱讀 163,875評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長宇弛。 經(jīng)常有香客問我鸡典,道長,這世上最難降的妖魔是什么枪芒? 我笑而不...
    開封第一講書人閱讀 58,441評論 1 293
  • 正文 為了忘掉前任彻况,我火速辦了婚禮谁尸,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘纽甘。我一直安慰自己良蛮,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,488評論 6 392
  • 文/花漫 我一把揭開白布悍赢。 她就那樣靜靜地躺著决瞳,像睡著了一般。 火紅的嫁衣襯著肌膚如雪左权。 梳的紋絲不亂的頭發(fā)上皮胡,一...
    開封第一講書人閱讀 51,365評論 1 302
  • 那天,我揣著相機與錄音涮总,去河邊找鬼胸囱。 笑死祷舀,一個胖子當(dāng)著我的面吹牛瀑梗,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播裳扯,決...
    沈念sama閱讀 40,190評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼抛丽,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了饰豺?” 一聲冷哼從身側(cè)響起亿鲜,我...
    開封第一講書人閱讀 39,062評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎冤吨,沒想到半個月后蒿柳,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,500評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡漩蟆,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,706評論 3 335
  • 正文 我和宋清朗相戀三年垒探,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片怠李。...
    茶點故事閱讀 39,834評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡圾叼,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出捺癞,到底是詐尸還是另有隱情夷蚊,我是刑警寧澤,帶...
    沈念sama閱讀 35,559評論 5 345
  • 正文 年R本政府宣布髓介,位于F島的核電站惕鼓,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏唐础。R本人自食惡果不足惜箱歧,卻給世界環(huán)境...
    茶點故事閱讀 41,167評論 3 328
  • 文/蒙蒙 一夫否、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧叫胁,春花似錦凰慈、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至输钩,卻和暖如春豺型,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背买乃。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評論 1 269
  • 我被黑心中介騙來泰國打工姻氨, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人剪验。 一個月前我還...
    沈念sama閱讀 47,958評論 2 370
  • 正文 我出身青樓肴焊,卻偏偏與公主長得像,于是被迫代替她去往敵國和親功戚。 傳聞我的和親對象是個殘疾皇子娶眷,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,779評論 2 354

推薦閱讀更多精彩內(nèi)容

  • WebSocket-Swift Starscream的使用 WebSocket 是 HTML5 一種新的協(xié)議。它實...
    香橙柚子閱讀 23,860評論 8 183
  • 我對陜西的感情比較特殊啸臀。 爺爺届宠,太爺爺都出生在陜西。 打我記事起乘粒,經(jīng)常聽家人說起先前的事情豌注。當(dāng)時好像很富裕,住著四...
    銀子姐閱讀 380評論 0 0
  • 今天太遲了灯萍,沒有通讀轧铁,明天補讀,感賞自己越來越能穩(wěn)住情緒竟稳,即使孩子不起來吃晚飯也能理解他属桦,讓他自己把握,沒有太多的...
    zhangxiaoyu閱讀 120評論 0 1
  • 昨天作為發(fā)泄后的第一天上課他爸,我的精神還有些恍惚聂宾,不過還好上課前有時間緩沖一下,也好多了诊笤,上午中午都沒有什么好說的系谐,...
    堅志閱讀 151評論 0 0