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,我们完成了整个界面的框架(空壳)设计,当然这是一个简单到爆而且极度不美观的界面,不过这无法妨碍我们之后在其中添加一些我们所关心的控件。

猜你喜欢

转载自blog.csdn.net/qq992817263/article/details/79654398