Unity 编辑器开发实战【Editor Window】- 构建公司内部的PackageManager

2022-08-29 16:28:40 浏览数 (1)

Unity中的资源包管理器Package Manager为我们提供了模块、工具包的集中管理功能,可在其中下载、升级相应的资源包,本文介绍如何构建公司内部的Package Manager资源包管理器。

首先我们需要一个局域网服务器,将我们的资源包放到服务器内提供下载。构建该服务器有很多途径,可以让公司后端人员进行开发,并定制下载等相关接口,我们只需要调用接口。如果没有该条件,可以使用一些相关的软件,比如HFS,一个简易的HTTP服务器软件,下载链接:https://www.rejetto.com/hfs/?f=dl

或者使用phpstudy软件,链接:https://www.xp.cn/ 具体的使用教程不在此进行介绍,可以参考一些相关的博客。

有了服务器环境后,开始在Unity中创建编辑器,创建一个编辑器窗口首先需要继承Editor Window类,在往期的博客中也有介绍: 四、编辑器开发之EditorWindow

代码语言:javascript复制
using UnityEditor;

namespace SK.Framework
{
    public class PackageManagerInternal : EditorWindow
    {
        [MenuItem("Window/Package Manager Internal", priority = 1500)]
        private static void Open()
        {
             GetWindow<PackageManagerInternal>("Package Manager Internal").Show();
        }
    }
}

定义资源包的数据结构,参考Unity中的Package Manager,一个资源包包含的信息大概有:名称、作者、版本、发布日期、简介、依赖项、示例等:

代码语言:javascript复制
using System;
using UnityEngine;
using UnityEditor;

namespace SK.Framework
{
    public class PackageManagerInternal : EditorWindow
    {
        [MenuItem("Window/Package Manager Internal", priority = 1500)]
        private static void Open()
        {
            var window = GetWindow<PackageManagerInternal>("Package Manager Internal");
            //设置窗口的最小尺寸
            window.minSize = new Vector2(600f, 400f);
            window.Show();
        }

        /// <summary>
        /// 资源包数据结构
        /// </summary>
        [Serializable]
        private class PackageTemplate
        {
            /// <summary>
            /// 名称
            /// </summary>
            public string name;
            /// <summary>
            /// 作者
            /// </summary>
            public string author;
            /// <summary>
            /// 版本
            /// </summary>
            public string version;
            /// <summary>
            /// 发布日期
            /// </summary>
            public string releasedDate;
            /// <summary>
            /// 简介
            /// </summary>
            public string description;
            /// <summary>
            /// 依赖项
            /// </summary>
            public string[] dependencies;
        }
    }
}

通过GUILayout类中Begin Horizontal和End Horizontal、Begin Vertical和End Vertical方法定义窗口的大概布局:

代码语言:javascript复制
private void OnGUI()
{
    //水平布局 顶部
    GUILayout.BeginHorizontal("Toolbar");
    {
        //在此处添加一个搜索栏
    }
    GUILayout.EndHorizontal();

    //水平布局
    GUILayout.BeginHorizontal(GUILayout.ExpandHeight(true));
    {
        //垂直布局 设置左侧列表宽度
        GUILayout.BeginVertical(GUILayout.Width(200f));
        {
            //在此处列举所有资源包名称   版本信息
        }
        GUILayout.EndVertical();

        //分割线
        GUILayout.Box(string.Empty, "EyeDropperVerticalLine", GUILayout.ExpandHeight(true), GUILayout.Width(1f));

        //垂直布局
        GUILayout.BeginVertical(GUILayout.ExpandHeight(true));
        {
            //在此处展示当前选中的资源包的详细信息
        }
        GUILayout.EndVertical();
    }
    GUILayout.EndHorizontal();
}

搜索栏使用TextField添加一个文本输入框,GUIStyle使用Unity中内置的SearchTextField,内置GUIStyle的查看方法在往期的博客中有介绍,链接:四、编辑器开发之EditorWindow

