Skip to content

Instantly share code, notes, and snippets.

@WooshiiDev
Last active December 8, 2023 09:50
Show Gist options
  • Save WooshiiDev/6af0e615793f7563adaf36c2b8c56b9f to your computer and use it in GitHub Desktop.
Save WooshiiDev/6af0e615793f7563adaf36c2b8c56b9f to your computer and use it in GitHub Desktop.
Build Window for Unity, adding some flexibility into your projects! Make sure to add this to a folder named "Editor" so that Unity doesn't show errors when building your project/game
using System;
using System.Collections.Generic;
using System.IO;
using System.Diagnostics;
using UnityEditor;
using UnityEngine;
using UnityEditorInternal;
using Debug = UnityEngine.Debug;
using System.Collections;
namespace BuildTools
{
public static class BuildData
{
internal const string PATH_PREF = "_BuildFolder";
internal const string NAME_PREF = "_BuildName";
internal const string VER_PREF = "_BuildVersion";
//Settings
internal static bool HasBuildPath => EditorPrefs.HasKey(PATH_PREF);
internal static string BuildPath => EditorPrefs.GetString(PATH_PREF);
internal static string BuildName => EditorPrefs.GetString(NAME_PREF);
//Defaults
internal static string DefaultPath => Application.dataPath;
internal const string DEFAULT_NAME = "Build";
//Methods
internal static void SetBuildPath(string path) => EditorPrefs.SetString(PATH_PREF, path);
internal static void SetBuildName(string name) => EditorPrefs.SetString(NAME_PREF, name);
internal static void SetUseVersioning(int value) => EditorPrefs.SetInt(VER_PREF, value);
internal static void ClearPrefs()
{
EditorPrefs.DeleteKey(PATH_PREF);
EditorPrefs.DeleteKey(NAME_PREF);
EditorPrefs.DeleteKey(VER_PREF);
}
internal static string SelectBuildFolder() => EditorUtility.SaveFolderPanel("Build Folder Path", DefaultPath, DefaultPath);
}
internal class BuildToolsWindow : EditorWindow
{
//Build settings
private string prefBuildFolder = "";
private string prefBuildName = "";
private bool addVersioning = false;
private static BuildPlayerOptions options = new BuildPlayerOptions()
{
target = BuildTarget.StandaloneWindows,
options = BuildOptions.Development,
};
private static readonly string[] buttonLabel = { "PC", "Mac", "Both" };
// --- GUIStyles ---
private static GUIStyle largeBold;
private static GUIStyle smallBold;
private static GUIStyle smallLabel;
// --- Scene Data ---
private static ReorderableList list;
private static Vector2 scrollPosition;
private static Vector2 reorderableScrollPosition;
// --- Properties ---
private static bool HasKey => BuildData.HasBuildPath;
private void OnEnable()
{
this.name = "Build Tools";
this.minSize = new Vector2(310, this.minSize.y);
titleContent = new GUIContent()
{
text = "Build Tools",
image = EditorGUIUtility.Load("icons/d_BuildSettings.Standalone.Small.png") as Texture2D,
};
if (list == null)
{
list = new ReorderableList(null, typeof(SceneAsset), true, true, true, true);
list.drawElementCallback = (Rect rect, int index, bool isActive, bool isFocused) =>
{
SceneAsset element = (SceneAsset)list.list[index];
EditorGUI.PrefixLabel(rect, new GUIContent(index.ToString()));
list.list[index] = EditorGUI.ObjectField(
new Rect(rect.x + 18, rect.y, rect.width - 18, EditorGUIUtility.singleLineHeight),
element, typeof(SceneAsset), false);
};
list.drawHeaderCallback = (Rect rect) =>
{
rect.width *= 0.5f;
EditorGUI.LabelField(new Rect(rect.x, rect.y, rect.width / 2, rect.height), "Build Scenes");
rect.x += rect.width;
if (GUI.Button(rect, "Save Scenes to Build"))
SaveToBuild();
};
list.onAddCallback = (ReorderableList list) =>
{
list.list.Add(null);
};
}
CheckValidation();
GetBuildScenes();
EditorBuildSettings.sceneListChanged -= GetBuildScenes;
EditorBuildSettings.sceneListChanged += GetBuildScenes;
}
private void OnGUI()
{
if (largeBold == null)
{
largeBold = new GUIStyle(EditorStyles.label)
{
fontStyle = FontStyle.Bold,
alignment = TextAnchor.MiddleCenter,
fontSize = 16
};
}
if (smallLabel == null)
{
smallLabel = new GUIStyle(EditorStyles.label)
{
alignment = TextAnchor.MiddleCenter,
};
}
if (smallBold == null)
{
smallBold = new GUIStyle(smallLabel)
{
fontStyle = FontStyle.Bold
};
}
if (!HasKey)
SetupInitial();
else
{
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
DrawBuildSettings();
EditorGUILayout.EndScrollView();
}
}
#region Initalization
private void CheckValidation()
{
if (HasKey)
{
prefBuildFolder = BuildData.BuildPath;
prefBuildName = BuildData.BuildName;
}
else
{
prefBuildFolder = BuildData.DefaultPath;
prefBuildName = BuildData.DEFAULT_NAME;
BuildData.SetBuildName(prefBuildName);
}
}
private void SetupInitial()
{
GUILayout.FlexibleSpace();
{
EditorGUILayout.BeginHorizontal();
{
GUILayout.Space(10);
EditorGUILayout.BeginVertical(GUI.skin.window);
InitFolderSelection();
EditorGUILayout.EndVertical();
GUILayout.Space(10);
}
EditorGUILayout.EndHorizontal();
}
GUILayout.FlexibleSpace();
}
private void InitFolderSelection()
{
//Base info
DirectorySettings();
GUILayout.Label("Please choose a default folder for creating builds", smallLabel);
}
#endregion
#region Draw
private void DrawBuildSettings()
{
DirectorySettings();
BuildParameters();
SceneSelector();
CreateBuild();
}
private void DirectorySettings()
{
GUILayout.Label("Build Platform", largeBold);
EditorGUILayout.LabelField("Build Folder", EditorStyles.boldLabel);
EditorGUILayout.BeginHorizontal();
{
prefBuildFolder = EditorGUILayout.DelayedTextField(prefBuildFolder);
if (GUILayout.Button("Browse...", GUILayout.MaxWidth(80f)))
prefBuildFolder = BuildData.SelectBuildFolder();
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.LabelField("Build File Name", EditorStyles.boldLabel);
//Draw build path
EditorGUILayout.BeginHorizontal();
{
prefBuildName = EditorGUILayout.DelayedTextField(prefBuildName);
EditorGUI.BeginDisabledGroup(true);
EditorGUILayout.TextField(".exe", GUILayout.MaxWidth(32f));
EditorGUI.EndDisabledGroup();
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
{
addVersioning = GUILayout.Toggle(addVersioning, "Add Version to Name");
if (GUILayout.Button("Save Settings"))
{
BuildData.SetBuildName(prefBuildName);
BuildData.SetUseVersioning(addVersioning ? 1 : 0);
if (!Directory.Exists(prefBuildFolder))
prefBuildFolder = BuildData.DefaultPath;
else
BuildData.SetBuildPath(prefBuildFolder);
}
}
EditorGUILayout.EndHorizontal();
}
private void BuildParameters()
{
GUILayout.Space(16);
GUILayout.Label("Build Player Options", largeBold);
options.target = (BuildTarget)EditorGUILayout.EnumPopup("Target Platform", options.target);
options.options = (BuildOptions)EditorGUILayout.EnumPopup("Build Options", options.options);
}
//Scene
private void SceneSelector()
{
GUILayout.Space(16);
GUILayout.Label("Scene Selector", largeBold);
GUILayout.BeginHorizontal();
if (GUILayout.Button("Load all project scenes"))
GetAllScenes();
if (GUILayout.Button("Load Build Scenes"))
GetBuildScenes();
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
if (GUILayout.Button("Remove null scenes"))
{
var scenes = new List<SceneAsset>();
for (int i = 0; i < list.list.Count; i++)
{
if (list.list[i] != null)
scenes.Add((SceneAsset)list.list[i]);
}
list.list = scenes;
}
if (GUILayout.Button("Clear all scenes"))
list.list.Clear();
GUILayout.EndHorizontal();
list.DoLayoutList();
}
private void CreateBuild()
{
if (GUILayout.Button("Create Build", GUILayout.MinHeight(50)))
{
var scenes = GetScenePaths();
string build = prefBuildName;
if (addVersioning)
build += Application.version;
try
{
options.scenes = scenes;
options.locationPathName = prefBuildFolder + "/" + build + ".exe";
var player = BuildPipeline.BuildPlayer(options);
var summary = player.summary;
if (summary.result == UnityEditor.Build.Reporting.BuildResult.Succeeded)
{
Debug.Log($"Build created successfully to {summary.outputPath} in {summary.totalTime}");
if (EditorUtility.DisplayDialogComplex("Build Completed!", "Run Build?", "Yes", "No", "") == 0)
Process.Start(player.summary.outputPath);
}
}
catch (Exception e)
{
Debug.Log(e);
}
}
}
#endregion
#region Helpers
private void GetBuildScenes()
{
List<SceneAsset> scenes = new List<SceneAsset>();
foreach (var scene in EditorBuildSettings.scenes)
{
var asset = AssetDatabase.LoadAssetAtPath<SceneAsset>(scene.path);
if (asset != null)
{
if (!scenes.Contains(asset))
scenes.Add(asset);
}
}
list.list = scenes;
}
private void GetAllScenes()
{
List<SceneAsset> scenes = new List<SceneAsset>();
string[] allScenes = AssetDatabase.FindAssets("t:SceneAsset");
foreach (var scene in allScenes)
{
var asset = AssetDatabase.LoadAssetAtPath<SceneAsset>(AssetDatabase.GUIDToAssetPath(scene));
if (asset != null)
{
if (!scenes.Contains(asset))
scenes.Add(asset);
}
}
list.list = scenes;
}
private void SaveToBuild()
{
// Find valid Scene paths and make a list of EditorBuildSettingsScene
List<EditorBuildSettingsScene> editorBuildSettingsScenes = new List<EditorBuildSettingsScene>();
IList sceneList = list.list;
for (int i = 0; i < sceneList.Count; i++)
{
var scene = sceneList[i];
if (scene == null)
{
list.list.Remove(scene);
Debug.LogError("Removed null scene at index " + i);
continue;
}
SceneAsset sceneAsset = (SceneAsset)scene;
string scenePath = AssetDatabase.GetAssetPath(sceneAsset);
if (!string.IsNullOrEmpty(scenePath))
editorBuildSettingsScenes.Add(new EditorBuildSettingsScene(scenePath, true));
}
// Set the Build Settings window Scene list
EditorBuildSettings.scenes = editorBuildSettingsScenes.ToArray();
}
private string[] GetScenePaths()
{
string[] scenes = new string[EditorBuildSettings.scenes.Length];
for (int i = 0; i < scenes.Length; i++)
scenes[i] = EditorBuildSettings.scenes[i].path;
return scenes;
}
#endregion
#region Menu Items
[MenuItem("Build Tools/Build Window")]
public static void ShowWindow()
{
GetWindow(typeof(BuildToolsWindow));
}
[MenuItem("Build Tools/Default Window")]
public static void ShowDefaultWindow()
{
BuildPlayerWindow.ShowBuildPlayerWindow();
}
[MenuItem("Build Tools/Clear Build Data")]
public static void ClearPrefData()
{
BuildData.ClearPrefs();
}
#endregion
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment