1. 程式人生 > >Unity編輯器開發(三):實戰、開發一個AB包編輯器工具

Unity編輯器開發(三):實戰、開發一個AB包編輯器工具

前言

本系列將會從零開始開發一個輕量級的AB包編輯器工具(也就是打包或者管理AssetBundle的工具),完成以後,他的最終應用介面可能是如下這樣的:

這裡寫圖片描述

介面詳解:
1、Create:建立一個新的空的AB包;
2、Rename:重新命名當前選中的AB包(必須選中任意一個AB包後方可生效);
3、Clear:清空當前選中的AB包中的所有資源(必須選中任意一個AB包後方可生效);
4、Delete:刪除當前的AB包,同時會自動執行Clear操作(必須選中任意一個AB包後方可生效);
5、Add Assets:將14中被勾選的資源新增到當前選中的AB包(必須選中任意一個AB包後方可生效);
6、Hide Invalid:是否隱藏無效的資源(無法打進AB包中的資源,比如.cs .js等);
7、Hide Bundled:是否隱藏已經打包的資源;
8、Open:開啟右側的Build Path對應的路徑,用於在打包完成後,開啟路徑取出打好的AB包;
9、Browse:瀏覽並選擇新的打包路徑;
10、打包對應平臺選擇;
11、Build:開始打包;
12、當前工程的所有AB包預覽,白色為空AB包,藍色為非空AB包;
13、當前選中的AB包中的所有資源預覽(必須選中任意一個AB包後方可生效);
14、當前工程的Asset路徑下的資源結構圖預覽,無效資源和已打包資源顯示為灰色,無法被再次選中並打入新的AB包,已打包資源的後面會顯示該資源對應的目標AB包;

建立編輯器視窗

首先,我們新建一個類AssetBundleEditor,繼承至EditorWindow,那麼他將表現為一個編輯器視窗的特徵,為其新增一個靜態的以便於在外界開啟他的方法,併為這個方法新增快捷鍵觸發的功能,%#O 也就對應了快捷鍵 Ctrl + Shift + O。

    public class AssetBundleEditor : EditorWindow
    {
        [MenuItem("Window/AssetBundle Editor %#O")]
        private static void OpenAssetBundleWindow
() { AssetBundleEditor ABEditor = GetWindow<AssetBundleEditor>("AssetBundles"); ABEditor.Show(); } }

這裡寫圖片描述

編輯器窗口布局

回顧一下我們視窗的佈局,先不用考慮其他那些雜七雜八的按鈕。

這裡寫圖片描述

1、橫向標題欄,當然這裡有兩層標題按鈕,我們可以看到4區域比2區域上面多了一層,為什麼呢,因為上面放不下這麼多按鈕,所以向下多開闢了一層;
2、一個方形區域,用來顯示AB包列表;
3、一個方形區域,用來顯示某一AB包中的資源列表;
4、一個方形區域,用來顯示整個專案中的資源列表;

首先,我們的佈局需求是這樣的:
1、標題欄1區域總是在最上方,這個毋容置疑;
2、然後區域2和區域3我們將寬度固定,讓他們兩個平分這個視窗的高度,為什麼這樣做呢,因為我不想視窗變小的時候會影響這兩個區域的寬度,因為那樣會妨礙我看某一個東西的名字,畢竟它的名字可能是足夠長的,而區域的高度的話,只要新增一個滾動區域,就不怕被拉伸了;
3、區域4的話,我們讓他以左上角為錨點固定,視窗拉伸時,左上角保持不動,因為為了配合區域2和區域3,然後為其新增一個滾動區域就不怕其中的資源過多而導致擁擠了。

那麼,想法已經有了,我們開始幹吧。

編輯器視窗區域劃分

我們已經將視窗劃分為了四大模組:標題區域,AB包列表區域,當前AB包資源列表區域,所有資源列表區域。

程式碼的話,塞到OnGUI中依次繪製就OK了。

        private void OnGUI()
        {
            TitleGUI();
            AssetBundlesGUI();
            CurrentAssetBundlesGUI();
            AssetsGUI();
        }

完事了!今天的教程結束!

……

好吧,放下菜刀,我覺得我還能再寫點。