代码语言:javascript复制
using System;
using System.IO;
using UnityEngine;
using UnityEditor;
using System.Collections;
using System.Collections.Generic;

namespace SK.Framework
{
    public class PackageManagerInternal : EditorWindow
    {
        [MenuItem("Window/Package Manager Internal", priority = 1500)]
        private static void Open()
        {
            var window = GetWindow<PackageManagerInternal>("Package Manager Internal");
            window.minSize = new Vector2(600f, 400f);
            window.Show();
        }

        /// <summary>
        /// 资源包数据结构
        /// </summary>
        [Serializable]
        private class PackageTemplate
        {
            /// <summary>
            /// 名称
            /// </summary>
            public string name;
            /// <summary>
            /// 作者
            /// </summary>
            public string author;
            /// <summary>
            /// 版本
            /// </summary>
            public string version;
            /// <summary>
            /// 发布日期
            /// </summary>
            public string releasedDate;
            /// <summary>
            /// 简介
            /// </summary>
            public string description;
            /// <summary>
            /// 依赖项
            /// </summary>
            public string[] dependencies;
        }
        //搜索内容
        private string searchContent;

        private void OnGUI()
        {
            //水平布局
            GUILayout.BeginHorizontal("Toolbar");
            {
                //搜索
                OnSearchGUI();
            }
            GUILayout.EndHorizontal();

            //水平布局
            GUILayout.BeginHorizontal(GUILayout.ExpandHeight(true));
            {
                //垂直布局 设置左侧列表宽度
                GUILayout.BeginVertical(GUILayout.Width(200f));
                {

                }
                GUILayout.EndVertical();

                //分割线
                GUILayout.Box(string.Empty, "EyeDropperVerticalLine", GUILayout.ExpandHeight(true), GUILayout.Width(1f));

                //垂直布局
                GUILayout.BeginVertical(GUILayout.ExpandHeight(true));
                {

                }
                GUILayout.EndVertical();
            }
            GUILayout.EndHorizontal();
        }

        private void OnSearchGUI()
        {
            var newSearchContent = GUILayout.TextField(searchContent, "SearchTextField");
            if (newSearchContent != searchContent)
            {
                searchContent = newSearchContent;
                Repaint();
            }
            if (Event.current.type == EventType.MouseDown && !GUILayoutUtility.GetLastRect().Contains(Event.current.mousePosition))
            {
                GUI.FocusControl(null);
            }
        }       
    }
}

在左侧列举所有资源包信息,资源包可能会有不同的版本,我们可以将名称相同而版本不同的资源包放到一个列表中,因此使用一个二维列表。因为数量不确定,可能会超出窗口高度大小,因此使用滚动视图来列举资源包信息,滚动视图通过BeginScrollView和EndScrollView方法实现:

代码语言:javascript复制
//资源包信息列表
private List<List<PackageTemplate>> packages;

//列表滚动视图
private Vector2 listScroll;

private void OnListGUI()
{
    //滚动视图
    listScroll = GUILayout.BeginScrollView(listScroll);
    {
        for (int i = 0; i < packages.Count; i  )
        {
            List<PackageTemplate> list = packages[i];
        }
    }
    GUILayout.EndScrollView();
}

遍历资源包列表,通过资源包的名称是否包含搜索栏里检索的内容来判断是否列举该项资源包信息。还需要增加一个折叠栏,折叠栏为打开状态时列举不同的版本,否则只列举第一个版本,使用一个字典来存储折叠状态信息,Key值为资源包名称(string),Value值为折叠栏状态(bool),点击时记录当前选中的资源包

代码语言:javascript复制
//折叠状态
private Dictionary<string, bool> foldoutDic;
//当前选中的资源包信息
private PackageTemplate currentSelected;

