一般来讲如果项目是PC或Android、IOS端不会有批量Build打包这样的需求,但如果项目是WebGL端可能会遇到这样的需求:不同场景打包成不同的包体,入口是前端在页面中布局的,点击链接打开相应的程序。依次手动打包比较繁琐而且需要等待很长时间,因此写了批量Build这样的功能,下班时点击Build经历漫长的夜晚,第二天上班时包体已经都打好了。

核心API是UnityEditor.BuildPipeline类中的BuildPlayer,调用该方法传入相应参数即可实现打包,我们要做的是做一个配置文件,在其中配置打包不同包体对应的数据,包含打包的场景、名称和平台等。首先构建可序列化类:

/// <summary>
/// 打包任务
/// </summary>
[Serializable]
public sealed class BuildTask
{/// <summary>/// 名称/// </summary>public string ProductName;/// <summary>/// 目标平台/// </summary>public BuildTarget BuildTarget;/// <summary>/// 打包路径/// </summary>public string BuildPath;/// <summary>/// 打包场景/// </summary>public List<SceneAsset> SceneAssets = new List<SceneAsset>(0);
}

使用ScriptableObject构建配置:

/// <summary>
/// 打包配置表
/// </summary>
[CreateAssetMenu(fileName = "New Build Profile", menuName = "Build Profile")]
public sealed class BuildProfile : ScriptableObject
{/// <summary>/// 打包任务列表/// </summary>public List<BuildTask> BuildTasks = new List<BuildTask>(0);
}

有了BuildProfile后,配置打包列表,批量打包要做的就是遍历该列表依次调用BuildPipeline中的BuildPlayer方法。创建Editor类,重写BuildProfile的Inspector面板,编写打包功能,以及添加、移除打包项等菜单。

