I made this simple script that loads the scene at index 0 in the build settings when you push Play. I hope someone find it useful.
It detects when the play button is push and load the scene. Then, everything comes back to normal.
Oh! And it automatically executes itself after opening Unity and after compile scripts, so never mind about execute it. Simply put it on a Editor folder and it works.
#if UNITY_EDITOR
using UnityEditor;
using UnityEditor.SceneManagement;
[InitializeOnLoadAttribute]
public static class DefaultSceneLoader
{
static DefaultSceneLoader(){
EditorApplication.playModeStateChanged += LoadDefaultScene;
}
static void LoadDefaultScene(PlayModeStateChange state){
if (state == PlayModeStateChange.ExitingEditMode) {
EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo ();
}
if (state == PlayModeStateChange.EnteredPlayMode) {
EditorSceneManager.LoadScene (0);
}
}
}
#endif
Hereās my edits to the SceneAutoLoader that handles multiple previous scenes (still only a single master scene):
using UnityEngine;
using UnityEditor;
using UnityEditor.SceneManagement;
using System;
using System.Collections.Generic;
/// <summary>
/// Scene auto loader.
/// </summary>
/// <description>
/// This class adds a File > Scene Autoload menu containing options to select
/// a "master scene" enable it to be auto-loaded when the user presses play
/// in the editor. When enabled, the selected scene will be loaded on play,
/// then the original scene will be reloaded on stop.
///
/// Based on an idea on this thread:
/// http://forum.unity3d.com/threads/157502-Executing-first-scene-in-build-settings-when-pressing-play-button-in-editor
/// </description>
[InitializeOnLoad]
static class SceneAutoLoader
{
// Static constructor binds a playmode-changed callback.
// [InitializeOnLoad] above makes sure this gets executed.
static SceneAutoLoader() {
EditorApplication.playModeStateChanged += OnPlayModeChanged;
}
// Menu items to select the "master" scene and control whether or not to load it.
[MenuItem("File/Scene Autoload/Select Master Scene...")]
private static void SelectMasterScene() {
string masterScene = EditorUtility.OpenFilePanel("Select Master Scene", Application.dataPath, "unity");
masterScene = masterScene.Replace(Application.dataPath, "Assets"); //project relative instead of absolute path
if (!string.IsNullOrEmpty(masterScene)) {
MasterScene = masterScene;
LoadMasterOnPlay = true;
}
}
[MenuItem("File/Scene Autoload/Load Master On Play", true)]
private static bool ShowLoadMasterOnPlay() {
return !LoadMasterOnPlay;
}
[MenuItem("File/Scene Autoload/Load Master On Play")]
private static void EnableLoadMasterOnPlay() {
LoadMasterOnPlay = true;
}
[MenuItem("File/Scene Autoload/Don't Load Master On Play", true)]
private static bool ShowDontLoadMasterOnPlay() {
return LoadMasterOnPlay;
}
[MenuItem("File/Scene Autoload/Don't Load Master On Play")]
private static void DisableLoadMasterOnPlay() {
LoadMasterOnPlay = false;
}
// Play mode change callback handles the scene load/reload.
private static void OnPlayModeChanged(PlayModeStateChange state) {
if (!LoadMasterOnPlay) {
return;
}
if (!EditorApplication.isPlaying && EditorApplication.isPlayingOrWillChangePlaymode) {
// User pressed play -- autoload master scene.
SetPreviousScenes();
if (EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo()) {
try {
EditorSceneManager.OpenScene(MasterScene);
} catch {
Debug.LogError(string.Format("error: scene not found: {0}", MasterScene));
EditorApplication.isPlaying = false;
}
} else {
// User cancelled the save operation -- cancel play as well.
EditorApplication.isPlaying = false;
}
}
// isPlaying check required because cannot OpenScene while playing
if (!EditorApplication.isPlaying && !EditorApplication.isPlayingOrWillChangePlaymode) {
// User pressed stop -- reload previous scenes.
foreach (var scene in PreviousScenes) {
try {
EditorSceneManager.OpenScene(scene, OpenSceneMode.Additive);
} catch {
Debug.LogError(string.Format("Error: scene not found: {0}", scene));
}
}
}
}
private static void SetPreviousScenes() {
var scenes = new List<string>();
for (int i = 0; i < EditorSceneManager.sceneCount; i++) {
scenes.Add(EditorSceneManager.GetSceneAt(i).path);
}
PreviousScenes = scenes.ToArray();
}
// Properties are remembered as editor preferences.
private const string cEditorPrefLoadMasterOnPlay = "SceneAutoLoader.LoadMasterOnPlay";
private const string cEditorPrefMasterScene = "SceneAutoLoader.MasterScene";
private const string cEditorPrefPreviousScenes = "SceneAutoLoader.PreviousScenes";
private static bool LoadMasterOnPlay {
get { return EditorPrefs.GetBool(cEditorPrefLoadMasterOnPlay, false); }
set { EditorPrefs.SetBool(cEditorPrefLoadMasterOnPlay, value); }
}
private static string MasterScene {
get { return EditorPrefs.GetString(cEditorPrefMasterScene, "Master.unity"); }
set { EditorPrefs.SetString(cEditorPrefMasterScene, value); }
}
private static string[] PreviousScenes {
get { return EditorPrefs.GetString(cEditorPrefPreviousScenes, EditorSceneManager.GetActiveScene().path).Split(';'); }
set { EditorPrefs.SetString(cEditorPrefPreviousScenes, String.Join(";", value)); }
}
}
No, I havenāt noticed that bug at all. I did make a few more changes to set the scenes to use the same loaded state as before. Note that this new code uses some .NET 4.x features, which is out of experimental as of today since 2018.1.1f1 was released (Iām not sure if itās enabled by default now though).
using UnityEngine;
using UnityEditor;
using UnityEditor.SceneManagement;
using System;
using System.Collections.Generic;
using System.Linq;
/// <summary>
/// Scene auto loader.
/// </summary>
/// <description>
/// This class adds a File > Scene Autoload menu containing options to select
/// a "master scene" enable it to be auto-loaded when the user presses play
/// in the editor. When enabled, the selected scene will be loaded on play,
/// then the original scene will be reloaded on stop.
///
/// Based on an idea on this thread:
/// http://forum.unity3d.com/threads/157502-Executing-first-scene-in-build-settings-when-pressing-play-button-in-editor
/// </description>
[InitializeOnLoad]
public static class SceneAutoLoader
{
// Static constructor binds a playmode-changed callback.
// [InitializeOnLoad] above makes sure this gets executed.
static SceneAutoLoader() {
EditorApplication.playModeStateChanged += OnPlayModeChanged;
}
// Menu items to select the "master" scene and control whether or not to load it.
[MenuItem("File/Scene Autoload/Select Master Scene...")]
private static void SelectMasterScene() {
string masterScene = EditorUtility.OpenFilePanel("Select Master Scene", Application.dataPath, "unity");
masterScene = masterScene.Replace(Application.dataPath, "Assets"); //project relative instead of absolute path
if (!string.IsNullOrEmpty(masterScene)) {
MasterScene = masterScene;
LoadMasterOnPlay = true;
}
}
[MenuItem("File/Scene Autoload/Load Master On Play", true)]
private static bool ShowLoadMasterOnPlay() {
return !LoadMasterOnPlay;
}
[MenuItem("File/Scene Autoload/Load Master On Play")]
private static void EnableLoadMasterOnPlay() {
LoadMasterOnPlay = true;
}
[MenuItem("File/Scene Autoload/Don't Load Master On Play", true)]
private static bool ShowDontLoadMasterOnPlay() {
return LoadMasterOnPlay;
}
[MenuItem("File/Scene Autoload/Don't Load Master On Play")]
private static void DisableLoadMasterOnPlay() {
LoadMasterOnPlay = false;
}
// Play mode change callback handles the scene load/reload.
private static void OnPlayModeChanged(PlayModeStateChange state) {
if (!LoadMasterOnPlay || IsInTestMode) {
return;
}
if (!EditorApplication.isPlaying && EditorApplication.isPlayingOrWillChangePlaymode) {
// User pressed play -- autoload master scene.
PreviousScenes = BuildPreviousScenesList();
PreviousScenesLoadedState = BuildPreviousScenesLoadedList();
if (EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo()) {
try {
EditorSceneManager.OpenScene(MasterScene);
} catch {
Debug.LogError(string.Format("error: scene not found: {0}", MasterScene));
EditorApplication.isPlaying = false;
}
} else {
// User cancelled the save operation -- cancel play as well.
EditorApplication.isPlaying = false;
}
}
// isPlaying check required because cannot OpenScene while playing
if (!EditorApplication.isPlaying && !EditorApplication.isPlayingOrWillChangePlaymode) {
// User pressed stop -- reload previous scenes.
foreach (var tuple in Enumerable.Zip(PreviousScenes, PreviousScenesLoadedState, (scene, loadedState) => { return new Tuple<string, bool>(scene, loadedState); })) {
try {
if (tuple.Item2) {
EditorSceneManager.OpenScene(tuple.Item1, OpenSceneMode.Additive);
} else {
EditorSceneManager.OpenScene(tuple.Item1, OpenSceneMode.AdditiveWithoutLoading);
}
} catch {
Debug.LogError(string.Format("Error: scene not found: {0}", tuple.Item1));
}
}
}
}
private static string[] BuildPreviousScenesList() {
var result = new List<string>();
for (int i = 0; i < EditorSceneManager.sceneCount; i++) {
var scene = EditorSceneManager.GetSceneAt(i);
result.Add(scene.path);
}
return result.ToArray();
}
private static bool[] BuildPreviousScenesLoadedList() {
var result = new List<bool>();
for (int i = 0; i < EditorSceneManager.sceneCount; i++) {
var scene = EditorSceneManager.GetSceneAt(i);
result.Add(scene.isLoaded);
}
return result.ToArray();
}
// Properties are remembered as editor preferences.
private const string cEditorPrefLoadMasterOnPlay = "SceneAutoLoader.LoadMasterOnPlay";
private const string cEditorPrefMasterScene = "SceneAutoLoader.MasterScene";
private const string cEditorPrefPreviousScenes = "SceneAutoLoader.PreviousScenes";
private const string cEditorPrefPreviousScenesLoadedState = "SceneAutoLoader.PreviousScenesLoadedState";
private static bool LoadMasterOnPlay {
get { return EditorPrefs.GetBool(cEditorPrefLoadMasterOnPlay, false); }
set { EditorPrefs.SetBool(cEditorPrefLoadMasterOnPlay, value); }
}
private static string MasterScene {
get { return EditorPrefs.GetString(cEditorPrefMasterScene, "Master.unity"); }
set { EditorPrefs.SetString(cEditorPrefMasterScene, value); }
}
private static string[] PreviousScenes {
get { return EditorPrefs.GetString(cEditorPrefPreviousScenes, String.Join(";", BuildPreviousScenesList())).Split(';'); }
set { EditorPrefs.SetString(cEditorPrefPreviousScenes, String.Join(";", value)); }
}
private static bool[] PreviousScenesLoadedState {
get {
var strings = EditorPrefs.GetString(cEditorPrefPreviousScenesLoadedState, String.Join(";", BuildPreviousScenesLoadedList())).Split(';');
List<bool> result = new List<bool>(strings.Length);
foreach (var s in strings) {
result.Add(bool.Parse(s));
}
return result.ToArray();
}
set { EditorPrefs.SetString(cEditorPrefPreviousScenesLoadedState, String.Join(";", value)); }
}
public static bool IsInTestMode { get { return EditorSceneManager.GetActiveScene().path.Contains("InitTestScene"); } }
}
Unity 2017.1 added the playModeStartScene property, which allows you to easily define a scene to load first, when entering play mode.
Update: Although this is only an example, Iāve improved it some to make it more useful as is.
using UnityEditor;
using UnityEditor.SceneManagement;
/// <summary>
/// This script ensures the first scene defined in build settings (if any) is always loaded when entering play mode.
/// </summary>
[InitializeOnLoad]
public class AutoPlayModeSceneSetup
{
static AutoPlayModeSceneSetup()
{
// Execute once on Unity editor startup.
SetStartScene();
// Also execute whenever build settings change.
EditorBuildSettings.sceneListChanged += SetStartScene;
}
static void SetStartScene()
{
// At least one scene in build settings?
if (EditorBuildSettings.scenes.Length > 0)
{
// Set start scene to first scene in build settings.
EditorSceneManager.playModeStartScene = AssetDatabase.LoadAssetAtPath<SceneAsset>(EditorBuildSettings.scenes[0].path);
}
else
{
// Reset start scene.
EditorSceneManager.playModeStartScene = null;
}
}
}
Holy shit! This actually what I was waiting forever and never thought it was going to happen! This is really a game changer for multiscene setups!
This is an actually proper way to do it. Main issue with the previous workarounds was that each time play button was pressed, scene setup has to be loaded, then unloaded, and scene 0 has to be loaded again.
With this it completely bypasses first load and goes straight to the loading master scene directly! (Without firing .Awake() / .Start() and messing around scripts etc);
Moreover, for me it saves drastic amount of time due to multiscene setup not being loaded / unloaded first.
Iām so glad that I did searched for this one more time.
I have modified this a bit and added a menu tool to Set the current scene as the main start scene in the build settings. One reason it was modified was that if you reordered the build setting items, it does not update to the current scene order. So you be calling the previous scene that was at 0 index instead of the one you just moved to that index. It initially fixes itself after hitting play the second time but thats just time wasted. Now this script looks for the EditorBuildSettings.sceneListChanged callback to update the EditorSceneManager.playModeStartScene immediately so this problem goes away.
using UnityEngine;
using UnityEditor;
using UnityEditor.SceneManagement;
using System.Collections.Generic;
using System.Linq;
//This script was modified from https://discussions.unity.com/t/489673/47
[InitializeOnLoad]
public class AutoPlayModeSceneSetup
{
[MenuItem("Tools/Set As First Scene", false, 0)]
public static void SetAsFirstScene()
{
List<EditorBuildSettingsScene> editorBuildSettingsScenes = new List<EditorBuildSettingsScene>(EditorBuildSettings.scenes);
List<string> scenePaths = editorBuildSettingsScenes.Select(i => i.path).ToList();
//Add the scene to build settings if not already there; place as the first scene
if (!scenePaths.Contains(EditorSceneManager.GetActiveScene().path))
{
editorBuildSettingsScenes.Insert(0, new EditorBuildSettingsScene(EditorSceneManager.GetActiveScene().path, true));
}
else
{
//Reference the current scene
EditorBuildSettingsScene scene = new EditorBuildSettingsScene(EditorSceneManager.GetActiveScene().path, true);
int index = -1;
//Loop and find index from scene; we are doing this way cause IndexOf returns a -1 index for some reason
for(int i = 0; i < editorBuildSettingsScenes.Count; i++)
{
if(editorBuildSettingsScenes[i].path == scene.path)
{
index = i;
}
}
if (index != 0)
{
//Remove from current index
editorBuildSettingsScenes.RemoveAt(index);
//Then place as first scene in build settings
editorBuildSettingsScenes.Insert(0, scene);
}
}
//copy arrays back to build setting scenes
EditorBuildSettings.scenes = editorBuildSettingsScenes.ToArray();
}
static AutoPlayModeSceneSetup()
{
EditorBuildSettings.sceneListChanged += SceneListChanged;
}
static void SceneListChanged()
{
// Ensure at least one build scene exist.
if (EditorBuildSettings.scenes.Length == 0)
{
return;
}
//Reference the first scene
SceneAsset theScene = AssetDatabase.LoadAssetAtPath<SceneAsset>(EditorBuildSettings.scenes[0].path);
// Set Play Mode scene to first scene defined in build settings.
EditorSceneManager.playModeStartScene = theScene;
}
}
This works great!
It would be even better if there was a way to quickly reload the open (editor) scenes after loading the start scene.
Closing all the scenes and then reloading them during playmode takes an additional 30s for me.
Does anybody have an idea how to keep them in memory?
So that the startup scene is set even without the scenelist updating. I wanted to get the behavior no matter if I just started Unity or just changed the scene list. This change makes it work perfectly!
Always touching when someone takes the time to thank you for something, so I greatly appreciate it. But I am Just happy I could help people discover this great feature because it is super useful.
Itās so much better when you have multiple projects
using UnityEditor;
using UnityEngine;
using UnityEditor.SceneManagement;
/// <summary>
/// First Scene Auto Loader
/// </summary>
/// <description>
/// Based on an idea on this thread:
/// http://forum.unity3d.com/threads/157502-Executing-first-scene-in-build-settings-when-pressing-play-button-in-editor
/// </description>
[InitializeOnLoad]
public static class FirstSceneAutoLoader {
private const string NAME = "ā¢ Custom/Load First Scene";
static bool isEnabled {
get {
return EditorPrefs.GetBool(NAME, false);
}
set {
EditorPrefs.SetBool(NAME, value);
SceneListChanged();
}
}
[MenuItem(NAME, false, 0)]
static void LoadFirstScene() {
isEnabled = !isEnabled;
}
[MenuItem(NAME, true, 0)]
static bool ValidateLoadFirstScene() {
Menu.SetChecked(NAME, isEnabled);
return true;
}
static FirstSceneAutoLoader() {
SceneListChanged();
EditorBuildSettings.sceneListChanged += SceneListChanged;
}
static void SceneListChanged() {
if(!isEnabled) {
EditorSceneManager.playModeStartScene = default;
return;
}
//Ensure at least one build scene exist.
if(EditorBuildSettings.scenes.Length == 0) {
Debug.Log("No Scenes in Build Settings");
isEnabled = false;
return;
}
//Reference the first scene
SceneAsset theScene = AssetDatabase.LoadAssetAtPath<SceneAsset>(EditorBuildSettings.scenes[0].path);
// Set Play Mode scene to first scene defined in build settings.
EditorSceneManager.playModeStartScene = theScene;
}
}
This is my version so you can quickly enable/disable
Sorry to bump this again, but I ran into some issue with your bit of code (I thinkā¦)
Changing EditorSceneManager.playModeStartScene seems to be definitive, because even if I remove my script entirely, it still load my scene 0 again, and not my current scene.
I donāt know how to set it back to default.
Does someone know ?