標題區域

我們先看一下標題區域,將所有的按鈕或是其他東西寫出來,我們再進行探討:

        //標記,用於標記當前選中的AB包索引
        private int _currentAB = -1;
        //一種系統樣式,使用它就可以使按鈕展示為矩形黑框樣式
        private GUIStyle _preButton = new GUIStyle("PreButton");
        //一種系統樣式,使用它就可以使下拉框展示為矩形黑框樣式
        private GUIStyle _preDropDown = new GUIStyle("PreDropDown");
        //是否隱藏無效資源
        private bool _hideInvalidAsset = false;
        //是否隱藏已繫結資源
        private bool _hideBundleAsset = false;
        //打包路徑
        private string _buildPath = "";
        //打包平臺
        private BuildTarget _buildTarget = BuildTarget.StandaloneWindows;

        private void TitleGUI()
        {
            if (GUI.Button(new Rect(5, 5, 60, 15), "Create", _preButton))
            {
            }
            //當前未選中任一AB包的話,禁用之後的所有UI控制元件
            GUI.enabled = _currentAB == -1 ? false : true;
            if (GUI.Button(new Rect(65, 5, 60, 15), "Rename", _preButton))
            {
            }
            if (GUI.Button(new Rect(125, 5, 60, 15), "Clear", _preButton))
            {
            }
            if (GUI.Button(new Rect(185, 5, 60, 15), "Delete", _preButton))
            {
            }
            if (GUI.Button(new Rect(250, 5, 100, 15), "Add Assets", _preButton))
            {
            }
            //取消UI控制元件的禁用
            GUI.enabled = true;

            _hideInvalidAsset = GUI.Toggle(new Rect(360, 5, 100, 15), _hideInvalidAsset, "Hide Invalid");
            _hideBundleAsset = GUI.Toggle(new Rect(460, 5, 100, 15), _hideBundleAsset, "Hide Bundled");

            if (GUI.Button(new Rect(250, 25, 60, 15), "Open", _preButton))
            {
            }
            if (GUI.Button(new Rect(310, 25, 60, 15), "Browse", _preButton))
            {
            }

            GUI.Label(new Rect(370, 25, 70, 15), "Build Path:");
            _buildPath = GUI.TextField(new Rect(440, 25, 300, 15), _buildPath);

            BuildTarget buildTarget = (BuildTarget)EditorGUI.EnumPopup(new Rect((int)position.width - 205, 5, 150, 15), _buildTarget, _preDropDown);

            if (GUI.Button(new Rect((int)position.width - 55, 5, 50, 15), "Build", _preButton))
            {
            }
        }

這裡寫圖片描述

我們可以將所有按鈕或其他的東西都以絕對位置挨著擺放就可以了,Rename按鈕、Clear按鈕、Delete按鈕、Add Assets按鈕等都是針對某一AB包進行操作的,如果當前沒有選中AB包的話,這裡肯定就要禁用他們的功能,將GUI.enabled = false就是禁用之後的一切控制元件,遇到GUI.enabled = true的話解除禁用,按鈕的擺放的話,這裡就沒什麼難度了,自己拷貝原始碼編譯了看看效果就差不多明白了。

AB包列表區域

        //區域檢視的範圍
        private Rect _ABViewRect;
        //區域檢視滾動的範圍
        private Rect _ABScrollRect;
        //區域檢視滾動的位置
        private Vector2 _ABScroll;
        //區域高度標記,這裡不用管它,是後續用來控制檢視滾動量的
        private int _ABViewHeight = 0;
        //一種系統樣式,使用他可以使控制元件周圍表現為一個BOX的模樣
        private GUIStyle _box = new GUIStyle("Box");

        private void AssetBundlesGUI()
        {
            //區域的檢視範圍:左上角位置固定,寬度固定(240),高度為視窗高度的一半再減去標題欄高度(20),標題欄高度為什麼是20?看一下標題欄的控制元件高度就行了唄,多餘的是空隙之類的
            _ABViewRect = new Rect(5, 25, 240, (int)position.height / 2 - 20);
            //滾動的區域是根據當前顯示的控制元件數量來確定的,如果顯示的控制元件(AB包)太少,則滾動區域小於檢視範圍,則不生效,_ABViewHeight會根據AB包數量累加
            _ABScrollRect = new Rect(5, 25, 240, _ABViewHeight);


            _ABScroll = GUI.BeginScrollView(_ABViewRect, _ABScroll, _ABScrollRect);
            GUI.BeginGroup(_ABScrollRect, _box);

            //Begin和End中間就是我們要顯示的控制元件列表,當然,如果AB包數量太少,我們的滾動區域還是不能小於檢視區域
            if (_ABViewHeight < _ABViewRect.height)
            {
                _ABViewHeight = (int)_ABViewRect.height;
            }

            GUI.EndGroup();
            GUI.EndScrollView();
        }

