Executing first scene in build settings when pressing play button in editor?

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)); }
    }
}
1 Like

@dfraska When I select the menu item it crashes Unity sometimes. Have you noticed this?

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"); } }
}
1 Like

Can I just say that this is priceless, thank you so much for saving me from this nightmare!

You are the real MVP! Thank you

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;
        }
    }
}
14 Likes

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.

2 Likes

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;

    }

}

Hopefully someone finds this helpful.

Thanks,
jrDev

2 Likes

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?

In case anyone is using this script like I am, you need to change static AutoPlayModeSceneSetup() from this

    static AutoPlayModeSceneSetup()
    {
EditorBuildSettings.sceneListChanged += SceneListChanged;
    }

to this

    static AutoPlayModeSceneSetup()
    {
        //Call scenelistchanged at start up.
        SceneListChanged();
       
        EditorBuildSettings.sceneListChanged += SceneListChanged;
    }

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!

Works like a charmā€¦ I endorse yoyo for president of The National Badass Association.

Agreed, the script is excellent. Saves me a ton of time.

Thanks for sharing it!

wiki version works great. Itā€™s similar to one I used on the asset store that was free 2-3 years ago that has disappeared.

Sorry for bump but I just wanted to say that Iā€™m so glad I found this solution, itā€™s absolutely amazing!

Thanks @AlternativeShit ! Nice name too ;).

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.

1 Like

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

4 Likes

Sorry to bump this again, but I ran into some issue with your bit of code (I thinkā€¦) :slight_smile:

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 ?

You need to set playModeStartScene to ā€œdefaultā€, otherwise previously set will persist.

Also, its better to just expose option from which to load or just wrap into define, instead of removing script completely.

1 Like

I donā€™t know what you mean by ā€œwrap into defineā€. Can you show me an example ?

Thanks for your answer, setting it to ā€œdefaultā€ did the trick (I didnā€™t know I just could say playModeStartScene = default)