在unity中写编辑器扩展工具,如在编辑器中加个菜单,点击这个菜单项时执行打包功能。

类如下,其中的静态变量,如果每次进来不清空,则LIST会越来越大,打包函数执行完后系统不会帮我们清空

  1. #if UNITY_EDITOR
  2.  
  3. using System;
  4. using System.Collections;
  5. using System.Collections.Generic;
  6. using System.IO;
  7. using UnityEditor;
  8.  
  9. using UnityEngine;
  10. using LitJson;
  11. using System.Security.Cryptography;
  12.  
  13. //资源清单,列出了游戏中用到的所有资源
  14. [Serializable]
  15. public class AssetsList
  16. {
  17. /*** 资源清单
  18. * <相对路径, MD5串>
  19. */
  20. public Dictionary<string, string> Assets2Md5 = new Dictionary<string, string>();//容量未设置,后面可以写成常量,根据上次数据得出
  21. }
  22.  
  23. public class AssetBundleBuilder
  24. {
  25. public static string BundlePath = "";
  26. public static AssetsList AssetsList = new AssetsList();
  27. public static List<string> DependAssets_PathList = new List<string>(); //InGameRes所依赖的ArtSource等文件夹下的资源
  28. public static List<string> Assets_PathList = new List<string>(); //InGameRes文件夹下的资源
  29. public static List<AssetBundleBuild> BuildList = new List<AssetBundleBuild>(); //估计值,后面可以写成常量,根据上次数据得出
  30.  
  31. public static string ResRootPath = "Assets/Resources"; //资源目录,根据此目录查找所有依赖的资源
  32. public static string ResDependPath = "Assets/ArtSource"; //资源目录,根据此目录查找所有依赖的资源
  33. public static string ResFolder = "Resources"; //资源打包的依据文件夹,此文件夹下的所有文件及所有依赖文件都被打进AB包,不要在此文件夹下放备份或无用的资源
  34.  
  35. public static string Depends_List_File = "Assets/depends_list.json";
  36. public static string Assets_List_File = "Assets/assets_list.json";
  37.  
  38. public static int FileNumLimit = ; //测试使用,只收集限定数量的文件
  39. public static void BuildAB(BuildTarget buildTarget)
  40. {
  41. if (!Directory.Exists(ResRootPath))
  42. {
  43. Debug.LogError("资源目录不存在:" + ResRootPath);
  44. return;
  45. }
  46.  
  47. var files = Directory.GetFiles(ResRootPath, "*", SearchOption.AllDirectories);
  48.  
  49. var stopwt = System.Diagnostics.Stopwatch.StartNew();
  50. var t1 = stopwt.ElapsedMilliseconds;
  51.  
  52. /***
  53. * 坑,这里必须要清空,否则每次进来就会往Assets_PathList里添加一次内容
  54. */
  55. BuildList.Clear();
  56. Assets_PathList.Clear();
  57. DependAssets_PathList.Clear();
  58. AssetsList.Assets2Md5.Clear();
  59.  
  60. var infoTitle = "收集文件,目录 : " + ResRootPath;
  61. //第一步,将ResRootPath目录下的文件收集起来,此目录的东西都是游戏中要用到的,此目录之外的东西不一定会用到
  62. var idx = ;
  63. for (int i=, cnt=files.Length; i<cnt; ++i)
  64. {
  65. var item = files[i];
  66. var ext = Path.GetExtension(item);
  67. if (string.Compare(ext, ".meta", true)== || string.Compare(ext, ".cs", true)==)
  68. continue;
  69.  
  70. if ((string.Compare(ext, ".prefab", true) == || string.Compare(ext, ".mat", true) ==
  71. || string.Compare(ext, ".unity", true) == || string.Compare(ext, ".controller", true) == ))
  72. {
  73. idx++;
  74. if (idx > FileNumLimit)
  75. break;
  76. //添加到构建列表
  77. var abName = AddToBuildList(item);
  78. Assets_PathList.Add(abName);
  79. }
  80.  
  81. EditorUtility.DisplayProgressBar(infoTitle, item, i * 1.0f / cnt);
  82. }
  83.  
  84. WriteToJson(Assets_List_File, Assets_PathList);
  85.  
  86. //第二步,将ResRootPath依赖的文件收集进来
  87. CheckAllDepends(files);
  88.  
  89. var infoTitle2 = "收集依赖,目录 : " + ResDependPath;
  90.  
  91. for (int i = , cnt = DependAssets_PathList.Count; i < cnt; ++i)
  92. {
  93. var item = DependAssets_PathList[i];
  94.  
  95. //添加到构建列表
  96. AddToBuildList(item);
  97.  
  98. EditorUtility.DisplayProgressBar(infoTitle2, item, i * 1.0f / cnt);
  99. }
  100.  
  101. //第三步,增量打包
  102. var targetName = buildTarget == BuildTarget.iOS ? "IOS" : "Android";
  103. BundlePath = Application.dataPath.Substring(, Application.dataPath.Length-) + "Bundles" + targetName; //放到Assets同目录
  104.  
  105. if (!Directory.Exists(BundlePath))
  106. {
  107. Directory.CreateDirectory(BundlePath);
  108. }
  109.  
  110. var dt1 = stopwt.ElapsedMilliseconds - t1;
  111.  
  112. ClearUnusedBundles(files);
  113. /***
  114. * 一次传入所有需要打包的数据,UNITY会保证它们之间的依赖被正确处理:先打被依赖的包,再打自己
  115. * 我们必须保证传入数据的完全,如果被依赖的包不在我们传入的列表中,则相应资源会被打到所有用到该包的包中,造成资源的重复包含
  116. */
  117. AssetBundleManifest manifest = BuildPipeline.BuildAssetBundles(BundlePath, BuildList.ToArray(), BuildAssetBundleOptions.ChunkBasedCompression, buildTarget);
  118. EditorUtility.ClearProgressBar();
  119.  
  120. var dt2 = stopwt.ElapsedMilliseconds - t1 - dt1;
  121.  
  122. EditorUtility.DisplayDialog("打包完成", "耗时:" + dt1 / 1000.0f + " + " + dt2/1000.0f + "\n路径:" + BundlePath, "ok");
  123.  
  124. GenResList(manifest);
  125.  
  126. EditorUtility.UnloadUnusedAssetsImmediate();
  127.  
  128. }
  129.  
  130. public static string AddToBuildList(string resPath)
  131. {
  132. var relativeDir = resPath;
  133.  
  134. //不能这样处理,因为可能有同名却不同后缀的文件存在
  135. //relativeDir = relativeDir.Substring(0, relativeDir.LastIndexOf(".")); //去除后缀
  136. relativeDir = relativeDir.Replace("\\", "/");
  137. var abBuild = new AssetBundleBuild();
  138. abBuild.assetBundleName = relativeDir;
  139. abBuild.assetNames = new string[] { resPath };
  140. abBuild.assetBundleVariant = "ab";
  141. BuildList.Add(abBuild);
  142.  
  143. return relativeDir;
  144. }
  145.  
  146. //将AB包移动到StreamingAssets目录下,为打APK或IPA作准备
  147. public static void CopyToStreamingAssets(BuildTarget buildTarget)
  148. {
  149. CopyFolder(BundlePath, Application.streamingAssetsPath + "/Bundles/");
  150. }
  151.  
  152. /***
  153. * 增量打包的关键,遍历上次打出的包,与本次【构建列表BuildList】里的内容对比,不在列表中的删除掉。
  154. * 在列表中的保留不变。这样当我们再次执行打包操作时,如果文件对应的AB包已存在且文件没有更改,则不用重新打包
  155. */
  156. public static void ClearUnusedBundles(string[] files)
  157. {
  158. if (!Directory.Exists(BundlePath))
  159. {
  160. Debug.LogError("包目录不存在:" + BundlePath);
  161. return;
  162. }
  163.  
  164. var abFiles = Directory.GetFiles(BundlePath + "/assets", "*", SearchOption.AllDirectories);
  165. var delList = new List<string>();
  166.  
  167. for (int i=, n=abFiles.Length; i<n; ++i)
  168. {
  169. var ext = Path.GetExtension(abFiles[i]);
  170. var file = Path.ChangeExtension(abFiles[i], null);
  171. if(string.Compare(ext, ".manifest", true) == )
  172. {
  173. file = Path.ChangeExtension(file, null);
  174. }
  175.  
  176. file = file.Substring(file.IndexOf("assets", StringComparison.CurrentCultureIgnoreCase));
  177. if (!File.Exists(file))
  178. {
  179. delList.Add(abFiles[i]);
  180. }
  181.  
  182. EditorUtility.DisplayProgressBar("正在检查AssetBundle", file, i * 1.0f / n);
  183.  
  184. }
  185.  
  186. for (int i=, n= delList.Count; i<n; ++i)
  187. {
  188. EditorUtility.DisplayProgressBar("正在清理无用的AssetBundle", delList[i], i * 1.0f / n);
  189. if (!File.Exists(delList[i]))
  190. {
  191. EditorUtility.DisplayDialog("", "不存在此文件", "ok");
  192. continue;
  193. }
  194.  
  195. File.Delete(delList[i]);
  196.  
  197. }
  198.  
  199. EditorUtility.ClearProgressBar();
  200.  
  201. WriteToJson("Assets/delete_list.json", delList);
  202. }
  203.  
  204. //剪切到某个目录,并重命名
  205. public static void MoveFolder(string src, string dest)
  206. {
  207. if (Directory.Exists(dest))
  208. {
  209. Directory.Delete(dest, true);
  210. }
  211. Directory.CreateDirectory(dest);
  212.  
  213. File.Move(src, dest + "/Bundles");
  214. }
  215.  
  216. //复制到某个目录,并重命名
  217. public static void CopyFolder(string src, string dest)
  218. {
  219. if (Directory.Exists(dest))
  220. {
  221. Directory.Delete(dest, true);
  222. }
  223. Directory.CreateDirectory(dest);
  224.  
  225. foreach (var sub in Directory.GetDirectories(src))
  226. {
  227. CopyFolder(sub + "/", dest + Path.GetFileName(sub) + "/");
  228. }
  229.  
  230. foreach (var file in Directory.GetFiles(src))
  231. {
  232. File.Copy(file, dest + Path.GetFileName(file));
  233. }
  234. }
  235.  
  236. /*** 生成资源清单
  237. * 格式:MD5 : 相对于/Bundles/的路径
  238. * 作用:热更的对比依据,通过MD5对比,确定文件的增-删-修改
  239. */
  240. public static void GenResList(AssetBundleManifest manifest)
  241. {
  242. AssetsList.Assets2Md5.Clear();
  243.  
  244. var abs = manifest.GetAllAssetBundles();
  245. foreach (var ab in abs)
  246. {
  247. var hashes = manifest.GetAssetBundleHash(ab);
  248. AssetsList.Assets2Md5.Add(ab, hashes.ToString());
  249. }
  250.  
  251. var writer = new JsonWriter();
  252. writer.PrettyPrint = true;
  253. JsonMapper.ToJson(AssetsList, writer);
  254. File.WriteAllText(BundlePath + "/asset_list.json", writer.ToString(), System.Text.Encoding.ASCII);
  255.  
  256. }
  257.  
  258. public static void DeleteBundles(BuildTarget target)
  259. {
  260. var path = Application.dataPath + "/../" + (target == BuildTarget.Android ? "BundlesAndroid" : "BundlesIOS");
  261. var msg = "文件夹不存在" + " " + path;
  262. if (Directory.Exists(path))
  263. {
  264. Directory.Delete(path, true);
  265. msg = "清理完成";
  266. }
  267.  
  268. EditorUtility.DisplayDialog("清理", msg, "ok");
  269. }
  270.  
  271. //得到指定文件夹下资源的所有依赖
  272. public static void CheckAllDepends(string targetPath)
  273. {
  274. if (!Directory.Exists(targetPath))
  275. {
  276. Debug.LogError("资源目录不存在:" + targetPath);
  277. return;
  278. }
  279.  
  280. var files = Directory.GetFiles(targetPath, "*", SearchOption.AllDirectories);
  281.  
  282. CheckAllDepends(files);
  283. }
  284.  
  285. //得到指定文件的所有依赖
  286. public static void CheckAllDepends(string[] files)
  287. {
  288. var stopWatch = System.Diagnostics.Stopwatch.StartNew();
  289. var startTime = stopWatch.ElapsedMilliseconds;
  290.  
  291. DependAssets_PathList.Clear();
  292.  
  293. var idx = ;
  294. for(int i=, fileNum = files.Length; i< fileNum; ++i)
  295. {
  296. var item = files[i];
  297. var ext = Path.GetExtension(item);
  298.  
  299. if (string.Compare(ext, ".meta", true) == || string.Compare(ext, ".cs",true) == )
  300. continue;
  301. if ((string.Compare(ext, ".prefab", true) == || string.Compare(ext, ".mat", true) ==
  302. || string.Compare(ext, ".unity", true) == || string.Compare(ext, ".controller", true) == ))
  303. {
  304. idx++;
  305. if (idx > FileNumLimit)
  306. break;
  307.  
  308. var dpends = AssetDatabase.GetDependencies(item, true);
  309. foreach (var s in dpends)
  310. {
  311. if (s.Contains(ResFolder) || s.Contains(".cs"))
  312. continue;
  313. //var substr = s.Substring(s.IndexOf("Assets/"));
  314. if (!DependAssets_PathList.Contains(s))
  315. {
  316. DependAssets_PathList.Add(s);
  317. }
  318. }
  319. }
  320.  
  321. var progress = i *1.0f/ fileNum;
  322. EditorUtility.DisplayProgressBar("正在查找引用" + fileNum, item, progress);
  323. }
  324.  
  325. EditorUtility.ClearProgressBar();
  326.  
  327. //var savePath = "Assets/refs_list.json";
  328. WriteToJson(Depends_List_File, DependAssets_PathList);
  329.  
  330. var deltaTime = stopWatch.ElapsedMilliseconds - startTime;
  331.  
  332. EditorUtility.DisplayDialog("检查完成", "耗时" + deltaTime/1000.0f + "\n保存路径: " + Depends_List_File, "ok");
  333. }
  334.  
  335. public static void WriteToJson(string path, object obj)
  336. {
  337. var writer = new JsonWriter();
  338. writer.PrettyPrint = true;
  339. JsonMapper.ToJson(obj, writer);
  340. File.WriteAllText(path, writer.ToString());
  341. }
  342.  
  343. [MenuItem("AssetBundle/安卓/构建AB包")]
  344. public static void BuildAndroidAB()
  345. {
  346. BuildAB(BuildTarget.Android);
  347. }
  348.  
  349. [MenuItem("AssetBundle/IOS/构建AB包")]
  350. public static void BuildIOSAB()
  351. {
  352. BuildAB(BuildTarget.iOS);
  353. }
  354.  
  355. [MenuItem("AssetBundle/安卓一键打包")]
  356. public static void AndroidOneKeyBuild()
  357. {
  358. BuildAB(BuildTarget.Android);
  359. CopyToStreamingAssets(BuildTarget.Android);
  360. }
  361.  
  362. [MenuItem("AssetBundle/IOS一键打包")]
  363. public static void IOSOneKeyBuild()
  364. {
  365. BuildAB(BuildTarget.iOS);
  366. CopyToStreamingAssets(BuildTarget.iOS);
  367. }
  368.  
  369. [MenuItem("AssetBundle/安卓/拷到StreamingAssets目录")]
  370. public static void CopyAndroidABToStreamingAssets()
  371. {
  372. CopyToStreamingAssets(BuildTarget.Android);
  373. }
  374.  
  375. [MenuItem("AssetBundle/IOS/拷到StreamingAssets目录")]
  376. public static void CopyIOSABToStreamingAssets()
  377. {
  378. CopyToStreamingAssets(BuildTarget.iOS);
  379. }
  380.  
  381. [MenuItem("AssetBundle/清理/清理安卓包")]
  382. public static void DeleteAndroidABs()
  383. {
  384. DeleteBundles(BuildTarget.Android);
  385. }
  386.  
  387. [MenuItem("AssetBundle/清理/清理苹果包")]
  388. public static void DeleteIOSABs()
  389. {
  390. DeleteBundles(BuildTarget.iOS);
  391. }
  392.  
  393. [MenuItem("AssetBundle/资源检查/检查依赖")]
  394. public static void CheckAssetsDepends()
  395. {
  396. CheckAllDepends(ResRootPath);
  397. }
  398. }
  399.  
  400. #endif

