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

Win!
This rocks.

Finally i got the unofficial patch for this excellent script :smile:, Since now you need to use EditorSceneManager and SceneManger :OO, i hope someone find this useful and someone makes this script even better :smile:

using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEditor;
using UnityEditor.SceneManagement;

/// <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 execusted.
    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");
        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()
    {
        if (!LoadMasterOnPlay) return;

        if (!EditorApplication.isPlaying && EditorApplication.isPlayingOrWillChangePlaymode)
        {
            // User pressed play -- autoload master scene.
            PreviousScene = EditorSceneManager.GetActiveScene().name;
            if (EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo())
            {
                EditorSceneManager.OpenScene(MasterScene, OpenSceneMode.Single);
            }
            else
            {
                // User cancelled the save operation -- cancel play as well.
                EditorApplication.isPlaying = false;
            }
        }
        if (EditorApplication.isPlaying && !EditorApplication.isPlayingOrWillChangePlaymode)
        {
            // User pressed stop -- reload previous scene.
            if (SceneAutoLoader.PreviousScene != SceneAutoLoader.MasterScene)
                EditorApplication.update += ReloadLastScene;
        }
    }

    public static void ReloadLastScene()
    {
        Debug.Log("Reloading..");
        if (EditorSceneManager.GetActiveScene().name != SceneAutoLoader.PreviousScene)
        {
            EditorSceneManager.LoadScene(SceneAutoLoader.PreviousScene);
            EditorApplication.update -= ReloadLastScene;
        }
    }

    // Properties are remembered as editor preferences.
    private const string cEditorPrefLoadMasterOnPlay = "SceneAutoLoader.LoadMasterOnPlay";
    private const string cEditorPrefMasterScene = "SceneAutoLoader.MasterScene";
    private const string cEditorPrefPreviousScene = "SceneAutoLoader.PreviousScene";

    public 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 _previousScene;

    public static string PreviousScene
    {
        get { return EditorPrefs.GetString(cEditorPrefPreviousScene, _previousScene); }
        set
        {
            _previousScene = value;
            EditorPrefs.SetString(cEditorPrefPreviousScene, value);
        }
    }
}

If its desirable to check for MasterScene and PreviousScene existence this part of the code can be change (Havenā€™t Tested though, but it should work)

if (!EditorApplication.isPlaying && EditorApplication.isPlayingOrWillChangePlaymode)
{
    // User pressed play -- autoload master scene.
    PreviousScene = EditorSceneManager.GetActiveScene().name;
    if (EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo())
    {
        // If master scene exist, opens that scene
        foreach (Scene scene in EditorSceneManager.GetAllScenes())
        {
            if (scene.name == MasterScene)
            {
                EditorSceneManager.OpenScene(MasterScene, OpenSceneMode.Single);
                return;
            }
        }
        //Else cancel play and throw error
        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;
    }
}
else if (EditorApplication.isPlaying && !EditorApplication.isPlayingOrWillChangePlaymode)
{
    // User pressed stop .
    // If previous scene exist add ReloadLastScene to editors update
    foreach (Scene scene in EditorSceneManager.GetAllScenes())
    {
        if (scene.name == PreviousScene)
        {
            if (SceneAutoLoader.PreviousScene != SceneAutoLoader.MasterScene)
                EditorApplication.update += ReloadLastScene;
            return;
        }
    }
    //Else throw error
    Debug.LogError(string.Format("error: scene not found: {0}", PreviousScene));
}
1 Like

@inferno222 Thank you for the update on the script. It seems to work when loading the MasterScene. However, when I go back to my editor (so when I cancel Play-mode), the PreviousScene is loaded and then immediately replaced by the MasterScene.

So itā€™s like this:

  • I setup the Script using, adding the MasterScene via File ā†’ Scene Autoload
  • I execute my game (using the Play button) within my PreviousScene
  • The game starts with the MasterScene (hurray!)
  • I press the Play button once again to cancel the run
  • The game stops, switches to the PreviousScene and the immediately to the MasterScene
  • The hierarchy shows a weird overview of my two scenes (see screenshots) and the ReloadLastScene void gets called twice (see Loadingā€¦ in debug).

Any idea? Iā€™m running the latest Unity Pro on OSX 10.11.12


2495977--172365--Screen Shot 2016-02-04 at 14.05.45.png