private void OnListGUI()
{
    //滚动视图
    listScroll = GUILayout.BeginScrollView(listScroll);
    {
        GUIStyle versionStyle = new GUIStyle(GUI.skin.label) { fontStyle = FontStyle.Italic };
        for (int i = 0; i < packages.Count; i  )
        {
            List<PackageTemplate> list = packages[i];
            PackageTemplate first = list[0];
            //该资源包不符合检索内容 continue
            if (!string.IsNullOrEmpty(searchContent) && !first.name.ToLower().Contains(searchContent.ToLower())) continue;
            //折叠栏为打开状态
            if (foldoutDic[first.name])
            {
                foldoutDic[first.name] = EditorGUILayout.Foldout(foldoutDic[first.name], first.name);
                //列举所有版本信息 使用不同GUIStyle区分当前是否为选中项
                for (int n = 0; n < list.Count; n  )
                {
                    GUILayout.BeginHorizontal(currentSelected == list[n] ? "SelectionRect" : "IN Title");
                    GUILayout.FlexibleSpace();
                    GUILayout.Label(list[n].version, versionStyle);
                    GUILayout.Space(30f);
                    GUILayout.EndHorizontal();

                    if (GUILayoutUtility.GetLastRect().Contains(Event.current.mousePosition) && Event.current.type == EventType.MouseDown)
                    {
                        currentSelected = list[n];
                        Event.current.Use();
                    }
                }
            }
            //折叠栏为关闭状态
            else
            {
                GUILayout.BeginHorizontal(currentSelected == first ? "SelectionRect" : "Toolbar");
                {
                    foldoutDic[first.name] = EditorGUILayout.Foldout(foldoutDic[first.name], first.name);
                    GUILayout.FlexibleSpace();
                    GUILayout.Label(first.version, versionStyle);
                }
                GUILayout.EndHorizontal();

                //鼠标点击选中
                if (GUILayoutUtility.GetLastRect().Contains(Event.current.mousePosition) && Event.current.type == EventType.MouseDown)
                {
                    currentSelected = first;
                    Event.current.Use();
                }
            }
        }
    }
    GUILayout.EndScrollView();
}   

在列表的最下方展示最后刷新的时间,并添加一个刷新按钮,点击按钮时,从服务器下载资源包Json数据,记录最后刷新时间,清空当前的资源包信息列表并根据下载的信息重新赋值:

代码语言:javascript复制
//最后更新日期
private string lastUpdateDate;

private void OnListGUI()
{
    //滚动视图
    listScroll = GUILayout.BeginScrollView(listScroll);
    {
        GUIStyle versionStyle = new GUIStyle(GUI.skin.label) { fontStyle = FontStyle.Italic };
        for (int i = 0; i < packages.Count; i  )
        {
            List<PackageTemplate> list = packages[i];
            PackageTemplate first = list[0];
            if (!string.IsNullOrEmpty(searchContent) && !first.name.ToLower().Contains(searchContent.ToLower())) continue;
            if (foldoutDic[first.name])
            {
                foldoutDic[first.name] = EditorGUILayout.Foldout(foldoutDic[first.name], first.name);
                for (int n = 0; n < list.Count; n  )
                {
                    GUILayout.BeginHorizontal(currentSelected == list[n] ? "SelectionRect" : "IN Title");
                    GUILayout.FlexibleSpace();
                    GUILayout.Label(list[n].version, versionStyle);
                    GUILayout.Space(30f);
                    GUILayout.EndHorizontal();

                    if (GUILayoutUtility.GetLastRect().Contains(Event.current.mousePosition) && Event.current.type == EventType.MouseDown)
                    {
                        currentSelected = list[n];
                        Event.current.Use();
                    }
                }
            }
            else
            {
                GUILayout.BeginHorizontal(currentSelected == first ? "SelectionRect" : "Toolbar");
                {
                    foldoutDic[first.name] = EditorGUILayout.Foldout(foldoutDic[first.name], first.name);
                    GUILayout.FlexibleSpace();
                    GUILayout.Label(first.version, versionStyle);
                }
                GUILayout.EndHorizontal();

                //鼠标点击选中
                if (GUILayoutUtility.GetLastRect().Contains(Event.current.mousePosition) && Event.current.type == EventType.MouseDown)
                {
                    currentSelected = first;
                    Event.current.Use();
                }
            }
        }
    }
    GUILayout.EndScrollView();

    //分割线
    GUILayout.Box(string.Empty, "EyeDropperHorizontalLine", GUILayout.ExpandWidth(true), GUILayout.Height(1f));

    //水平布局 设置高度
    GUILayout.BeginHorizontal(GUILayout.Height(23f));
    {
        //最后更新日期
        GUILayout.Label(lastUpdateDate);
        //刷新按钮
        if (GUILayout.Button(EditorGUIUtility.IconContent("Refresh"), GUILayout.Width(30f)))
        {
            //清空当前的资源包信息列表
            packages.Clear();
            //清空折叠栏信息
            foldoutDic.Clear();
            //当前选中的资源包设为空
            currentSelected = null;
            //发起网络请求
            EditorCoroutineRunner.StartEditorCoroutine(GetPackagesInfo());
        }
    }
    GUILayout.EndHorizontal();
}