[CustomEditor(typeof(BuildProfile))]
public sealed class BuildProfileInspector : Editor
{private readonly Dictionary<BuildTask, bool> foldoutMap = new Dictionary<BuildTask, bool>();private Vector2 scroll = Vector2.zero;private BuildProfile profile;private void OnEnable(){profile = target as BuildProfile;}public override void OnInspectorGUI(){GUILayout.BeginHorizontal();{if (GUILayout.Button("新建", "ButtonLeft")){Undo.RecordObject(profile, "Create");var task = new BuildTask(){ProductName = "Product Name",BuildTarget = BuildTarget.StandaloneWindows64,BuildPath = Directory.GetParent(Application.dataPath).FullName};profile.BuildTasks.Add(task);}if (GUILayout.Button("展开", "ButtonMid")){for (int i = 0; i < profile.BuildTasks.Count; i++){foldoutMap[profile.BuildTasks[i]] = true;}}if (GUILayout.Button("收缩", "ButtonMid")){for (int i = 0; i < profile.BuildTasks.Count; i++){foldoutMap[profile.BuildTasks[i]] = false;}}GUI.color = Color.yellow;if (GUILayout.Button("清空", "ButtonMid")){Undo.RecordObject(profile, "Clear");if (EditorUtility.DisplayDialog("提醒", "是否确定清空列表?", "确定", "取消")){profile.BuildTasks.Clear();}}GUI.color = Color.cyan;if (GUILayout.Button("打包", "ButtonRight")){if (EditorUtility.DisplayDialog("提醒", "打包需要耗费一定时间,是否确定开始?", "确定", "取消")){StringBuilder sb = new StringBuilder();sb.Append("打包报告:\r\n");for (int i = 0; i < profile.BuildTasks.Count; i++){EditorUtility.DisplayProgressBar("Build", "Building...", i + 1 / profile.BuildTasks.Count);var task = profile.BuildTasks[i];List<EditorBuildSettingsScene> buildScenes = new List<EditorBuildSettingsScene>();for (int j = 0; j < task.SceneAssets.Count; j++){var scenePath = AssetDatabase.GetAssetPath(task.SceneAssets[j]);if (!string.IsNullOrEmpty(scenePath)){buildScenes.Add(new EditorBuildSettingsScene(scenePath, true));}}string locationPathName = $"{task.BuildPath}/{task.ProductName}";var report = BuildPipeline.BuildPlayer(buildScenes.ToArray(), locationPathName, task.BuildTarget, BuildOptions.None);sb.Append($"[{task.ProductName}] 打包结果: {report.summary.result}\r\n");}EditorUtility.ClearProgressBar();Debug.Log(sb.ToString());}return;}GUI.color = Color.white;}GUILayout.EndHorizontal();scroll = GUILayout.BeginScrollView(scroll);{for (int i = 0; i < profile.BuildTasks.Count; i++){var task = profile.BuildTasks[i];if (!foldoutMap.ContainsKey(task)) foldoutMap.Add(task, true);GUILayout.BeginHorizontal("Badge");GUILayout.Space(12);foldoutMap[task] = EditorGUILayout.Foldout(foldoutMap[task], $"{task.ProductName}", true);GUILayout.Label(string.Empty);if (GUILayout.Button(EditorGUIUtility.IconContent("TreeEditor.Trash"), "IconButton", GUILayout.Width(20))){Undo.RecordObject(profile, "Delete Task");foldoutMap.Remove(task);profile.BuildTasks.Remove(task);break;}GUILayout.EndHorizontal();if (foldoutMap[task]){GUILayout.BeginVertical("Box");GUILayout.BeginHorizontal();GUILayout.Label("打包场景:", GUILayout.Width(70));if (GUILayout.Button(EditorGUIUtility.IconContent("Toolbar Plus More"), GUILayout.Width(28))){task.SceneAssets.Add(null);}GUILayout.EndHorizontal();if (task.SceneAssets.Count > 0){GUILayout.BeginHorizontal();GUILayout.Space(75);GUILayout.BeginVertical("Badge");for (int j = 0; j < task.SceneAssets.Count; j++){var sceneAsset = task.SceneAssets[j];GUILayout.BeginHorizontal();GUILayout.Label($"{j + 1}.", GUILayout.Width(20));task.SceneAssets[j] = EditorGUILayout.ObjectField(sceneAsset, typeof(SceneAsset), false) as SceneAsset;if (GUILayout.Button("↑", "MiniButtonLeft", GUILayout.Width(20))){if (j > 0){Undo.RecordObject(profile, "Move Up Scene Assets");var temp = task.SceneAssets[j - 1];task.SceneAssets[j - 1] = sceneAsset;task.SceneAssets[j] = temp;}}if (GUILayout.Button("↓", "MiniButtonMid", GUILayout.Width(20))){if (j < task.SceneAssets.Count - 1){Undo.RecordObject(profile, "Move Down Scene Assets");var temp = task.SceneAssets[j + 1];task.SceneAssets[j + 1] = sceneAsset;task.SceneAssets[j] = temp;}}if (GUILayout.Button(EditorGUIUtility.IconContent("Toolbar Plus"), "MiniButtonMid", GUILayout.Width(20))){Undo.RecordObject(profile, "Add Scene Assets");task.SceneAssets.Insert(j + 1, null);break;}if (GUILayout.Button(EditorGUIUtility.IconContent("Toolbar Minus"), "MiniButtonMid", GUILayout.Width(20))){Undo.RecordObject(profile, "Delete Scene Assets");task.SceneAssets.RemoveAt(j);break;}GUILayout.EndHorizontal();}GUILayout.EndVertical();GUILayout.EndHorizontal();}GUILayout.BeginHorizontal();GUILayout.Label("产品名称:", GUILayout.Width(70));var newPN = GUILayout.TextField(task.ProductName);if (task.ProductName != newPN){Undo.RecordObject(profile, "Product Name");task.ProductName = newPN;}GUILayout.EndHorizontal();GUILayout.BeginHorizontal();GUILayout.Label("打包平台:", GUILayout.Width(70));var newBT = (BuildTarget)EditorGUILayout.EnumPopup(task.BuildTarget);if (task.BuildTarget != newBT){Undo.RecordObject(profile, "Build Target");task.BuildTarget = newBT;}GUILayout.EndHorizontal();GUILayout.BeginHorizontal();GUILayout.Label("打包路径:", GUILayout.Width(70));GUILayout.TextField(task.BuildPath);if (GUILayout.Button("Browse", GUILayout.Width(60))){task.BuildPath = EditorUtility.SaveFolderPanel("Build Path", task.BuildPath, "");}GUILayout.EndHorizontal();GUILayout.EndVertical();}}}GUILayout.EndScrollView();serializedObject.ApplyModifiedProperties();if (GUI.changed) EditorUtility.SetDirty(profile);}
}