這裡寫圖片描述

這裡要怎麼解釋這段程式碼?呃我想我最多也只能解釋成註釋中那樣的效果,最難理解的可能就是GUI.BeginScrollView方法中的那三個引數,第一個跟第三個容易搞混,一個表示顯示的區域,一個表示可滾動的最大區域,沒什麼其他好解釋的了,最好自己拷貝程式碼編譯了看看效果就熟悉了。

當前AB包資源列表區域

        //區域檢視的範圍
        private Rect _currentABViewRect;
        //區域檢視滾動的範圍
        private Rect _currentABScrollRect;
        //區域檢視滾動的位置
        private Vector2 _currentABScroll;
        //區域高度標記,這裡不用管它,是後續用來控制檢視滾動量的
        private int _currentABViewHeight = 0;

        private void CurrentAssetBundlesGUI()
        {
        //區域的檢視範圍:左上角位置固定在上一個區域的底部,寬度固定(240),高度為視窗高度的一半再減去空隙(15),上下都有空隙
            _currentABViewRect = new Rect(5, (int)position.height / 2 + 10, 240, (int)position.height / 2 - 15);
            _currentABScrollRect = new Rect(5, (int)position.height / 2 + 10, 240, _currentABViewHeight);


            _currentABScroll = GUI.BeginScrollView(_currentABViewRect, _currentABScroll, _currentABScrollRect);
            GUI.BeginGroup(_currentABScrollRect, _box);

            if (_currentABViewHeight < _currentABViewRect.height)
            {
                _currentABViewHeight = (int)_currentABViewRect.height;
            }

            GUI.EndGroup();
            GUI.EndScrollView();
        }

這裡寫圖片描述

左邊的兩個區域就排好了,至於為什麼上面的程式碼會是這樣的效果,這就是Rect佈局的效果,沒看明白的話可以改變其中一些資料看一下視窗變化,只要Rect運用得當,還是可以做出一些不亞於GUILayout才能有的佈局效果的。

所有資源列表區域

最後一個佈局區域,也很簡單,原理幾乎同上。

        //區域檢視的範圍
        private Rect _assetViewRect;
        //區域檢視滾動的範圍
        private Rect _assetScrollRect;
        //區域檢視滾動的位置
        private Vector2 _assetScroll;
        //區域高度標記,這裡不用管它,是後續用來控制檢視滾動量的
        private int _assetViewHeight = 0;

        private void AssetsGUI()
        {
        //區域的檢視範圍:左上角位置固定,寬度為視窗寬度減去左邊的區域寬度以及一些空隙(255),高度為視窗高度減去上方兩層標題欄以及一些空隙(50)
            _assetViewRect = new Rect(250, 45, (int)position.width - 255, (int)position.height - 50);
            _assetScrollRect = new Rect(250, 45, (int)position.width - 255, _assetViewHeight);


            _assetScroll = GUI.BeginScrollView(_assetViewRect, _assetScroll, _assetScrollRect);
            GUI.BeginGroup(_assetScrollRect, _box);

            if (_assetViewHeight < _assetViewRect.height)
            {
                _assetViewHeight = (int)_assetViewRect.height;
            }

            GUI.EndGroup();
            GUI.EndScrollView();
        }

這裡寫圖片描述

OK,我們完成了整個介面的框架(空殼)設計,當然這是一個簡單到爆而且極度不美觀的介面,不過這無法妨礙我們之後在其中新增一些我們所關心的控制元件。