//获取资源包信息
private IEnumerator GetPackagesInfo()
{
    string url = "http://192.168.3.27:8888/packages.txt";
    WWW www = new WWW(url);
    yield return www;
    if (www.error == null)
    {
        //使用LitJson反序列化
        List<PackageTemplate> list = JsonMapper.ToObject<List<PackageTemplate>>(www.text);
        //遍历列表
        for (int i = 0; i < list.Count; i  )
        {
            var package = list[i];
            //查找列表中是否已经存在该资源包其他版本
            int index = packages.FindIndex(m => m != null && m.Count > 0 && m[0].name == package.name);
            if (index == -1)
            {
                var newList = new List<PackageTemplate> { package };
                packages.Add(newList);
                foldoutDic.Add(package.name, false);
            }
            else
            {
                packages[index].Add(package);
            }
        }
        //更新最后刷新日期
        lastUpdateDate = DateTime.Now.ToString();
    }
    else
    {
        Debug.LogError(www.error);
    }
}
代码语言:javascript复制
    EditorCoroutineRunner类是一个在Unity Editor编辑器环境下运行携程的工具类,代码如下:
代码语言:javascript复制
public static class EditorCoroutineRunner
{
    private class EditorCoroutine : IEnumerator
    {
        private Stack<IEnumerator> executionStack;

        public EditorCoroutine(IEnumerator iterator)
        {
            executionStack = new Stack<IEnumerator>();
            executionStack.Push(iterator);
        }

        public bool MoveNext()
        {
            IEnumerator i = executionStack.Peek();

            if (i.MoveNext())
            {
                object result = i.Current;
                if (result != null && result is IEnumerator)
                {
                    executionStack.Push((IEnumerator)result);
                }

                return true;
            }
            else
            {
                if (executionStack.Count > 1)
                {
                    executionStack.Pop();
                    return true;
                }
            }

            return false;
        }

        public void Reset()
        {
            throw new NotSupportedException("This Operation Is Not Supported.");
        }

        public object Current
        {
            get { return executionStack.Peek().Current; }
        }

        public bool Find(IEnumerator iterator)
        {
            return executionStack.Contains(iterator);
        }
    }

    private static List<EditorCoroutine> editorCoroutineList;
    private static List<IEnumerator> buffer;

    public static IEnumerator StartEditorCoroutine(IEnumerator iterator)
    {
        if (editorCoroutineList == null)
        {
            editorCoroutineList = new List<EditorCoroutine>();
        }
        if (buffer == null)
        {
            buffer = new List<IEnumerator>();
        }
        if (editorCoroutineList.Count == 0)
        {
            EditorApplication.update  = Update;
        }
        buffer.Add(iterator);
        return iterator;
    }