UNITY编辑器模式下static变量的坑的更多相关文章

  1. 实现Unity编辑器模式下的旋转

    最近在做一个模型展示的项目,我的想法是根据滑动屏幕的x方向差值和Y方向的差值,来根据世界坐标下的X轴和Y轴进行旋转,但是实习时候总是有一些卡顿.在观察unity编辑器下的旋转之后,发现编辑器下的旋转非 ...

  2. 二、Unity Editor模式下,操作选中对象

    使用Unity提供的工具类 UnityEditor.Selection public static GameObject activeGameObject public static UnityEng ...

  3. 编辑器模式下如何实例化Prefab

    当我们在EditMode下需要用脚本批量添加prefab时,可以用 PrefabUtility.InstantiatePrefab(prefab) as GameObject; 注意:如果用GameO ...

  4. unity editor模式下读取文件夹资源

    string path = EditorUtility.OpenFolderPanel("Load png Textures", "", "" ...

  5. Unity编辑器 - 编辑器控制特效播放

    编辑器控制特效播放 Unity的动画编辑器不能预览粒子系统的播放,为了方便预览特效,设想制作一个预览特效的工具,通常一个特效有三种组件: - Animation - Animator - Partic ...

  6. Lua------------------改善Unity编辑器对Lua文件的支持

    原创 2017年03月10日 18:44:22 标签: Unity / lua / 编辑器 952 当前版本的Unity(截至Unity5.5.x)中TextAsset类不支持后缀为lua的文件,将l ...

  7. VUE Node模式下,如何改变菜单的颜色,如何将超长文字缩略显示,在鼠标进入后展开全部显示,鼠标移出则恢复缩略显示

    VUE Node模式下,如何改变菜单的颜色,如何将超长文字缩略显示,在鼠标进入后展开全部显示,鼠标移出则恢复缩略显示: “事件”引起变量值的变化,系统引擎自动根据变量值的变化刷新页面 在VUE Nod ...

  8. 交互模式下测试python代码及变量的四则运算

    在交互模式下,python代码可以立即执行,所以这很方便我们进行代码测试 1.命令窗口,输入python (如果没配置环境变量则需带python安装目录的绝对路径) >>> 这个就是 ...

  9. DEBUG模式下, 内存中的变量地址分析

    测试函数的模板实现 /// @file my_template.h /// @brief 测试数据类型用的模板实现 #ifndef MY_TEMPLATE_H_2016_0123_1226 #defi ...

随机推荐

  1. 使用OkHttp模拟登陆LeetCode

    前言 网上有很多模拟登陆 LeetCode 的教程,但是基本都是使用 Python 来实现的.作为一个 Java 语言爱好者,因此想用 Java 来实现下.在实现的过程中,也遇到了一些坑点,故在此作为 ...

  2. SQLServer中的Merge使用

    Merge DML 作用: 数据同步 数据转换 基于源表对目标表做Insert,Update,Delete操作 Merge关键字的一些限制 使用Merge关键字只能更新一个表 源表中不能有重复的记录 ...

  3. SpringBoot使用RestTemplate 摘要认证

    SpringBoot使用RestTempate SpringBoot使用RestTemplate摘要认证 SpringBoot使用RestTemplate基础认证 SpringBoot使用RestTe ...

  4. Spring boot集成Swagger,并配置多个扫描路径

    Spring boot集成Swagger,并配置多个扫描路径 1:认识Swagger Swagger 是一个规范和完整的框架,用于生成.描述.调用和可视化 RESTful 风格的 Web 服务.总体目 ...

  5. 09-js定时器、函数

    # js定时器 通过使用 JavaScript,我们有能力作到在一个设定的时间间隔之后来执行代码,而不是在函数被调用后立即执行.我们称之为计时事件. **定时器在javascript中的作用** 1. ...

  6. JAVA一个文件写多个类

    JAVA一个文件写多个类,并且是同级类,需注意: 在一个.java文件中可以有多个同级类,  其修饰符只可以public/abstract/final/和无修饰符 public修饰的只能有一个,且必须 ...

  7. js中基本数据类型与引用数据类型的本质区别

    代码 /** * 基本数据类型:string, number, boolean, null, undefined. * * 说明: * 基本数据类型的变量是保存在栈内存中的,基本数据类型的值 * 直接 ...

  8. C# 获得系统环境

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...

  9. ubuntu18.04 设置环境变量

    1.第一步:命令行输入 sudo gedit /etc/profile 2.第二步:将你想要设置环境变量的内容追加到文件结尾 例如:export JAVA_HOME=/usr/java/latest ...

  10. 如何查看Codeforces的GYM中比赛的数据

    前置条件:黄名(rating >= 2100) 或者 紫名(rating >= 1900)并且打过30场计分的比赛. 开启:首先打开GYM的界面,如果符合要求会在右边展示出一个Coach ...