Unity 实现批量Build打包

简介: Unity 实现批量Build打包

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

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

/// <summary>/// 打包任务/// </summary>[Serializable]
publicsealedclassBuildTask{
/// <summary>/// 名称/// </summary>publicstringProductName;
/// <summary>/// 目标平台/// </summary>publicBuildTargetBuildTarget;
/// <summary>/// 打包路径/// </summary>publicstringBuildPath;
/// <summary>/// 打包场景/// </summary>publicList<SceneAsset>SceneAssets=newList<SceneAsset>(0);
}

image.gif

使用ScriptableObject构建配置:

/// <summary>/// 打包配置表/// </summary>[CreateAssetMenu(fileName="New Build Profile", menuName="Build Profile")]
publicsealedclassBuildProfile : ScriptableObject{
/// <summary>/// 打包任务列表/// </summary>publicList<BuildTask>BuildTasks=newList<BuildTask>(0);
}

image.gif

image.gif

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

[CustomEditor(typeof(BuildProfile))]
publicsealedclassBuildProfileInspector : Editor{
privatereadonlyDictionary<BuildTask, bool>foldoutMap=newDictionary<BuildTask, bool>();
privateVector2scroll=Vector2.zero;
privateBuildProfileprofile;
privatevoidOnEnable()
    {
profile=targetasBuildProfile;
    }
publicoverridevoidOnInspectorGUI()
    {
GUILayout.BeginHorizontal();
        {
if (GUILayout.Button("新建", "ButtonLeft"))
            {
Undo.RecordObject(profile, "Create");
vartask=newBuildTask()
                {
ProductName="Product Name",
BuildTarget=BuildTarget.StandaloneWindows64,
BuildPath=Directory.GetParent(Application.dataPath).FullName                };
profile.BuildTasks.Add(task);
            }
if (GUILayout.Button("展开", "ButtonMid"))
            {
for (inti=0; i<profile.BuildTasks.Count; i++)
                {
foldoutMap[profile.BuildTasks[i]] =true;
                }
            }
if (GUILayout.Button("收缩", "ButtonMid"))
            {
for (inti=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("提醒", "打包需要耗费一定时间,是否确定开始?", "确定", "取消"))
                {
StringBuildersb=newStringBuilder();
sb.Append("打包报告:\r\n");
for (inti=0; i<profile.BuildTasks.Count; i++)
                    {
EditorUtility.DisplayProgressBar("Build", "Building...", i+1/profile.BuildTasks.Count);
vartask=profile.BuildTasks[i];
List<EditorBuildSettingsScene>buildScenes=newList<EditorBuildSettingsScene>();
for (intj=0; j<task.SceneAssets.Count; j++)
                        {
varscenePath=AssetDatabase.GetAssetPath(task.SceneAssets[j]);
if (!string.IsNullOrEmpty(scenePath))
                            {
buildScenes.Add(newEditorBuildSettingsScene(scenePath, true));
                            }
                        }
stringlocationPathName=$"{task.BuildPath}/{task.ProductName}";
varreport=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 (inti=0; i<profile.BuildTasks.Count; i++)
            {
vartask=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 (intj=0; j<task.SceneAssets.Count; j++)
                        {
varsceneAsset=task.SceneAssets[j];
GUILayout.BeginHorizontal();
GUILayout.Label($"{j + 1}.", GUILayout.Width(20));
task.SceneAssets[j] =EditorGUILayout.ObjectField(sceneAsset, typeof(SceneAsset), false) asSceneAsset;
if (GUILayout.Button("↑", "MiniButtonLeft", GUILayout.Width(20)))
                            {
if (j>0)
                                {
Undo.RecordObject(profile, "Move Up Scene Assets");
vartemp=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");
vartemp=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));
varnewPN=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));
varnewBT= (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);
    }
}

image.gif

image.gif

目录
相关文章
|
编解码 程序员 atlas
Unity 之 图集属性详解和代码示例 -- 拓展一键自动打包图集工具
图集只是当所有给低昂的纹理需要相同的着色器时采用的一种方法,如果一些纹理需要通过着色器应用独立的图形效果,它们就必须分离到自己的材质中,并在单独的组中打图集。
1767 0
Unity 之 图集属性详解和代码示例 -- 拓展一键自动打包图集工具
|
图形学 Android开发 iOS开发
|
API Android开发 图形学
【Unity3D】Android App Bundle(aab)打包上架Google Play介绍
总体说来,Android App Bundle打包有3种方式,每种方式都有成功上架Google Play进行测试通过,因此实用程度还是挺高的。能够理解以下内容的前提是会打apk包,知道如何生成Asset Bundle文件,这块内容可以参考我的上一篇文章。
1358 0
【Unity3D】Android App Bundle(aab)打包上架Google Play介绍
|
编解码 开发框架 Java
Unity 之 打包参数 -- Player面板属性详解
Unity Project Setting Player面板详解,看完这篇文章,彻底搞懂各个选项设置实际作用。
2525 0
Unity 之 打包参数 -- Player面板属性详解
|
4月前
|
图形学 数据安全/隐私保护 iOS开发
Unity与IOS⭐Xcode打包,上架TestFlight的完整教程
Unity与IOS⭐Xcode打包,上架TestFlight的完整教程
|
6月前
|
编解码 算法 图形学
【unity小技巧】减少Unity中的构建打包大小
【unity小技巧】减少Unity中的构建打包大小
191 0
|
7月前
|
缓存 API 图形学
【Unity 3D】AssetBundle打包、上传、加载、卸载详解及演示(附源码)
【Unity 3D】AssetBundle打包、上传、加载、卸载详解及演示(附源码)
324 0
|
7月前
|
算法 图形学 UED
【Unity 3D】AssetBundle工作流程、打包策略详解(超详细必看)
【Unity 3D】AssetBundle工作流程、打包策略详解(超详细必看)
427 0
|
图形学 Android开发
Unity打包安卓报http请求错误
Unity打包安卓报http请求错误
230 1
|
Java 图形学
Unity打包符号表 使用ndk addr2line.exe+符号表 将崩溃内存地址解析成函数名
符号表的路径,符号表发布出来的时候是一个zip文件要把它解压出来,里面会有两个文件:arm64-v8a(64位)、armeabi-v7a(32位)不过unity默认打包出来的都是64位的程序,所以这个前面加上你的真实路径+arm64-v8a\libil2cpp.sym.so就可以了。