    private static bool Find(IEnumerator iterator)
    {
        foreach (EditorCoroutine editorCoroutine in editorCoroutineList)
        {
            if (editorCoroutine.Find(iterator))
            {
                return true;
            }
        }
        return false;
    }

    private static void Update()
    {
        editorCoroutineList.RemoveAll(coroutine => { return coroutine.MoveNext() == false; });
        if (buffer.Count > 0)
        {
            foreach (IEnumerator iterator in buffer)
            {
                if (!Find(iterator))
                {
                    editorCoroutineList.Add(new EditorCoroutine(iterator));
                }
            }
            buffer.Clear();
        }
        if (editorCoroutineList.Count == 0)
        {
            EditorApplication.update -= Update;
        }
    }
}

列举资源包列表信息后,在右侧展示当前选中项的详细信息,并在底部添加一个Import的Button按钮,用于下载并导入该资源包:

代码语言:javascript复制
private void OnDetailGUI()
{
    if (currentSelected != null)
    {
        //名称
        GUILayout.Label(currentSelected.name, new GUIStyle(GUI.skin.label) { fontSize = 25, fontStyle = FontStyle.Bold });
        EditorGUILayout.Space();
        //作者
        GUILayout.Label(currentSelected.author, new GUIStyle(GUI.skin.label) { fontSize = 12 });
        EditorGUILayout.Space();
        //版本   发布日期
        GUILayout.Label($"Version {currentSelected.version} - {currentSelected.releasedDate}", new GUIStyle(GUI.skin.label) { fontSize = 14, fontStyle = FontStyle.Bold });
        EditorGUILayout.Space();

        //分割线
        GUILayout.Box(string.Empty, GUILayout.ExpandWidth(true), GUILayout.Height(1f));

        //简介
        GUILayout.Label(currentSelected.description);
    }
    GUILayout.FlexibleSpace();

    //分割线
    GUILayout.Box(string.Empty, "EyeDropperHorizontalLine", GUILayout.ExpandWidth(true), GUILayout.Height(1f));

    //水平布局 设置高度
    GUILayout.BeginHorizontal(GUILayout.Height(21f));
    {
        GUILayout.FlexibleSpace();
        //下载并导入
        if (GUILayout.Button("Import", GUILayout.Width(50f)))
        {
            if (currentSelected != null)
            {
                EditorCoroutineRunner.StartEditorCoroutine(DownloadPackage(currentSelected));
            }
        }
    }
    GUILayout.EndHorizontal();
}

//下载并导入资源包
private IEnumerator DownloadPackage(PackageTemplate package)
{

}

发起下载资源包的网络请求后,根据接收到的bytes字节数据,将.unitypackage文件写入本地,然后调用AssetDatabase类中的ImportPackage方法,该方法可以将.unitypackage资源包导入Unity中,导入完成后,再将下载的文件删除:

代码语言:javascript复制
//下载并导入资源包
private IEnumerator DownloadPackage(PackageTemplate package)
{
    string url = $"http://192.168.3.27:8888/packages/{package.name}/{package.version}.unitypackage";
    WWW www = new WWW(url);
    yield return www;
    if (www.error == null)
    {
        byte[] bytes = www.bytes;
        string path = $"{Application.dataPath}/{package.name}-{package.version}.unitypackage";
        //写入本地
        using (FileStream fs = new FileStream(path, FileMode.Create))
        {
            fs.Write(bytes, 0, bytes.Length);
        }
        //导入
        AssetDatabase.ImportPackage(path, false);
        //删除
        File.Delete(path);
    }
    else
    {
        Debug.LogError(www.error);
    }
}

最终完整代码:

代码语言:javascript复制
using System;
using System.IO;
using UnityEngine;
using UnityEditor;
using System.Collections;
using System.Collections.Generic;