1 Like

Iā€™ve changed the script a bit to my needs (itā€™s very very basic now, but it works with no hassle). Itā€™s not super-perfect, but it does what it does. If anyone needs it:

using UnityEditor;
using UnityEngine;
using System.Collections;
using UnityEditor.SceneManagement;

[InitializeOnLoad]
public static class LoadMainScene
{
    static LoadMainScene ()
    {
        EditorApplication.playmodeStateChanged += OnPlayModeChanged;
    }
      
    private static void OnPlayModeChanged ()
    {
        if (!EditorApplication.isPlaying && EditorApplication.isPlayingOrWillChangePlaymode) {
            if (EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo ()) {
                EditorSceneManager.OpenScene ("Assets/Scenes/PUT_MAIN_SCENE_NAME_HERE.unity");
                EditorApplication.isPlaying = true;
            } else {
                EditorApplication.isPlaying = false;
            }
        }

        if (!EditorApplication.isPlaying && !EditorApplication.isPlayingOrWillChangePlaymode) {
            EditorApplication.isPlaying = false;
            EditorSceneManager.OpenScene ("Assets/Scenes/PUT_WORKING_SCENE_NAME_HERE.unity");
        }
    }
}

Reloading previous scene didnā€™t work for me, it was happening too early. I changed the code so that reload happens after return to edit mode:

  // Play mode change callback handles the scene load/reload.
  private static void OnPlayModeChanged() {
    if (!LoadMasterOnPlay) return;

    if (!EditorApplication.isPlaying && EditorApplication.isPlayingOrWillChangePlaymode) {
      // User pressed play -- autoload master scene.
      PreviousScene = EditorSceneManager.GetActiveScene().name;
      if (EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo()) {
        EditorSceneManager.OpenScene(MasterScene, OpenSceneMode.Single);
      } else {
        // User cancelled the save operation -- cancel play as well.
        EditorApplication.isPlaying = false;
      }
    }
    if (!EditorApplication.isPlaying && !EditorApplication.isPlayingOrWillChangePlaymode) {
      // User pressed stop -- reload previous scene.
      if (SceneAutoLoader.PreviousScene != SceneAutoLoader.MasterScene)
        EditorApplication.update += ReloadLastScene;
    }
  }

  public static void ReloadLastScene() {
    Debug.Log("Reloading..");
    if (EditorSceneManager.GetActiveScene().name != SceneAutoLoader.PreviousScene) {
      EditorSceneManager.OpenScene("Assets/" + SceneAutoLoader.PreviousScene + ".unity", OpenSceneMode.Single);
    }
    EditorApplication.update -= ReloadLastScene;
  }

Made it so scene paths are used and the scene is loaded after the editor is fully out of play mode. Use this. It will work as expected.

using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEditor;
using UnityEditor.SceneManagement;
/// <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 execusted.
    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");
        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()
    {
        if (!LoadMasterOnPlay) return;
        if (!EditorApplication.isPlaying && EditorApplication.isPlayingOrWillChangePlaymode)
        {
            // User pressed play -- autoload master scene.
            PreviousScene = EditorSceneManager.GetActiveScene().path;
            if (EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo())
            {
                EditorSceneManager.OpenScene(MasterScene, OpenSceneMode.Single);
            }
            else
            {
                // User cancelled the save operation -- cancel play as well.
                EditorApplication.isPlaying = false;
            }
        }
        if (EditorApplication.isPlaying && !EditorApplication.isPlayingOrWillChangePlaymode)
        {
            // User pressed stop -- reload previous scene.
            if (SceneAutoLoader.PreviousScene != SceneAutoLoader.MasterScene)
                EditorApplication.update += ReloadLastScene;
        }
    }
    public static void ReloadLastScene()
    {
        Debug.Log("Reloading..");
        if (!EditorApplication.isPlaying && EditorSceneManager.GetActiveScene().path != SceneAutoLoader.PreviousScene)
        {
            EditorSceneManager.LoadScene(SceneAutoLoader.PreviousScene);
            EditorApplication.update -= ReloadLastScene;
        }
    }
    // Properties are remembered as editor preferences.
    private const string cEditorPrefLoadMasterOnPlay = "SceneAutoLoader.LoadMasterOnPlay";
    private const string cEditorPrefMasterScene = "SceneAutoLoader.MasterScene";
    private const string cEditorPrefPreviousScene = "SceneAutoLoader.PreviousScene";
    public 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 _previousScene;
    public static string PreviousScene
    {
        get { return EditorPrefs.GetString(cEditorPrefPreviousScene, _previousScene); }
        set
        {
            _previousScene = value;
            EditorPrefs.SetString(cEditorPrefPreviousScene, value);
        }
    }
}
3 Likes

