Unity實(shí)現(xiàn)批量Build打包詳解
一般來(lái)講如果項(xiàng)目是PC或Android、IOS端不會(huì)有批量Build打包這樣的需求,但如果項(xiàng)目是WebGL端可能會(huì)遇到這樣的需求:不同場(chǎng)景打包成不同的包體,入口是前端在頁(yè)面中布局的,點(diǎn)擊鏈接打開(kāi)相應(yīng)的程序。依次手動(dòng)打包比較繁瑣而且需要等待很長(zhǎng)時(shí)間,因此寫(xiě)了批量Build這樣的功能,下班時(shí)點(diǎn)擊Build經(jīng)歷漫長(zhǎng)的夜晚,第二天上班時(shí)包體已經(jīng)都打好了。
核心API是UnityEditor.BuildPipeline類(lèi)中的BuildPlayer,調(diào)用該方法傳入相應(yīng)參數(shù)即可實(shí)現(xiàn)打包,我們要做的是做一個(gè)配置文件,在其中配置打包不同包體對(duì)應(yīng)的數(shù)據(jù),包含打包的場(chǎng)景、名稱(chēng)和平臺(tái)等。首先構(gòu)建可序列化類(lèi):
/// <summary> /// 打包任務(wù) /// </summary> [Serializable] public sealed class BuildTask { /// <summary> /// 名稱(chēng) /// </summary> public string ProductName; /// <summary> /// 目標(biāo)平臺(tái) /// </summary> public BuildTarget BuildTarget; /// <summary> /// 打包路徑 /// </summary> public string BuildPath; /// <summary> /// 打包場(chǎng)景 /// </summary> public List<SceneAsset> SceneAssets = new List<SceneAsset>(0); }
使用ScriptableObject構(gòu)建配置:
/// <summary> /// 打包配置表 /// </summary> [CreateAssetMenu(fileName = "New Build Profile", menuName = "Build Profile")] public sealed class BuildProfile : ScriptableObject { /// <summary> /// 打包任務(wù)列表 /// </summary> public List<BuildTask> BuildTasks = new List<BuildTask>(0); }
有了BuildProfile后,配置打包列表,批量打包要做的就是遍歷該列表依次調(diào)用BuildPipeline中的BuildPlayer方法。創(chuàng)建Editor類(lèi),重寫(xiě)B(tài)uildProfile的Inspector面板,編寫(xiě)打包功能,以及添加、移除打包項(xiàng)等菜單。
[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("展開(kāi)", "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("提醒", "打包需要耗費(fèi)一定時(shí)間,是否確定開(kāi)始?", "確定", "取消")) { StringBuilder sb = new StringBuilder(); sb.Append("打包報(bào)告:\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}] 打包結(jié)果: {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("打包場(chǎng)景:", 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("產(chǎn)品名稱(chēng):", 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("打包平臺(tái):", 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); } }
到此這篇關(guān)于Unity實(shí)現(xiàn)批量Build打包詳解的文章就介紹到這了,更多相關(guān)Unity批量Build打包內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C#實(shí)現(xiàn)在啟動(dòng)目錄創(chuàng)建快捷方式的方法
這篇文章主要介紹了C#實(shí)現(xiàn)在啟動(dòng)目錄創(chuàng)建快捷方式的方法,涉及C#快捷方式的創(chuàng)建技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-09-09C# 本地函數(shù)與 Lambda 表達(dá)式詳細(xì)介紹
這篇文章主要介紹了C 語(yǔ)言本地函數(shù)與 Lambda 表達(dá)式,,由于 C# 長(zhǎng)期以來(lái)一直使用 lambda,因此從差異和相似之處來(lái)看本地函數(shù)確實(shí)是有意義的,感興趣的小伙伴可以參考下面文章內(nèi)容2021-09-09C# 使用Microsoft Edge WebView2的相關(guān)總結(jié)
這篇文章主要介紹了C# 使用Microsoft Edge WebView2的相關(guān)總結(jié),幫助大家更好的理解和學(xué)習(xí)使用c#,感興趣的朋友可以了解下2021-02-02C#文件管理類(lèi)Directory實(shí)例分析
這篇文章主要介紹了C#文件管理類(lèi)Directory,非常實(shí)用,需要的朋友可以參考下2014-08-08C#中調(diào)用MySQL存儲(chǔ)過(guò)程的方法
這篇文章主要介紹了C#中調(diào)用MySQL存儲(chǔ)過(guò)程的方法,涉及C#數(shù)據(jù)庫(kù)操作的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-07-07C# SQLite數(shù)據(jù)庫(kù)入門(mén)使用說(shuō)明
這篇文章主要給大家介紹了關(guān)于C#中SQLite數(shù)據(jù)庫(kù)入門(mén)使用的相關(guān)資料,文中通過(guò)圖文以及示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-11-11