// // BuildScript.cs // // Author: // fjy // // Copyright (c) 2020 fjy // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. using System; using System.Collections.Generic; using System.IO; using UnityEditor; using UnityEngine; namespace libx { public static class BuildScript { public static string outputPath = "../Release/" + GetPlatformName(); public static void ClearAssetBundles() { string[] allAssetBundleNames = AssetDatabase.GetAllAssetBundleNames(); for (int i = 0; i < allAssetBundleNames.Length; i++) { string text = allAssetBundleNames[i]; if (EditorUtility.DisplayCancelableProgressBar( string.Format("Clear AssetBundles {0}/{1}", i, allAssetBundleNames.Length), text, i * 1f / allAssetBundleNames.Length)) break; AssetDatabase.RemoveAssetBundleName(text, true); } EditorUtility.ClearProgressBar(); } internal static void ApplyBuildRules() { BuildRules rule = GetBuildRules(); rule.Apply(); } internal static BuildRules GetBuildRules() { return GetAsset("Assets/Res/Common/Rules.asset"); } public static void CopyAssetBundlesTo(string path, bool vfs = false) { if (!Directory.Exists(path)) { Directory.CreateDirectory(path); } var versions = new List(); if (!vfs) { versions.AddRange(Versions.LoadVersions(outputPath + "/" + Versions.Filename)); versions.Add(new VFile() {name = Versions.Filename}); versions.RemoveAt(versions.FindIndex(file => file.name.Equals(Versions.Dataname))); } else { versions.Add(new VFile() {name = Versions.Filename}); versions.Add(new VFile() {name = Versions.Dataname}); } foreach (VFile item in versions) { string src = outputPath + "/" + item.name; string dest = path + "/" + item.name; if (File.Exists(src)) { File.Copy(src, dest, true); } } AssetDatabase.Refresh(); } public static string GetPlatformName() { return GetPlatformForAssetBundles(EditorUserBuildSettings.activeBuildTarget); } private static string GetPlatformForAssetBundles(BuildTarget target) { switch (target) { case BuildTarget.Android: return "Android"; case BuildTarget.iOS: return "iOS"; case BuildTarget.WebGL: return "WebGL"; case BuildTarget.StandaloneWindows: case BuildTarget.StandaloneWindows64: return "Windows"; #if UNITY_2017_3_OR_NEWER case BuildTarget.StandaloneOSX: return "OSX"; #else case BuildTarget.StandaloneOSXIntel: case BuildTarget.StandaloneOSXIntel64: case BuildTarget.StandaloneOSXUniversal: return "OSX"; #endif default: return null; } } private static string[] GetLevelsFromBuildSettings() { List scenes = new List(); foreach (SceneAsset item in GetBuildRules().scenesInBuild) { string path = AssetDatabase.GetAssetPath(item); if (!string.IsNullOrEmpty(path)) { scenes.Add(path); } } return scenes.ToArray(); } private static string GetAssetBundleManifestFilePath() { string relativeAssetBundlesOutputPathForPlatform = Path.Combine("Asset", GetPlatformName()); return Path.Combine(relativeAssetBundlesOutputPathForPlatform, GetPlatformName()) + ".manifest"; } public static void BuildStandalonePlayer() { string outputPath = Path.Combine(Environment.CurrentDirectory, "Build/" + GetPlatformName() .ToLower()); //EditorUtility.SaveFolderPanel("Choose Location of the Built Game", "", ""); if (outputPath.Length == 0) return; string[] levels = GetLevelsFromBuildSettings(); if (levels.Length == 0) { Debug.Log("Nothing to build."); return; } string targetName = GetBuildTargetName(EditorUserBuildSettings.activeBuildTarget); if (targetName == null) return; #if UNITY_5_4 || UNITY_5_3 || UNITY_5_2 || UNITY_5_1 || UNITY_5_0 BuildOptions option = EditorUserBuildSettings.development ? BuildOptions.Development : BuildOptions.None; BuildPipeline.BuildPlayer(levels, path + targetName, EditorUserBuildSettings.activeBuildTarget, option); #else BuildPlayerOptions buildPlayerOptions = new BuildPlayerOptions { scenes = levels, locationPathName = outputPath + targetName, assetBundleManifestPath = GetAssetBundleManifestFilePath(), target = EditorUserBuildSettings.activeBuildTarget, options = EditorUserBuildSettings.development ? BuildOptions.Development : BuildOptions.None }; BuildPipeline.BuildPlayer(buildPlayerOptions); #endif } public static string CreateAssetBundleDirectory() { // Choose the output path according to the build target. if (!Directory.Exists(outputPath)) Directory.CreateDirectory(outputPath); return outputPath; } public static void BuildAssetBundles() { // Choose the output path according to the build target. string outputPath = CreateAssetBundleDirectory(); const BuildAssetBundleOptions options = BuildAssetBundleOptions.ChunkBasedCompression; BuildTarget targetPlatform = EditorUserBuildSettings.activeBuildTarget; BuildRules rules = GetBuildRules(); var builds = rules.GetBuilds(); AssetBundleManifest assetBundleManifest = BuildPipeline.BuildAssetBundles(outputPath, builds, options, targetPlatform); if (assetBundleManifest == null) { return; } Manifest manifest = GetManifest(); var dirs = new List(); var assets = new List(); string[] bundles = assetBundleManifest.GetAllAssetBundles(); var bundle2Ids = new Dictionary(); for (int index = 0; index < bundles.Length; index++) { string bundle = bundles[index]; bundle2Ids[bundle] = index; } var bundleRefs = new List(); for (int index = 0; index < bundles.Length; index++) { string bundle = bundles[index]; string[] deps = assetBundleManifest.GetAllDependencies(bundle); string path = string.Format("{0}/{1}", outputPath, bundle); if (File.Exists(path)) { using (FileStream stream = File.OpenRead(path)) { bundleRefs.Add(new BundleRef { name = bundle, id = index, deps = Array.ConvertAll(deps, input => bundle2Ids[input]), len = stream.Length, hash = assetBundleManifest.GetAssetBundleHash(bundle).ToString(), }); } } else { Debug.LogError(path + " file not exsit."); } } for (int i = 0; i < rules.ruleAssets.Length; i++) { RuleAsset item = rules.ruleAssets[i]; string path = item.path; string dir = Path.GetDirectoryName(path).Replace("\\", "/"); int index = dirs.FindIndex(o => o.Equals(dir)); if (index == -1) { index = dirs.Count; dirs.Add(dir); } if (!bundle2Ids.TryGetValue(item.bundle, out int bundle)) { throw new Exception($"{item.bundle}\n{item.path}"); } AssetRef asset = new AssetRef {bundle = bundle, dir = index, name = Path.GetFileName(path)}; assets.Add(asset); } manifest.dirs = dirs.ToArray(); manifest.assets = assets.ToArray(); manifest.bundles = bundleRefs.ToArray(); EditorUtility.SetDirty(manifest); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); string manifestBundleName = "manifest.unity3d"; builds = new[] { new AssetBundleBuild { assetNames = new[] {AssetDatabase.GetAssetPath(manifest),}, assetBundleName = manifestBundleName } }; BuildPipeline.BuildAssetBundles(outputPath, builds, options, targetPlatform); ArrayUtility.Add(ref bundles, manifestBundleName); Versions.BuildVersions(outputPath, bundles, GetBuildRules().AddVersion()); } private static string GetBuildTargetName(BuildTarget target) { string time = DateTime.Now.ToString("yyyyMMdd-HHmmss"); string name = PlayerSettings.productName + "-v" + PlayerSettings.bundleVersion + "."; switch (target) { case BuildTarget.Android: return string.Format("/{0}{1}-{2}.apk", name, GetBuildRules().version, time); case BuildTarget.StandaloneWindows: case BuildTarget.StandaloneWindows64: return string.Format("/{0}{1}-{2}.exe", name, GetBuildRules().version, time); #if UNITY_2017_3_OR_NEWER case BuildTarget.StandaloneOSX: return "/" + name + ".app"; #else case BuildTarget.StandaloneOSXIntel: case BuildTarget.StandaloneOSXIntel64: case BuildTarget.StandaloneOSXUniversal: return "/" + path + ".app"; #endif case BuildTarget.WebGL: case BuildTarget.iOS: return ""; // Add more build targets for your own. default: Debug.Log("Target not implemented."); return null; } } private static T GetAsset(string path) where T : ScriptableObject { T asset = AssetDatabase.LoadAssetAtPath(path); if (asset == null) { asset = ScriptableObject.CreateInstance(); AssetDatabase.CreateAsset(asset, path); AssetDatabase.SaveAssets(); } return asset; } public static Manifest GetManifest() { return GetAsset(Assets.ManifestAsset); } } }