Unity实现批量Build打包详解
一般来讲如果项目是PC或Android、IOS端不会有批量Build打包这样的需求,但如果项目是WebGL端可能会遇到这样的需求:不同场景打包成不同的包体,入口是前端在页面中布局的,点击链接打开相应的程序。依次手动打包比较繁琐而且需要等待很长时间,因此写了批量Build这样的功能,下班时点击Build经历漫长的夜晚,第二天上班时包体已经都打好了。
核心API是UnityEditor.BuildPipeline类中的BuildPlayer,调用该方法传入相应参数即可实现打包,我们要做的是做一个配置文件,在其中配置打包不同包体对应的数据,包含打包的场景、名称和平台等。首先构建可序列化类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | /// <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构建配置:
1 2 3 4 5 6 7 8 9 10 11 | /// <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面板,编写打包功能,以及添加、移除打包项等菜单。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 | [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打包详解的文章就介绍到这了
原文链接:https://blog.csdn.net/qq_42139931/article/details/121675391