当前位置:   article > 正文

Unity 编辑器开发实战【Editor Window】- 构建公司内部的PackageManager_unity中如何将editorwindow面板显示在游戏内

unity中如何将editorwindow面板显示在游戏内

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

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

        或者使用phpstudy软件,链接:PhpStudy 让天下没有难配的服务器环境 具体的使用教程不在此进行介绍,可以参考一些相关的博客,例如:【Unity3D日常开发】Unity3D中实现向Web服务器上传图片以及下载图片功能

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

  1. using UnityEditor;
  2. namespace SK.Framework
  3. {
  4. public class PackageManagerInternal : EditorWindow
  5. {
  6. [MenuItem("Window/Package Manager Internal", priority = 1500)]
  7. private static void Open()
  8. {
  9. GetWindow<PackageManagerInternal>("Package Manager Internal").Show();
  10. }
  11. }
  12. }

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

  1. using System;
  2. using UnityEngine;
  3. using UnityEditor;
  4. namespace SK.Framework
  5. {
  6. public class PackageManagerInternal : EditorWindow
  7. {
  8. [MenuItem("Window/Package Manager Internal", priority = 1500)]
  9. private static void Open()
  10. {
  11. var window = GetWindow<PackageManagerInternal>("Package Manager Internal");
  12. //设置窗口的最小尺寸
  13. window.minSize = new Vector2(600f, 400f);
  14. window.Show();
  15. }
  16. /// <summary>
  17. /// 资源包数据结构
  18. /// </summary>
  19. [Serializable]
  20. private class PackageTemplate
  21. {
  22. /// <summary>
  23. /// 名称
  24. /// </summary>
  25. public string name;
  26. /// <summary>
  27. /// 作者
  28. /// </summary>
  29. public string author;
  30. /// <summary>
  31. /// 版本
  32. /// </summary>
  33. public string version;
  34. /// <summary>
  35. /// 发布日期
  36. /// </summary>
  37. public string releasedDate;
  38. /// <summary>
  39. /// 简介
  40. /// </summary>
  41. public string description;
  42. /// <summary>
  43. /// 依赖项
  44. /// </summary>
  45. public string[] dependencies;
  46. }
  47. }
  48. }

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

  1. private void OnGUI()
  2. {
  3. //水平布局 顶部
  4. GUILayout.BeginHorizontal("Toolbar");
  5. {
  6. //在此处添加一个搜索栏
  7. }
  8. GUILayout.EndHorizontal();
  9. //水平布局
  10. GUILayout.BeginHorizontal(GUILayout.ExpandHeight(true));
  11. {
  12. //垂直布局 设置左侧列表宽度
  13. GUILayout.BeginVertical(GUILayout.Width(200f));
  14. {
  15. //在此处列举所有资源包名称 + 版本信息
  16. }
  17. GUILayout.EndVertical();
  18. //分割线
  19. GUILayout.Box(string.Empty, "EyeDropperVerticalLine", GUILayout.ExpandHeight(true), GUILayout.Width(1f));
  20. //垂直布局
  21. GUILayout.BeginVertical(GUILayout.ExpandHeight(true));
  22. {
  23. //在此处展示当前选中的资源包的详细信息
  24. }
  25. GUILayout.EndVertical();
  26. }
  27. GUILayout.EndHorizontal();
  28. }

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

  1. using System;
  2. using System.IO;
  3. using UnityEngine;
  4. using UnityEditor;
  5. using System.Collections;
  6. using System.Collections.Generic;
  7. namespace SK.Framework
  8. {
  9. public class PackageManagerInternal : EditorWindow
  10. {
  11. [MenuItem("Window/Package Manager Internal", priority = 1500)]
  12. private static void Open()
  13. {
  14. var window = GetWindow<PackageManagerInternal>("Package Manager Internal");
  15. window.minSize = new Vector2(600f, 400f);
  16. window.Show();
  17. }
  18. /// <summary>
  19. /// 资源包数据结构
  20. /// </summary>
  21. [Serializable]
  22. private class PackageTemplate
  23. {
  24. /// <summary>
  25. /// 名称
  26. /// </summary>
  27. public string name;
  28. /// <summary>
  29. /// 作者
  30. /// </summary>
  31. public string author;
  32. /// <summary>
  33. /// 版本
  34. /// </summary>
  35. public string version;
  36. /// <summary>
  37. /// 发布日期
  38. /// </summary>
  39. public string releasedDate;
  40. /// <summary>
  41. /// 简介
  42. /// </summary>
  43. public string description;
  44. /// <summary>
  45. /// 依赖项
  46. /// </summary>
  47. public string[] dependencies;
  48. }
  49. //搜索内容
  50. private string searchContent;
  51. private void OnGUI()
  52. {
  53. //水平布局
  54. GUILayout.BeginHorizontal("Toolbar");
  55. {
  56. //搜索
  57. OnSearchGUI();
  58. }
  59. GUILayout.EndHorizontal();
  60. //水平布局
  61. GUILayout.BeginHorizontal(GUILayout.ExpandHeight(true));
  62. {
  63. //垂直布局 设置左侧列表宽度
  64. GUILayout.BeginVertical(GUILayout.Width(200f));
  65. {
  66. }
  67. GUILayout.EndVertical();
  68. //分割线
  69. GUILayout.Box(string.Empty, "EyeDropperVerticalLine", GUILayout.ExpandHeight(true), GUILayout.Width(1f));
  70. //垂直布局
  71. GUILayout.BeginVertical(GUILayout.ExpandHeight(true));
  72. {
  73. }
  74. GUILayout.EndVertical();
  75. }
  76. GUILayout.EndHorizontal();
  77. }
  78. private void OnSearchGUI()
  79. {
  80. var newSearchContent = GUILayout.TextField(searchContent, "SearchTextField");
  81. if (newSearchContent != searchContent)
  82. {
  83. searchContent = newSearchContent;
  84. Repaint();
  85. }
  86. if (Event.current.type == EventType.MouseDown && !GUILayoutUtility.GetLastRect().Contains(Event.current.mousePosition))
  87. {
  88. GUI.FocusControl(null);
  89. }
  90. }
  91. }
  92. }

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

  1. //资源包信息列表
  2. private List<List<PackageTemplate>> packages;
  3. //列表滚动视图
  4. private Vector2 listScroll;
  5. private void OnListGUI()
  6. {
  7. //滚动视图
  8. listScroll = GUILayout.BeginScrollView(listScroll);
  9. {
  10. for (int i = 0; i < packages.Count; i++)
  11. {
  12. List<PackageTemplate> list = packages[i];
  13. }
  14. }
  15. GUILayout.EndScrollView();
  16. }

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

  1. //折叠状态
  2. private Dictionary<string, bool> foldoutDic;
  3. //当前选中的资源包信息
  4. private PackageTemplate currentSelected;
  5. private void OnListGUI()
  6. {
  7. //滚动视图
  8. listScroll = GUILayout.BeginScrollView(listScroll);
  9. {
  10. GUIStyle versionStyle = new GUIStyle(GUI.skin.label) { fontStyle = FontStyle.Italic };
  11. for (int i = 0; i < packages.Count; i++)
  12. {
  13. List<PackageTemplate> list = packages[i];
  14. PackageTemplate first = list[0];
  15. //该资源包不符合检索内容 continue
  16. if (!string.IsNullOrEmpty(searchContent) && !first.name.ToLower().Contains(searchContent.ToLower())) continue;
  17. //折叠栏为打开状态
  18. if (foldoutDic[first.name])
  19. {
  20. foldoutDic[first.name] = EditorGUILayout.Foldout(foldoutDic[first.name], first.name);
  21. //列举所有版本信息 使用不同GUIStyle区分当前是否为选中项
  22. for (int n = 0; n < list.Count; n++)
  23. {
  24. GUILayout.BeginHorizontal(currentSelected == list[n] ? "SelectionRect" : "IN Title");
  25. GUILayout.FlexibleSpace();
  26. GUILayout.Label(list[n].version, versionStyle);
  27. GUILayout.Space(30f);
  28. GUILayout.EndHorizontal();
  29. if (GUILayoutUtility.GetLastRect().Contains(Event.current.mousePosition) && Event.current.type == EventType.MouseDown)
  30. {
  31. currentSelected = list[n];
  32. Event.current.Use();
  33. }
  34. }
  35. }
  36. //折叠栏为关闭状态
  37. else
  38. {
  39. GUILayout.BeginHorizontal(currentSelected == first ? "SelectionRect" : "Toolbar");
  40. {
  41. foldoutDic[first.name] = EditorGUILayout.Foldout(foldoutDic[first.name], first.name);
  42. GUILayout.FlexibleSpace();
  43. GUILayout.Label(first.version, versionStyle);
  44. }
  45. GUILayout.EndHorizontal();
  46. //鼠标点击选中
  47. if (GUILayoutUtility.GetLastRect().Contains(Event.current.mousePosition) && Event.current.type == EventType.MouseDown)
  48. {
  49. currentSelected = first;
  50. Event.current.Use();
  51. }
  52. }
  53. }
  54. }
  55. GUILayout.EndScrollView();
  56. }

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

  1. //最后更新日期
  2. private string lastUpdateDate;
  3. private void OnListGUI()
  4. {
  5. //滚动视图
  6. listScroll = GUILayout.BeginScrollView(listScroll);
  7. {
  8. GUIStyle versionStyle = new GUIStyle(GUI.skin.label) { fontStyle = FontStyle.Italic };
  9. for (int i = 0; i < packages.Count; i++)
  10. {
  11. List<PackageTemplate> list = packages[i];
  12. PackageTemplate first = list[0];
  13. if (!string.IsNullOrEmpty(searchContent) && !first.name.ToLower().Contains(searchContent.ToLower())) continue;
  14. if (foldoutDic[first.name])
  15. {
  16. foldoutDic[first.name] = EditorGUILayout.Foldout(foldoutDic[first.name], first.name);
  17. for (int n = 0; n < list.Count; n++)
  18. {
  19. GUILayout.BeginHorizontal(currentSelected == list[n] ? "SelectionRect" : "IN Title");
  20. GUILayout.FlexibleSpace();
  21. GUILayout.Label(list[n].version, versionStyle);
  22. GUILayout.Space(30f);
  23. GUILayout.EndHorizontal();
  24. if (GUILayoutUtility.GetLastRect().Contains(Event.current.mousePosition) && Event.current.type == EventType.MouseDown)
  25. {
  26. currentSelected = list[n];
  27. Event.current.Use();
  28. }
  29. }
  30. }
  31. else
  32. {
  33. GUILayout.BeginHorizontal(currentSelected == first ? "SelectionRect" : "Toolbar");
  34. {
  35. foldoutDic[first.name] = EditorGUILayout.Foldout(foldoutDic[first.name], first.name);
  36. GUILayout.FlexibleSpace();
  37. GUILayout.Label(first.version, versionStyle);
  38. }
  39. GUILayout.EndHorizontal();
  40. //鼠标点击选中
  41. if (GUILayoutUtility.GetLastRect().Contains(Event.current.mousePosition) && Event.current.type == EventType.MouseDown)
  42. {
  43. currentSelected = first;
  44. Event.current.Use();
  45. }
  46. }
  47. }
  48. }
  49. GUILayout.EndScrollView();
  50. //分割线
  51. GUILayout.Box(string.Empty, "EyeDropperHorizontalLine", GUILayout.ExpandWidth(true), GUILayout.Height(1f));
  52. //水平布局 设置高度
  53. GUILayout.BeginHorizontal(GUILayout.Height(23f));
  54. {
  55. //最后更新日期
  56. GUILayout.Label(lastUpdateDate);
  57. //刷新按钮
  58. if (GUILayout.Button(EditorGUIUtility.IconContent("Refresh"), GUILayout.Width(30f)))
  59. {
  60. //清空当前的资源包信息列表
  61. packages.Clear();
  62. //清空折叠栏信息
  63. foldoutDic.Clear();
  64. //当前选中的资源包设为空
  65. currentSelected = null;
  66. //发起网络请求
  67. EditorCoroutineRunner.StartEditorCoroutine(GetPackagesInfo());
  68. }
  69. }
  70. GUILayout.EndHorizontal();
  71. }
  72. //获取资源包信息
  73. private IEnumerator GetPackagesInfo()
  74. {
  75. string url = "http://192.168.3.27:8888/packages.txt";
  76. WWW www = new WWW(url);
  77. yield return www;
  78. if (www.error == null)
  79. {
  80. //使用LitJson反序列化
  81. List<PackageTemplate> list = JsonMapper.ToObject<List<PackageTemplate>>(www.text);
  82. //遍历列表
  83. for (int i = 0; i < list.Count; i++)
  84. {
  85. var package = list[i];
  86. //查找列表中是否已经存在该资源包其他版本
  87. int index = packages.FindIndex(m => m != null && m.Count > 0 && m[0].name == package.name);
  88. if (index == -1)
  89. {
  90. var newList = new List<PackageTemplate> { package };
  91. packages.Add(newList);
  92. foldoutDic.Add(package.name, false);
  93. }
  94. else
  95. {
  96. packages[index].Add(package);
  97. }
  98. }
  99. //更新最后刷新日期
  100. lastUpdateDate = DateTime.Now.ToString();
  101. }
  102. else
  103. {
  104. Debug.LogError(www.error);
  105. }
  106. }

        EditorCoroutineRunner类是一个在Unity Editor编辑器环境下运行携程的工具类,代码如下:

  1. public static class EditorCoroutineRunner
  2. {
  3. private class EditorCoroutine : IEnumerator
  4. {
  5. private Stack<IEnumerator> executionStack;
  6. public EditorCoroutine(IEnumerator iterator)
  7. {
  8. executionStack = new Stack<IEnumerator>();
  9. executionStack.Push(iterator);
  10. }
  11. public bool MoveNext()
  12. {
  13. IEnumerator i = executionStack.Peek();
  14. if (i.MoveNext())
  15. {
  16. object result = i.Current;
  17. if (result != null && result is IEnumerator)
  18. {
  19. executionStack.Push((IEnumerator)result);
  20. }
  21. return true;
  22. }
  23. else
  24. {
  25. if (executionStack.Count > 1)
  26. {
  27. executionStack.Pop();
  28. return true;
  29. }
  30. }
  31. return false;
  32. }
  33. public void Reset()
  34. {
  35. throw new NotSupportedException("This Operation Is Not Supported.");
  36. }
  37. public object Current
  38. {
  39. get { return executionStack.Peek().Current; }
  40. }
  41. public bool Find(IEnumerator iterator)
  42. {
  43. return executionStack.Contains(iterator);
  44. }
  45. }
  46. private static List<EditorCoroutine> editorCoroutineList;
  47. private static List<IEnumerator> buffer;
  48. public static IEnumerator StartEditorCoroutine(IEnumerator iterator)
  49. {
  50. if (editorCoroutineList == null)
  51. {
  52. editorCoroutineList = new List<EditorCoroutine>();
  53. }
  54. if (buffer == null)
  55. {
  56. buffer = new List<IEnumerator>();
  57. }
  58. if (editorCoroutineList.Count == 0)
  59. {
  60. EditorApplication.update += Update;
  61. }
  62. buffer.Add(iterator);
  63. return iterator;
  64. }
  65. private static bool Find(IEnumerator iterator)
  66. {
  67. foreach (EditorCoroutine editorCoroutine in editorCoroutineList)
  68. {
  69. if (editorCoroutine.Find(iterator))
  70. {
  71. return true;
  72. }
  73. }
  74. return false;
  75. }
  76. private static void Update()
  77. {
  78. editorCoroutineList.RemoveAll(coroutine => { return coroutine.MoveNext() == false; });
  79. if (buffer.Count > 0)
  80. {
  81. foreach (IEnumerator iterator in buffer)
  82. {
  83. if (!Find(iterator))
  84. {
  85. editorCoroutineList.Add(new EditorCoroutine(iterator));
  86. }
  87. }
  88. buffer.Clear();
  89. }
  90. if (editorCoroutineList.Count == 0)
  91. {
  92. EditorApplication.update -= Update;
  93. }
  94. }
  95. }

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

  1. private void OnDetailGUI()
  2. {
  3. if (currentSelected != null)
  4. {
  5. //名称
  6. GUILayout.Label(currentSelected.name, new GUIStyle(GUI.skin.label) { fontSize = 25, fontStyle = FontStyle.Bold });
  7. EditorGUILayout.Space();
  8. //作者
  9. GUILayout.Label(currentSelected.author, new GUIStyle(GUI.skin.label) { fontSize = 12 });
  10. EditorGUILayout.Space();
  11. //版本 + 发布日期
  12. GUILayout.Label($"Version {currentSelected.version} - {currentSelected.releasedDate}", new GUIStyle(GUI.skin.label) { fontSize = 14, fontStyle = FontStyle.Bold });
  13. EditorGUILayout.Space();
  14. //分割线
  15. GUILayout.Box(string.Empty, GUILayout.ExpandWidth(true), GUILayout.Height(1f));
  16. //简介
  17. GUILayout.Label(currentSelected.description);
  18. }
  19. GUILayout.FlexibleSpace();
  20. //分割线
  21. GUILayout.Box(string.Empty, "EyeDropperHorizontalLine", GUILayout.ExpandWidth(true), GUILayout.Height(1f));
  22. //水平布局 设置高度
  23. GUILayout.BeginHorizontal(GUILayout.Height(21f));
  24. {
  25. GUILayout.FlexibleSpace();
  26. //下载并导入
  27. if (GUILayout.Button("Import", GUILayout.Width(50f)))
  28. {
  29. if (currentSelected != null)
  30. {
  31. EditorCoroutineRunner.StartEditorCoroutine(DownloadPackage(currentSelected));
  32. }
  33. }
  34. }
  35. GUILayout.EndHorizontal();
  36. }
  37. //下载并导入资源包
  38. private IEnumerator DownloadPackage(PackageTemplate package)
  39. {
  40. }

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

  1. //下载并导入资源包
  2. private IEnumerator DownloadPackage(PackageTemplate package)
  3. {
  4. string url = $"http://192.168.3.27:8888/packages/{package.name}/{package.version}.unitypackage";
  5. WWW www = new WWW(url);
  6. yield return www;
  7. if (www.error == null)
  8. {
  9. byte[] bytes = www.bytes;
  10. string path = $"{Application.dataPath}/{package.name}-{package.version}.unitypackage";
  11. //写入本地
  12. using (FileStream fs = new FileStream(path, FileMode.Create))
  13. {
  14. fs.Write(bytes, 0, bytes.Length);
  15. }
  16. //导入
  17. AssetDatabase.ImportPackage(path, false);
  18. //删除
  19. File.Delete(path);
  20. }
  21. else
  22. {
  23. Debug.LogError(www.error);
  24. }
  25. }

