// Build Maker // For making iOS and Android builds automatically at default paths outside project folder // Default Path for iOS build is /Users/${UserName}/Documents/_Builds/${Product Name}/iOS Build // Default Path for Android build is ${UserName}/Documents/_Builds/${Product Name}/Android Build // Only active scenes in build settings are included in build // No need to create any folders all folders will be created automatically // If build already exists no need to delete, it will Append existing build for iOS // If APK already exists with the same version it will be deleted and new build will be generated // If build is completed succesfully respective folders are automatically opened // Shortcut key for Generating iOS Build => Cmd + Shift + i // Shortcut key opening Build Path => Cmd + Shift + o using System; using System.IO; using UnityEngine; using System.Collections.Generic; #if UNITY_EDITOR using UnityEditor; using UnityEditor.Build.Reporting; #endif namespace HGR.Utils { public static class BuildMaker { #if UNITY_EDITOR //=================================================== // FIELDS //=================================================== private static string _version = $"{PlayerSettings.bundleVersion}"; //=================================================== // METHODS //=================================================== /// /// Method return the Build path according to the OS. /// /// The Build Target type passed. /// A string of the whole path private static string GetBuildPath(BuildTarget target, bool server) { string path = System.Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments); //One check only for MacOSX because on MAC the Documents folder is inside the path returned by the above statement if(SystemInfo.operatingSystemFamily.Equals(OperatingSystemFamily.MacOSX)) path += "/Documents"; if(!Directory.Exists(path + "/_Builds")) Directory.CreateDirectory(path + "/_Builds"); if(!Directory.Exists(path + "/_Builds/" + PlayerSettings.productName)) Directory.CreateDirectory(path + "/_Builds/" + PlayerSettings.productName); path += "/_Builds/" + PlayerSettings.productName; switch(target) { case BuildTarget.StandaloneWindows64: if(server) { if(!Directory.Exists(path + "/Server Build")) Directory.CreateDirectory(path + "/Server Build"); else { Directory.Delete(path + "/Server Build", true); Directory.CreateDirectory(path + "/Server Build"); }//else end path += "/Server Build"; }//if end else { if(!Directory.Exists(path + "/Standalone Build")) Directory.CreateDirectory(path + "/Standalone Build"); else { Directory.Delete(path + "/Standalone Build", true); Directory.CreateDirectory(path + "/Standalone Build"); }//else end path += "/Standalone Build"; }//else end break; case BuildTarget.Android: if(!Directory.Exists(path + "/Android Build")) Directory.CreateDirectory(path + "/Android Build"); path += "/Android Build"; break; }//switch end return path; }//GetBuildPath() end /// /// Method finds and returns the active scenes in build settings. /// /// Return an array of the currently active scenes. private static string[] GetActiveScenes() { List scenes = new(); foreach(EditorBuildSettingsScene scene in EditorBuildSettings.scenes) { if(scene.enabled) scenes.Add(scene.path); }//loop end return scenes.ToArray(); }//GetBuildScenes() end /// /// Method opens the OS explorer according to the path passed. /// /// The path to open. private static void OpenInExplorer(string path) => EditorUtility.RevealInFinder(path); [MenuItem("Build/Generate Standalone Build")] private static void GenerateStandaloneBuild() { if(EditorUtility.DisplayDialog("BUILD MAKER", "Do you want to Generate Standalone Build?.", "Yes", "No")) MakeStandaloneBuild(); }//GenerateAndroidBuild() end private static void MakeStandaloneBuild() { string path = GetBuildPath(BuildTarget.StandaloneWindows64, false); string name = path + $"/{PlayerSettings.productName}.exe"; BuildPlayerOptions buildPlayerOptions = new() { scenes = GetActiveScenes(), locationPathName = name, target = BuildTarget.StandaloneWindows64, subtarget = (int)StandaloneBuildSubtarget.Player, options = BuildOptions.None }; BuildReport report = BuildPipeline.BuildPlayer(buildPlayerOptions); BuildSummary summary = report.summary; if(summary.result == BuildResult.Succeeded) { OpenInExplorer(path); Debug.Log("Standalone Build Generated Successfully to path\n" + path); }//if end }//MakeStandaloneBuild() end [MenuItem("Build/Generate Server Build %#s")] private static void GenerateServerBuild() { if(EditorUtility.DisplayDialog("BUILD MAKER", "Do you want to Generate Server Build?.", "Yes", "No")) MakeServerBuild(); }//GenerateAndroidBuild() end private static void MakeServerBuild() { string path = GetBuildPath(BuildTarget.StandaloneWindows64, true); string name = path + $"/{PlayerSettings.productName}.exe"; BuildPlayerOptions buildPlayerOptions = new() { scenes = GetActiveScenes(), locationPathName = name, target = BuildTarget.StandaloneWindows64, subtarget = (int)StandaloneBuildSubtarget.Server, options = BuildOptions.None }; BuildReport report = BuildPipeline.BuildPlayer(buildPlayerOptions); BuildSummary summary = report.summary; if(summary.result == BuildResult.Succeeded) { CreateLaunchFile(path); Debug.Log("Server Build Generated Successfully to path\n" + path); // MakeAndroidBuild(); }//if end }//MakeAndroidBuild() end private static void CreateLaunchFile(string path) { path += "/Launch.bat"; string dq = "\""; string bs = @"\"; string command = $"start cmd /k {dq}.{bs}{PlayerSettings.productName}.exe -batchmode -nographics -logFile output.log{dq}"; if (!File.Exists(path)) File.WriteAllText(path, command); OpenInExplorer(path); EditorUserBuildSettings.SwitchActiveBuildTarget(BuildTargetGroup.Android, BuildTarget.Android); }//CreateLaunchFile() end [MenuItem("Build/Generate Android Build %#a")] private static void GenerateAndroidBuild() { if(EditorUtility.DisplayDialog("BUILD MAKER", "Do you want to Generate Android Build?.", "Yes", "No")) MakeAndroidBuild(); }//GenerateAndroidBuild() end /// /// Method Generates an Android Platform Build. /// private static void MakeAndroidBuild() { string BuildPath = GetBuildPath(BuildTarget.Android, false); string BuildName = BuildPath + "/" + PlayerSettings.productName + " v" + _version + ".apk"; //if same version apk exists then delete it if(File.Exists(BuildName)) File.Delete(BuildName); BuildPlayerOptions buildPlayerOptions = new() { scenes = GetActiveScenes(), locationPathName = BuildName, target = BuildTarget.Android, options = BuildOptions.None }; BuildReport report = BuildPipeline.BuildPlayer(buildPlayerOptions); BuildSummary summary = report.summary; if(summary.result == BuildResult.Succeeded) { OpenInExplorer(BuildName); Debug.Log("Build Generated Successfully for Android Platform to path\n" + BuildPath); }//if end }//MakeAndroidBuild() end /// /// Method open the path at which the build has been generated. /// [MenuItem("Build/Open Build Path %#o")] private static void OpenBuildPath() { string BuildPath = string.Empty; switch(EditorUserBuildSettings.activeBuildTarget) { case BuildTarget.Android: BuildPath = GetBuildPath(BuildTarget.Android, false); if(File.Exists(BuildPath + "/" + PlayerSettings.productName + " v" + _version+ ".apk")) { OpenInExplorer(BuildPath + "/" + PlayerSettings.productName + " v" + _version + ".apk"); return; }//if end break; }//switch end OpenInExplorer(BuildPath); }//OpenBuildPath() end #endif }//class end }//namespace end