Iā€™m kind of amazed something like this isnā€™t built into Unity yet.

Minor bug, as written above, line 88 results in: InvalidOperationException: This can only be used during play mode, please use EditorSceneManager.OpenScene() instead.

Changing it to OpenScene fixes it.

2 Likes

@pixelminer_1 ā€“ I managed to get a login to the Unity Wiki, mind if I update it with your version of this?

Latest script from pixelminer with the tweak mentioned by MV10 works for me. Thanks guys!

1 Like

I have a small fix. Inside OnPlayModeChanged(), the comparison would sometimes pass when it shouldnā€™t because Master takes the full disk location whereas Previous only takes the project path. The fix is a small string operation as follows:

    // User pressed stop -- reload previous scene.
    if ((SceneAutoLoader.PreviousScene != SceneAutoLoader.MasterScene)
        && (!SceneAutoLoader.MasterScene.EndsWith(SceneAutoLoader.PreviousScene)) )

An improvement Iā€™d love to make to this script would be to allow reloading multiple scenes, since that is common workflow with the new SceneManager. If I get around to it Iā€™ll update this thread, but I donā€™t think it will be soon. Anyway itā€™s already a huge improvement for my project. Big thanks to those involved!

I rewrote the script for my own purpose. It now just uses a shortcut ā€œCtrl+0ā€ and a SelectMasterScene menu-item. It also ensures that you will get back to the previous-scene regardless of whether you press stop or use the shortcut to stop play-mode.

Short:
- Press CTRL+0 to start play-mode from your master scene; you will return to edit-scene once play-mode has stopped

Here is the script, have fun.
StartFromMasterScene.cs:

using UnityEngine;
using UnityEditor;
using UnityEditor.SceneManagement;

//Marrt's modification of the SceneAutoLoader based on these scripts:
    //http://forum.unity3d.com/threads/executing-first-scene-in-build-settings-when-pressing-play-button-in-editor.157502/
    //http://answers.unity3d.com/questions/441246/editor-script-to-make-play-always-jump-to-a-start.html
//
//    put this script into Assets/Editor/ or it won't show up

[InitializeOnLoad]
static class StartFromMasterScene{

    private    const string shortcut = "%0";

    //static constructor subscribes to event (this is the reason for [InitializeOnLoad] at the top)  
    static StartFromMasterScene(){    EditorApplication.playmodeStateChanged += ReloadIfPlayModeHasStopped;    }
  
    static void ReloadIfPlayModeHasStopped() {

        if( EditorApplication.isPlayingOrWillChangePlaymode )    { return;    }    // we are only interested in playmode-stop
        if (!StartedPerShortcut)                                { return;    }    // if we didn't start per shortcut, there is nothing to do here
              
        if(!EditorApplication.isPlaying){
            EditorSceneManager.OpenScene(PreviousScenePath);    //reload previous scene
            StartedPerShortcut = false;                            //reset
        }
    }

    [MenuItem("Edit/StartFromMasterScene/Play "+shortcut)]
    public static void PlayFromMasterScene(){
  
        //Load Master before play-mode starts
        if ( !EditorApplication.isPlaying == true ){
          
            // kindly ask for scene save
            if(!EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo()) {return;} //abort if canceled

            PreviousScenePath = EditorSceneManager.GetActiveScene().path;    // save PATH of current scene (LoadScene needs name, OpenScene needs path)          
            EditorSceneManager.OpenScene(MasterScenePath);                    // open Master scene before starting          
            EditorApplication.isPlaying = true;                                // start
            StartedPerShortcut = true;                                        // remember that we started per shortcut
          
            Debug.Log("Previous" +PreviousScenePath);
          
        //shortcut can be used as stop-button while play-mode is active
        }else{
            EditorApplication.isPlaying = false;
        }
    }
  
//    Menu item to choose the path of the master-scene
    [MenuItem("Edit/StartFromMasterScene/Select Master Scene...")]
    private static void SelectMasterScene(){
        string masterScene = EditorUtility.OpenFilePanel("Select Master Scene", Application.dataPath, "unity");
        if (!string.IsNullOrEmpty(masterScene)){
            MasterScenePath = masterScene;
        }
    }

    // Properties need to be remembered as editor preferences, otherwise they would be lost between playmode on-offs

    //path of master scene
    private const string cEditorPrefMasterScene = "SceneAutoLoader.MasterScene";   
    private static string MasterScenePath{
        get { return EditorPrefs.GetString(cEditorPrefMasterScene, "none"); }
        set { EditorPrefs.SetString(cEditorPrefMasterScene, value); }
    }

    //path of previous scene
    private const string cEditorPrefPreviousScene = "SceneAutoLoader.PreviousScenePath";
    private static string PreviousScenePath{
        get { return EditorPrefs.GetString(cEditorPrefPreviousScene, "none"); }
        set { EditorPrefs.SetString(cEditorPrefPreviousScene, value); }
    }
  
    //remember if editor started per shortcut
    private const string cEditorPrefStartedPerShortcut = "SceneAutoLoader.StartedPerShortcut";
    private static bool StartedPerShortcut{
        get { return EditorPrefs.GetBool(cEditorPrefStartedPerShortcut, false); }
        set { EditorPrefs.SetBool(cEditorPrefStartedPerShortcut, value); }
    }
}

Edit: changed script to stop loading another scene if user pressed [cancel] on save-scene-dialog

4 Likes

Hi all,

A variant on this for those are big into additive scenes. This one will persist whatever scenes you had loaded into the editor as well as the active scene and restore them all correctly when you exit play mode. I also updated the persistence function to support setting for multiple projects.

A.

2850665ā€“208319ā€“SceneAutoLoader.cs (6.77 KB)

2 Likes

Hi,

I have continued working on AhrenMā€™s script (thanks!) and ended up with something that has stricter requirements on all GameObjects/Components (they must behave well if they go through an Awake/OnEnable/OnDisable/OnDestroy sequence), but on the other hand the entire editor UI state is preserved. Implementation here: UnityTools/SceneAutoLoader at master Ā· falldamagestudio/UnityTools Ā· GitHub

1 Like

@Kalms
Iā€™m going to try and work out how to improve your version, but Iā€™m not sure Iā€™m going succeed :wink:

@Kalms - thank you for that - itā€™s excellent!

@Kalms Your solution has been working for me but the fact that the current scene is loaded first became a problem (as you mentioned in your disclaimer). Basically, if anything in the current scene depends on the autoloaded scene then it will fail. This was the whole point of having an autoloaded scene for me.

Holy-moly! :sunglasses: What a time saver!

That moment when you realized youā€™ve been Unity wrong all this timeā€¦

:smile:

Hey Guys! Thank you for your help, I did some changes and it worked great for me. I wanted to to load the scene when you press the Standard Unity button, and when you stop I wanted to back automatically to the last Scene so I grabbed some ideas here to create this solution:

using System.IO;
using UnityEditor;

/*
Created by Fernando Bonet
*/

// ensure class initializer is called whenever scripts recompile
[InitializeOnLoadAttribute]
public static class PlayModeListener
{
    // register an event handler when the class is initialized
    static PlayModeListener()
    {
        EditorApplication.playModeStateChanged += LogPlayModeState;
    }

    private static void LogPlayModeState(PlayModeStateChange state)
    {
        switch (state)
        {
            case PlayModeStateChange.EnteredEditMode:
                OpenLastScene();
                break;
            case PlayModeStateChange.ExitingEditMode:
                RunMainScene();
                break;
            default:
                break;
        }
    }

    private static void OpenLastScene()
    {
        string lastScene = File.ReadAllText(".lastScene");
        EditorApplication.OpenScene(lastScene);
    }

    public static void RunMainScene()
    {
        string currentSceneName = EditorApplication.currentScene;
        File.WriteAllText(".lastScene", currentSceneName);
        EditorApplication.OpenScene(Scenes.ScenesPath + Scenes.Global + ".unity");
        EditorApplication.isPlaying = true;
    }
}

Thank you thank you thank you!!!