最终完整代码:

  1. using System;
  2. using System.IO;
  3. using UnityEngine;
  4. using UnityEditor;
  5. using System.Collections;
  6. using System.Collections.Generic;
  7. namespace SK.Framework
  8. {
  9. public class PackageManagerInternal : EditorWindow
  10. {
  11. [MenuItem("Window/Package Manager Internal", priority = 1500)]
  12. private static void Open()
  13. {
  14. var window = GetWindow<PackageManagerInternal>("Package Manager Internal");
  15. window.minSize = new Vector2(600f, 400f);
  16. window.Show();
  17. }
  18. /// <summary>
  19. /// 资源包数据结构
  20. /// </summary>
  21. [Serializable]
  22. private class PackageTemplate
  23. {
  24. /// <summary>
  25. /// 名称
  26. /// </summary>
  27. public string name;
  28. /// <summary>
  29. /// 作者
  30. /// </summary>
  31. public string author;
  32. /// <summary>
  33. /// 版本
  34. /// </summary>
  35. public string version;
  36. /// <summary>
  37. /// 发布日期
  38. /// </summary>
  39. public string releasedDate;
  40. /// <summary>
  41. /// 简介
  42. /// </summary>
  43. public string description;
  44. /// <summary>
  45. /// 依赖项
  46. /// </summary>
  47. public string[] dependencies;
  48. }
  49. //资源包信息列表
  50. private List<List<PackageTemplate>> packages;
  51. //折叠状态
  52. private Dictionary<string, bool> foldoutDic;
  53. //列表滚动视图
  54. private Vector2 listScroll;
  55. //最后更新日期
  56. private string lastUpdateDate;
  57. //当前选中的资源包信息
  58. private PackageTemplate currentSelected;
  59. //搜索内容
  60. private string searchContent;
  61. private void OnEnable()
  62. {
  63. packages = new List<List<PackageTemplate>>();
  64. foldoutDic = new Dictionary<string, bool>();
  65. }
  66. private void OnGUI()
  67. {
  68. //水平布局
  69. GUILayout.BeginHorizontal("Toolbar");
  70. {
  71. //搜索
  72. OnSearchGUI();
  73. }
  74. GUILayout.EndHorizontal();
  75. //水平布局
  76. GUILayout.BeginHorizontal(GUILayout.ExpandHeight(true));
  77. {
  78. //垂直布局 设置左侧列表宽度
  79. GUILayout.BeginVertical(GUILayout.Width(200f));
  80. {
  81. //绘制列表
  82. OnListGUI();
  83. }
  84. GUILayout.EndVertical();
  85. //分割线
  86. GUILayout.Box(string.Empty, "EyeDropperVerticalLine", GUILayout.ExpandHeight(true), GUILayout.Width(1f));
  87. //垂直布局
  88. GUILayout.BeginVertical(GUILayout.ExpandHeight(true));
  89. {
  90. //绘制详情
  91. OnDetailGUI();
  92. }
  93. GUILayout.EndVertical();
  94. }
  95. GUILayout.EndHorizontal();
  96. }
  97. private void OnSearchGUI()
  98. {
  99. var newSearchContent = GUILayout.TextField(searchContent, "SearchTextField");
  100. if (newSearchContent != searchContent)
  101. {
  102. searchContent = newSearchContent;
  103. currentSelected = null;
  104. Repaint();
  105. }
  106. if (Event.current.type == EventType.MouseDown && !GUILayoutUtility.GetLastRect().Contains(Event.current.mousePosition))
  107. {
  108. GUI.FocusControl(null);
  109. }
  110. }
  111. private void OnListGUI()
  112. {
  113. //滚动视图
  114. listScroll = GUILayout.BeginScrollView(listScroll);
  115. {
  116. GUIStyle versionStyle = new GUIStyle(GUI.skin.label) { fontStyle = FontStyle.Italic };
  117. for (int i = 0; i < packages.Count; i++)
  118. {
  119. List<PackageTemplate> list = packages[i];
  120. PackageTemplate first = list[0];
  121. if (!string.IsNullOrEmpty(searchContent) && !first.name.ToLower().Contains(searchContent.ToLower())) continue;
  122. if (foldoutDic[first.name])
  123. {
  124. foldoutDic[first.name] = EditorGUILayout.Foldout(foldoutDic[first.name], first.name);
  125. for (int n = 0; n < list.Count; n++)
  126. {
  127. GUILayout.BeginHorizontal(currentSelected == list[n] ? "SelectionRect" : "IN Title");
  128. GUILayout.FlexibleSpace();
  129. GUILayout.Label(list[n].version, versionStyle);
  130. GUILayout.Space(30f);
  131. GUILayout.EndHorizontal();
  132. if (GUILayoutUtility.GetLastRect().Contains(Event.current.mousePosition) && Event.current.type == EventType.MouseDown)
  133. {
  134. currentSelected = list[n];
  135. Event.current.Use();
  136. }
  137. }
  138. }
  139. else
  140. {
  141. GUILayout.BeginHorizontal(currentSelected == first ? "SelectionRect" : "Toolbar");
  142. {
  143. foldoutDic[first.name] = EditorGUILayout.Foldout(foldoutDic[first.name], first.name);
  144. GUILayout.FlexibleSpace();
  145. GUILayout.Label(first.version, versionStyle);
  146. }
  147. GUILayout.EndHorizontal();
  148. //鼠标点击选中
  149. if (GUILayoutUtility.GetLastRect().Contains(Event.current.mousePosition) && Event.current.type == EventType.MouseDown)
  150. {
  151. currentSelected = first;
  152. Event.current.Use();
  153. }
  154. }
  155. }
  156. }
  157. GUILayout.EndScrollView();
  158. //分割线
  159. GUILayout.Box(string.Empty, "EyeDropperHorizontalLine", GUILayout.ExpandWidth(true), GUILayout.Height(1f));
  160. //水平布局 设置高度
  161. GUILayout.BeginHorizontal(GUILayout.Height(23f));
  162. {
  163. //最后更新日期
  164. GUILayout.Label(lastUpdateDate);
  165. //刷新按钮
  166. if (GUILayout.Button(EditorGUIUtility.IconContent("Refresh"), GUILayout.Width(30f)))
  167. {
  168. //清空当前的资源包信息列表
  169. packages.Clear();
  170. //清空折叠栏信息
  171. foldoutDic.Clear();
  172. //当前选中的资源包设为空
  173. currentSelected = null;
  174. //发起网络请求
  175. EditorCoroutineRunner.StartEditorCoroutine(GetPackagesInfo());
  176. }
  177. }
  178. GUILayout.EndHorizontal();
  179. }
  180. private void OnDetailGUI()
  181. {
  182. if (currentSelected != null)
  183. {
  184. //名称
  185. GUILayout.Label(currentSelected.name, new GUIStyle(GUI.skin.label) { fontSize = 25, fontStyle = FontStyle.Bold });
  186. EditorGUILayout.Space();
  187. //作者
  188. GUILayout.Label(currentSelected.author, new GUIStyle(GUI.skin.label) { fontSize = 12 });
  189. EditorGUILayout.Space();
  190. //版本 + 发布日期
  191. GUILayout.Label($"Version {currentSelected.version} - {currentSelected.releasedDate}", new GUIStyle(GUI.skin.label) { fontSize = 14, fontStyle = FontStyle.Bold });
  192. EditorGUILayout.Space();
  193. //分割线
  194. GUILayout.Box(string.Empty, GUILayout.ExpandWidth(true), GUILayout.Height(1f));
  195. //简介
  196. GUILayout.Label(currentSelected.description);
  197. }
  198. GUILayout.FlexibleSpace();
  199. //分割线
  200. GUILayout.Box(string.Empty, "EyeDropperHorizontalLine", GUILayout.ExpandWidth(true), GUILayout.Height(1f));
  201. //水平布局 设置高度
  202. GUILayout.BeginHorizontal(GUILayout.Height(21f));
  203. {
  204. GUILayout.FlexibleSpace();
  205. //下载并导入
  206. if (GUILayout.Button("Import", GUILayout.Width(50f)))
  207. {
  208. if (currentSelected != null)
  209. {
  210. EditorCoroutineRunner.StartEditorCoroutine(DownloadPackage(currentSelected));
  211. }
  212. }
  213. }
  214. GUILayout.EndHorizontal();
  215. }
  216. //获取资源包信息
  217. private IEnumerator GetPackagesInfo()
  218. {
  219. string url = "http://192.168.3.27:8888/packages.txt";
  220. WWW www = new WWW(url);
  221. yield return www;
  222. if (www.error == null)
  223. {
  224. //反序列化
  225. List<PackageTemplate> list = JsonMapper.ToObject<List<PackageTemplate>>(www.text);
  226. //遍历列表
  227. for (int i = 0; i < list.Count; i++)
  228. {
  229. var package = list[i];
  230. //查找列表中是否已经存在该资源包其他版本
  231. int index = packages.FindIndex(m => m != null && m.Count > 0 && m[0].name == package.name);
  232. if (index == -1)
  233. {
  234. var newList = new List<PackageTemplate> { package };
  235. packages.Add(newList);
  236. foldoutDic.Add(package.name, false);
  237. }
  238. else
  239. {
  240. packages[index].Add(package);
  241. }
  242. }
  243. //更新最后刷新日期
  244. lastUpdateDate = DateTime.Now.ToString();
  245. }
  246. else
  247. {
  248. Debug.LogError(www.error);
  249. }
  250. }
  251. //下载并导入资源包
  252. private IEnumerator DownloadPackage(PackageTemplate package)
  253. {
  254. string url = $"http://192.168.3.27:8888/packages/{package.name}/{package.version}.unitypackage";
  255. WWW www = new WWW(url);
  256. yield return www;
  257. if (www.error == null)
  258. {
  259. byte[] bytes = www.bytes;
  260. string path = $"{Application.dataPath}/{package.name}-{package.version}.unitypackage";
  261. //写入本地
  262. using (FileStream fs = new FileStream(path, FileMode.Create))
  263. {
  264. fs.Write(bytes, 0, bytes.Length);
  265. }
  266. //导入
  267. AssetDatabase.ImportPackage(path, false);
  268. //删除
  269. File.Delete(path);
  270. }
  271. else
  272. {
  273. Debug.LogError(www.error);
  274. }
  275. }
  276. }
  277. public static class EditorCoroutineRunner
  278. {
  279. private class EditorCoroutine : IEnumerator
  280. {
  281. private Stack<IEnumerator> executionStack;
  282. public EditorCoroutine(IEnumerator iterator)
  283. {
  284. executionStack = new Stack<IEnumerator>();
  285. executionStack.Push(iterator);
  286. }
  287. public bool MoveNext()
  288. {
  289. IEnumerator i = executionStack.Peek();
  290. if (i.MoveNext())
  291. {
  292. object result = i.Current;
  293. if (result != null && result is IEnumerator)
  294. {
  295. executionStack.Push((IEnumerator)result);
  296. }
  297. return true;
  298. }
  299. else
  300. {
  301. if (executionStack.Count > 1)
  302. {
  303. executionStack.Pop();
  304. return true;
  305. }
  306. }
  307. return false;
  308. }
  309. public void Reset()
  310. {
  311. throw new NotSupportedException("This Operation Is Not Supported.");
  312. }
  313. public object Current
  314. {
  315. get { return executionStack.Peek().Current; }
  316. }
  317. public bool Find(IEnumerator iterator)
  318. {
  319. return executionStack.Contains(iterator);
  320. }
  321. }
  322. private static List<EditorCoroutine> editorCoroutineList;
  323. private static List<IEnumerator> buffer;
  324. public static IEnumerator StartEditorCoroutine(IEnumerator iterator)
  325. {
  326. if (editorCoroutineList == null)
  327. {
  328. editorCoroutineList = new List<EditorCoroutine>();
  329. }
  330. if (buffer == null)
  331. {
  332. buffer = new List<IEnumerator>();
  333. }
  334. if (editorCoroutineList.Count == 0)
  335. {
  336. EditorApplication.update += Update;
  337. }
  338. buffer.Add(iterator);
  339. return iterator;
  340. }
  341. private static bool Find(IEnumerator iterator)
  342. {
  343. foreach (EditorCoroutine editorCoroutine in editorCoroutineList)
  344. {
  345. if (editorCoroutine.Find(iterator))
  346. {
  347. return true;
  348. }
  349. }
  350. return false;
  351. }
  352. private static void Update()
  353. {
  354. editorCoroutineList.RemoveAll(coroutine => { return coroutine.MoveNext() == false; });
  355. if (buffer.Count > 0)
  356. {
  357. foreach (IEnumerator iterator in buffer)
  358. {
  359. if (!Find(iterator))
  360. {
  361. editorCoroutineList.Add(new EditorCoroutine(iterator));
  362. }
  363. }
  364. buffer.Clear();
  365. }
  366. if (editorCoroutineList.Count == 0)
  367. {
  368. EditorApplication.update -= Update;
  369. }
  370. }
  371. }
  372. }

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/100074
推荐阅读
相关标签
  

闽ICP备14008679号