namespace SK.Framework
{
    public class PackageManagerInternal : EditorWindow
    {
        [MenuItem("Window/Package Manager Internal", priority = 1500)]
        private static void Open()
        {
            var window = GetWindow<PackageManagerInternal>("Package Manager Internal");
            window.minSize = new Vector2(600f, 400f);
            window.Show();
        }

        /// <summary>
        /// 资源包数据结构
        /// </summary>
        [Serializable]
        private class PackageTemplate
        {
            /// <summary>
            /// 名称
            /// </summary>
            public string name;
            /// <summary>
            /// 作者
            /// </summary>
            public string author;
            /// <summary>
            /// 版本
            /// </summary>
            public string version;
            /// <summary>
            /// 发布日期
            /// </summary>
            public string releasedDate;
            /// <summary>
            /// 简介
            /// </summary>
            public string description;
            /// <summary>
            /// 依赖项
            /// </summary>
            public string[] dependencies;
        }

        //资源包信息列表
        private List<List<PackageTemplate>> packages;
        //折叠状态
        private Dictionary<string, bool> foldoutDic;
        //列表滚动视图
        private Vector2 listScroll;
        //最后更新日期
        private string lastUpdateDate;
        //当前选中的资源包信息
        private PackageTemplate currentSelected;
        //搜索内容
        private string searchContent;

        private void OnEnable()
        {
            packages = new List<List<PackageTemplate>>();
            foldoutDic = new Dictionary<string, bool>();
        }

        private void OnGUI()
        {
            //水平布局
            GUILayout.BeginHorizontal("Toolbar");
            {
                //搜索
                OnSearchGUI();
            }
            GUILayout.EndHorizontal();

            //水平布局
            GUILayout.BeginHorizontal(GUILayout.ExpandHeight(true));
            {
                //垂直布局 设置左侧列表宽度
                GUILayout.BeginVertical(GUILayout.Width(200f));
                {
                    //绘制列表
                    OnListGUI();
                }
                GUILayout.EndVertical();

                //分割线
                GUILayout.Box(string.Empty, "EyeDropperVerticalLine", GUILayout.ExpandHeight(true), GUILayout.Width(1f));

                //垂直布局
                GUILayout.BeginVertical(GUILayout.ExpandHeight(true));
                {
                    //绘制详情
                    OnDetailGUI();
                }
                GUILayout.EndVertical();
            }
            GUILayout.EndHorizontal();
        }

        private void OnSearchGUI()
        {
            var newSearchContent = GUILayout.TextField(searchContent, "SearchTextField");
            if (newSearchContent != searchContent)
            {
                searchContent = newSearchContent;
                currentSelected = null;
                Repaint();
            }
            if (Event.current.type == EventType.MouseDown && !GUILayoutUtility.GetLastRect().Contains(Event.current.mousePosition))
            {
                GUI.FocusControl(null);
            }
        }