Unity 实现批量Build打包相关推荐

  1. Unity中编辑器扩展打包apk等

    记个笔记 因项目需要需要写个自定义打包工具,首先unity中提供了打包的方法我们只需要调用就可以,下面是核心的打包方法, 第一个参数是需要打包的场景, 第二个是打包路径, 第三个是打包类型比如安卓包等 ...

  2. Unity 基于Jenkins自动化打包流程

    什么是Jenkins? Jenkins是一款开源 CI&CD 软件,用于自动化各种任务,包括构建.测试和部署软件.Jenkins 支持各种运行方式,可通过系统包.Docker 或者通过一个独立 ...

  3. Vue通过build打包后 打开index.html页面是空白的

    最近在build打包vue项目遇到了几个问题,如下: 1.npm run build打包项目之后,我们通常是把dist文件里面被压缩后的static文件跟index.html提交到服务器,但最近发现直 ...

  4. recect build 打包发布后访问出现404错误的简易解决方法

    今天receat build 打包一个项目的时候,正常输入index.html访问的时候没有问题,但是刷新后出现了404错误.调试的时候正常. 文件打包后生成index.html文件和dist目录. ...

  5. vue-cli脚手架npm run build打包后图片路径问题 2018.11.21

    2018/11/21 最近在自己尝试做一个vue的项目,发现了一个打包以后出现的路径问题 使用npm run dev 可以正常打开项目,图片路径不会出错,使用脚手架npm run build 打包以后 ...

  6. vue组件中引入public文件,build打包后找不到资源报错404

    在组件中我使用object标签预览pdf文件 <object width="100%" height="100%" data="/help/he ...

  7. 上传服务器后字体文件丢失,详解Vue+elementUI build打包部署后字体图标丢失问题...

    错误描述: Vue+elementUI  build打包部署后字体图标丢失,控制台显示文件element-icons.woff和element-icons.ttf文件404 错误展现: 控制台报错截图 ...

  8. 多文件批量下载打包成.zip

    多文件批量下载打包成.zip //下载文件打包成zip压缩包@GetMapping("/zip/{id}")public void zip(@PathVariable(" ...

  9. npm run build 打包报错primordials is not defined的解决方法

    npm run build 打包报错primordials is not defined的解决方法 D:\uniapp项目\huishua_web_agent>npm run build> ...

  10. java批量文件打包成压缩成zip下载和大量数据导出excel时的处理方法

    对于我们来说,java导出数据成excel或其他数据文件,或者下载资源是开发中的家常便饭, 但是在导出的时候,如果点击一个按钮导出几百万条数据,如果不作处理的话很可能会出现一系列的问题. 这里介绍打包 ...

最新文章

  1. textarea选中行删除_如何一键删除表格空行,这个方法才最高级!
  2. access 导入 txt sql语句_[内附完整源码和文档] 基于C#和Access的智能聊天机器人
  3. phonegap html 缩放,phonegap常用事件总结(必看篇)
  4. linux知识(二)互斥量、信号量和生产者消费者模型
  5. H.264 中的相关问题
  6. Eclipse InstaSearch搜索词法 (很多并不支持)
  7. 操作系统中的hosts文件
  8. 自定义view——环形进度条,带progress值
  9. paip.php调试脱离IDE VC59
  10. IPO | 经纬恒润登科创板,好赛道下隐患依然很突出
  11. MongoDB同步原理解析
  12. Edge浏览器支持IE内核 / 增加Edge兼容性
  13. Ragel学习笔记(一)
  14. 小白电赛备战(1)msp430 f5529数据手册(中英文)
  15. mand-mobile TabPicker 多级联动选择
  16. 学习记录,linux使用wget命令实现整站下载
  17. 详解网络爬虫:网络爬虫是干什么的?有哪些应用场景?
  18. fgets()函数的使用分析
  19. matlab graythresh()函数使用的注意点
  20. ANT无线通信技术(1) 简介

热门文章

  1. 极寒天气肆虐美国中西部地区
  2. 观远数据带你乘云驾“务”,让决策更智能
  3. 2017中国之旅系列之十一:山西绵山之旅(上)
  4. 计算机技术专硕学哪些课程,计算机专业考研需要考哪些科目
  5. Spring知识整合(主要SSM)
  6. 微机原理、汇编语言与接口技术(韩晓茹)课后答案
  7. 入门级概述光学相干层析(OCT)原理
  8. Excel——输入身份证号后三位显示为0的问题
  9. Facebook登陆问题和在Android 11 上的问题
  10. python -白白入门篇