Unity实现批量Build打包详解

一般来讲如果项目是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打包详解的文章就介绍到这了,更多相关Unity批量Build打包内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Unity 使用TexturePacker打包图集的操作方法

    目录 Unity TexturePacker 使用方法 Unity 打开Unity Ctrl+9,打开Unity商店,下载TexturePacker Importer插件 这个插件是用来解析图集文件的,是免费的 TexturePacker 打开TexturePacker,右侧是设置栏,一般基础设置就够用了 选择Unity框架: 设置界面右下角,会将文件最终算出的大小告诉你,你可以按照你的需求调整纹理格式 在高级设置界面,有布局选项 博主测试,大小限制,使用任意尺寸,Unity读不出来,比较奇怪,

  • 浅谈Unity中IOS Build Settings选项的作用

    Run in Xcode as:分Release选项和Debug选项,分别对应的是Xcode中Scheme编辑的BuildConfiguration的Debug和Release选项 Symlink Unity libraries:这是专为IOS平台用的,是一个全名叫做Symbolic Link Unity Libraries的runtime库,勾选后,xcode工程会直接在Unity的安装路径下引用Unity ios runtime library,不勾选,这些ios动态库会直接copy到xco

  • Unity C#打包AssetBundle与场景详解

    Unity2018已经把打包过程简化很多了 我们只需要关心两个API: 1.BuildPipline.BuildAssetBundles() 打包AssetBundle 2.BuildPipline.BuildPlayer() 打包场景 1.打包AssetBundle 先在资源的Inspector面板最下方 填写资源所属的AssetBundle名称和后缀(后缀可以不填) 再利用BuildPipeline.BuildAssetBundles()进行打包 2.打包Scene 利用BuildPipel

  • Unity AssetBundle打包工具示例详解

    目录 Unity批量打AB包 1.PathTool 2.CreateAB 3.ClearABLable 4.拓展 Unity批量打AB包 为了资源热更新,Unity支持将所有资源打包成AssetBundle资源,存放在SteamingAssets文件夹中: 在项目发布之前,需要将所有资源打包成.ab文件,动态加载: 在项目更新时,替换.ab资源文件,即可完成热更新: ab文件在加载时,会多一步解压缩的过程,会增加性能消耗: 打包操作属于编辑器拓展,所有脚本放在Eidtor文件夹下: 1.Path

  • Unity实现批量Build打包详解

    一般来讲如果项目是PC或Android.IOS端不会有批量Build打包这样的需求,但如果项目是WebGL端可能会遇到这样的需求:不同场景打包成不同的包体,入口是前端在页面中布局的,点击链接打开相应的程序.依次手动打包比较繁琐而且需要等待很长时间,因此写了批量Build这样的功能,下班时点击Build经历漫长的夜晚,第二天上班时包体已经都打好了. 核心API是UnityEditor.BuildPipeline类中的BuildPlayer,调用该方法传入相应参数即可实现打包,我们要做的是做一个配置

  • maven的pom文件与打包详解

    目录 一.基础配置 1.<parent> 标签 1)使用 spring-boot-starter-parent 2)使用自定义 parent 2.classifier 元素 3.classifier 的用途: 二.构建配置 字段说明 三.profile 配置 四.springboot 打包配置 打包插件 1.Maven 项目结构 2.打包时资源文件的配置 (1)打包 src/main/java 目录下的 xml (2)src/main/resources 目录下的 xml 等资源文件不被打包

  • Unity实现植物识别示例详解

    接口介绍: 可识别超过2万种常见植物和近8千种花卉,接口返回植物的名称,并支持获取识别结果对应的百科信息:还可使用EasyDL定制训练平台,定制识别植物种类.适用于拍照识图.幼教科普.图像内容分析等场景. 创建应用: 在产品服务中搜索图像识别,创建应用,获取AppID.APIKey.SecretKey信息: 查阅官方文档,以下是植物识别接口返回数据参数详情: 定义数据结构: using System; /// <summary> /// 植物识别 /// </summary> [S

  • Unity制作游戏自定义按键详解

    目录 一.效果图 二.布局 1.场景布局 2.设置面板布局 三.脚本思路 1.KeyItem脚本 2.SetKeyPanle脚本 3.player移动脚本 一.效果图 二.布局 1.场景布局 创建一个Panel 创建三个cube,Panel地板 两个cube设置一个绿色材质,调整Scale大小让其成为柱子形状,一个cube改名为player设置一个红色材质,当作玩家(用来演示操作的),修改相机位置就可以了. 2.设置面板布局 2.1新建一个空节点名字改为SetKeyPanle,修改属性 2.2在

  • Java进阶学习:jar打包详解

    文章来源:pconline 作者:fivesky jar文件听说过吗,没有?或者陌生!好,没关系,这就是我们的第一站:打包发布. 为什么会有这个玩意呢,首先,这是jar的全称:JavaTM Archive (JAR) file,是的,就是java存档文件.这有点类似zip文件,想一想它是干什么的用的呢,压缩!?没错就是要压缩,将我们原先零散的东西放到一下,重新组织,所有这些目的只有一个:方便!好了,不用管他是怎么压缩的,我们的重点是哪些是我们要压缩的(输入),还有压缩成了什么(输出),进而将它发

  • Angular.js项目中使用gulp实现自动化构建以及压缩打包详解

    gulp介绍 基于流的前端自动化构建工具,利用gulp可以提高前端开发效率,特别是在前后端分离的项目中.使用gulp能完成以下任务: 压缩html.css和js 编译less或sass等 压缩图片 启动本地静态服务器 其他 目标 一键安装项目所有的依赖模块 一键安装项目所有的依赖库 代码检查确保严格语法正确 能将angularjs的html装换成js模块并且压缩到js文件中 将所有css文件合并压缩 将所有的js文件合并压缩 动态引入资源文件 拥有开发环境和生产环境两种打包方式 工具 npm基于

  • Python实现iOS自动化打包详解步骤

    可能是最简单的iOS自动化打包方式:无需手动配置证书,无需填写配置文件名称,更无需配置Bundle Identifer,总之无需很多繁琐配置,让打包流程一句命令完成!下面将会分享两种打包方式,一种是快速打包(打包时间就在一眨眼),一种是基于shenzhen(速度会比较慢),都实现了一行命令完成打包并上传蒲公英! 一:基于编译的打包 这种打包方式应该是目前所有打包方式中最快的,就是编译工程--找到.app文件--新建Payload文件夹--拷贝.app到Payload文件夹--压缩成zip--更改

  • Linux shell利用sed如何批量更改文件名详解

    前言 本文主要给大家介绍了关于Linux shell用sed批量更改文件名的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. 示例 去除特定字符 目标:将 2017-01-01.jpg.2018-01-01.jpg 改为 20170101.jpg.20180101.jpg 方法:将所有 - 替换为空 for file in `ls | grep .jpg` do newfile=`echo $file | sed 's/-//g'` mv $file $newfile

  • 利用pyinstaller或virtualenv将python程序打包详解

    运行环境: CentOS6.5_x64 Python版本 : 2.6 使用pyinstaller打包 pyinstaller可以将python程序打包成二进制文件,打包后的文件在没有python的环境中也可以执行(但要有相关底层libc相关so文件).pyinstaller支持将python程序打包成单个文件,它所做的只是将文本转换成二进制,并不能给python提速,相反还会影响打包后程序的运行速度. 安装pyinstaller 官方网址:http://www.pyinstaller.org/

  • MySQL分表实现上百万上千万记录分布存储的批量查询设计模式详解

    我们知道可以将一个海量记录的 MySQL 大表根据主键.时间字段,条件字段等分成若干个表甚至保存在若干服务器中. 唯一的问题就是跨服务器批量查询麻烦,只能通过应用程序来解决.谈谈在Java中的解决思路.其他语言原理类似.这里说的分表不是 MySQL 5.1 的 partition,而是人为把一个表分开存在若干表或不同的服务器.1. 应用程序级别实现见示意图 electThreadManager 分表数据查询管理器它为分表的每个database or server 建立一个 thread pool

随机推荐