        private void OnListGUI()
        {
            //滚动视图
            listScroll = GUILayout.BeginScrollView(listScroll);
            {
                GUIStyle versionStyle = new GUIStyle(GUI.skin.label) { fontStyle = FontStyle.Italic };
                for (int i = 0; i < packages.Count; i  )
                {
                    List<PackageTemplate> list = packages[i];
                    PackageTemplate first = list[0];
                    if (!string.IsNullOrEmpty(searchContent) && !first.name.ToLower().Contains(searchContent.ToLower())) continue;
                    if (foldoutDic[first.name])
                    {
                        foldoutDic[first.name] = EditorGUILayout.Foldout(foldoutDic[first.name], first.name);
                        for (int n = 0; n < list.Count; n  )
                        {
                            GUILayout.BeginHorizontal(currentSelected == list[n] ? "SelectionRect" : "IN Title");
                            GUILayout.FlexibleSpace();
                            GUILayout.Label(list[n].version, versionStyle);
                            GUILayout.Space(30f);
                            GUILayout.EndHorizontal();

                            if (GUILayoutUtility.GetLastRect().Contains(Event.current.mousePosition) && Event.current.type == EventType.MouseDown)
                            {
                                currentSelected = list[n];
                                Event.current.Use();
                            }
                        }
                    }
                    else
                    {
                        GUILayout.BeginHorizontal(currentSelected == first ? "SelectionRect" : "Toolbar");
                        {
                            foldoutDic[first.name] = EditorGUILayout.Foldout(foldoutDic[first.name], first.name);
                            GUILayout.FlexibleSpace();
                            GUILayout.Label(first.version, versionStyle);
                        }
                        GUILayout.EndHorizontal();

                        //鼠标点击选中
                        if (GUILayoutUtility.GetLastRect().Contains(Event.current.mousePosition) && Event.current.type == EventType.MouseDown)
                        {
                            currentSelected = first;
                            Event.current.Use();
                        }
                    }
                }
            }
            GUILayout.EndScrollView();

            //分割线
            GUILayout.Box(string.Empty, "EyeDropperHorizontalLine", GUILayout.ExpandWidth(true), GUILayout.Height(1f));

            //水平布局 设置高度
            GUILayout.BeginHorizontal(GUILayout.Height(23f));
            {
                //最后更新日期
                GUILayout.Label(lastUpdateDate);
                //刷新按钮
                if (GUILayout.Button(EditorGUIUtility.IconContent("Refresh"), GUILayout.Width(30f)))
                {
                    //清空当前的资源包信息列表
                    packages.Clear();
                    //清空折叠栏信息
                    foldoutDic.Clear();
                    //当前选中的资源包设为空
                    currentSelected = null;
                    //发起网络请求
                    EditorCoroutineRunner.StartEditorCoroutine(GetPackagesInfo());
                }
            }
            GUILayout.EndHorizontal();
        }
        private void OnDetailGUI()
        {
            if (currentSelected != null)
            {
                //名称
                GUILayout.Label(currentSelected.name, new GUIStyle(GUI.skin.label) { fontSize = 25, fontStyle = FontStyle.Bold });
                EditorGUILayout.Space();
                //作者
                GUILayout.Label(currentSelected.author, new GUIStyle(GUI.skin.label) { fontSize = 12 });
                EditorGUILayout.Space();
                //版本   发布日期
                GUILayout.Label($"Version {currentSelected.version} - {currentSelected.releasedDate}", new GUIStyle(GUI.skin.label) { fontSize = 14, fontStyle = FontStyle.Bold });
                EditorGUILayout.Space();

                //分割线
                GUILayout.Box(string.Empty, GUILayout.ExpandWidth(true), GUILayout.Height(1f));

                //简介
                GUILayout.Label(currentSelected.description);
            }
            GUILayout.FlexibleSpace();

            //分割线
            GUILayout.Box(string.Empty, "EyeDropperHorizontalLine", GUILayout.ExpandWidth(true), GUILayout.Height(1f));

            //水平布局 设置高度
            GUILayout.BeginHorizontal(GUILayout.Height(21f));
            {
                GUILayout.FlexibleSpace();
                //下载并导入
                if (GUILayout.Button("Import", GUILayout.Width(50f)))
                {
                    if (currentSelected != null)
                    {
                        EditorCoroutineRunner.StartEditorCoroutine(DownloadPackage(currentSelected));
                    }
                }
            }
            GUILayout.EndHorizontal();
        }

