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