        //获取资源包信息
        private IEnumerator GetPackagesInfo()
        {
            string url = "http://192.168.3.27:8888/packages.txt";
            WWW www = new WWW(url);
            yield return www;
            if (www.error == null)
            {
                //反序列化
                List<PackageTemplate> list = JsonMapper.ToObject<List<PackageTemplate>>(www.text);
                //遍历列表
                for (int i = 0; i < list.Count; i  )
                {
                    var package = list[i];
                    //查找列表中是否已经存在该资源包其他版本
                    int index = packages.FindIndex(m => m != null && m.Count > 0 && m[0].name == package.name);
                    if (index == -1)
                    {
                        var newList = new List<PackageTemplate> { package };
                        packages.Add(newList);
                        foldoutDic.Add(package.name, false);
                    }
                    else
                    {
                        packages[index].Add(package);
                    }
                }
                //更新最后刷新日期
                lastUpdateDate = DateTime.Now.ToString();
            }
            else
            {
                Debug.LogError(www.error);
            }
        }
        //下载并导入资源包
        private IEnumerator DownloadPackage(PackageTemplate package)
        {
            string url = $"http://192.168.3.27:8888/packages/{package.name}/{package.version}.unitypackage";
            WWW www = new WWW(url);
            yield return www;
            if (www.error == null)
            {
                byte[] bytes = www.bytes;
                string path = $"{Application.dataPath}/{package.name}-{package.version}.unitypackage";
                //写入本地
                using (FileStream fs = new FileStream(path, FileMode.Create))
                {
                    fs.Write(bytes, 0, bytes.Length);
                }
                //导入
                AssetDatabase.ImportPackage(path, false);
                //删除
                File.Delete(path);
            }
            else
            {
                Debug.LogError(www.error);
            }
        }
    }

    public static class EditorCoroutineRunner
    {
        private class EditorCoroutine : IEnumerator
        {
            private Stack<IEnumerator> executionStack;

            public EditorCoroutine(IEnumerator iterator)
            {
                executionStack = new Stack<IEnumerator>();
                executionStack.Push(iterator);
            }

            public bool MoveNext()
            {
                IEnumerator i = executionStack.Peek();

                if (i.MoveNext())
                {
                    object result = i.Current;
                    if (result != null && result is IEnumerator)
                    {
                        executionStack.Push((IEnumerator)result);
                    }

                    return true;
                }
                else
                {
                    if (executionStack.Count > 1)
                    {
                        executionStack.Pop();
                        return true;
                    }
                }

                return false;
            }

            public void Reset()
            {
                throw new NotSupportedException("This Operation Is Not Supported.");
            }

            public object Current
            {
                get { return executionStack.Peek().Current; }
            }

            public bool Find(IEnumerator iterator)
            {
                return executionStack.Contains(iterator);
            }
        }

        private static List<EditorCoroutine> editorCoroutineList;
        private static List<IEnumerator> buffer;

        public static IEnumerator StartEditorCoroutine(IEnumerator iterator)
        {
            if (editorCoroutineList == null)
            {
                editorCoroutineList = new List<EditorCoroutine>();
            }
            if (buffer == null)
            {
                buffer = new List<IEnumerator>();
            }
            if (editorCoroutineList.Count == 0)
            {
                EditorApplication.update  = Update;
            }
            buffer.Add(iterator);
            return iterator;
        }

        private static bool Find(IEnumerator iterator)
        {
            foreach (EditorCoroutine editorCoroutine in editorCoroutineList)
            {
                if (editorCoroutine.Find(iterator))
                {
                    return true;
                }
            }
            return false;
        }

        private static void Update()
        {
            editorCoroutineList.RemoveAll(coroutine => { return coroutine.MoveNext() == false; });
            if (buffer.Count > 0)
            {
                foreach (IEnumerator iterator in buffer)
                {
                    if (!Find(iterator))
                    {
                        editorCoroutineList.Add(new EditorCoroutine(iterator));
                    }
                }
                buffer.Clear();
            }
            if (editorCoroutineList.Count == 0)
            {
                EditorApplication.update -= Update;
            }
        }
    }
}

还可以拓展的东西很多,比如资源包的依赖项、比如资源包的Example示例等等。

0 人点赞