- Home /
Editor script to make Play button always jump to a start scene
Dear Editor Script Genius friends ..
So you have say 10 scenes in a project. In any real project it "has to" begin on a particular scene, your scene 0, "GamePrep" which loads stuff to make the game work.
When you're working on the game in the editor. You might be working on some other scene, say scene 4.
Every single time you hit play in the editor, in reality you FIRST have to click to scene zero, and then hit play.
Would it be possible, using Editor Script magic, to fix this Common Problem ?
So, when you click or keystroke "Play", it changes you to scene 0, and only then "Play"s.
BTW Here's a full simple working solution based on everyone's amazing work below!
// IN YOUR EDITOR FOLDER, have SimpleEditorUtils.cs.
// paste in this text.
// to play, HIT COMMAND-ZERO rather than command-P
// (the zero key, is near the P key, so it's easy to remember)
// simply insert the actual name of your opening scene
// "__preEverythingScene" on the second last line of code below.
using UnityEditor;
using UnityEngine;
using System.Collections;
[InitializeOnLoad]
public static class SimpleEditorUtils
{
// click command-0 to go to the prelaunch scene and then play
[MenuItem("Edit/Play-Unplay, But From Prelaunch Scene %0")]
public static void PlayFromPrelaunchScene()
{
if ( EditorApplication.isPlaying == true )
{
EditorApplication.isPlaying = false;
return;
}
EditorApplication.SaveCurrentSceneIfUserWantsTo();
EditorApplication.OpenScene(
"Assets/stuff/Scenes/__preEverythingScene.unity");
EditorApplication.isPlaying = true;
}
}
The API was different back then, so now in Unity 5.5 it's possible to use generic code that will trigger scene 0 from your build settings, and not need to hard code the string path to your base scene.
#if UNITY_EDITOR
UnityEditor.Scene$$anonymous$$anagement.EditorScene$$anonymous$$anager.SaveCurrent$$anonymous$$odifiedScenesIfUserWantsTo();
UnityEditor.Scene$$anonymous$$anagement.EditorScene$$anonymous$$anager.OpenScene(EditorBuildSettings.scenes[0].path);
EditorApplication.isPlaying = true;
#endif
Great Solution! The only thing that is missing is using the output of SaveCurrent$$anonymous$$odifiedScenesIfUserWantsTo If there are changes to the scene the user can cancel the scene change.
So you should do something like: bool isOk = EditorScene$$anonymous$$anager.SaveCurrent$$anonymous$$odifiedScenesIfUserWantsTo(); if (isOk) EditorScene$$anonymous$$anager.OpenScene("Assets/" + sceneName + ".unity");
Best answer: Go here and get this: http://wiki.unity3d.com/index.php/SceneAutoLoader
Answer by Loius · Apr 29, 2013 at 04:02 PM
Menu item with a shortcut!
using UnityEditor;
using UnityEngine;
using System.IO;
using System.Linq;
using System.Collections;
[ExecuteInEditMode]
public class PlayFromScene : EditorWindow {
[SerializeField] string lastScene="";
[SerializeField] int targetScene = 0;
[SerializeField] string waitScene = null;
[SerializeField] bool hasPlayed = false;
[MenuItem("Edit/Play From Scene %0")]
public static void Run() {
EditorWindow.GetWindow<PlayFromScene>();
}
static string[] sceneNames;
static EditorBuildSettingsScene[] scenes;
void OnEnable() {
scenes = EditorBuildSettings.scenes;
sceneNames = scenes.Select(x=>AsSpacedCamelCase(Path.GetFileNameWithoutExtension(x.path))).ToArray();
}
void Update() {
if ( !EditorApplication.isPlaying ) {
if ( null==waitScene && !string.IsNullOrEmpty(lastScene) ) {
EditorApplication.OpenScene(lastScene);
lastScene = null;
}
}
}
void OnGUI() {
if ( EditorApplication.isPlaying ) {
if ( EditorApplication.currentScene == waitScene ) {
waitScene = null;
}
return;
}
if ( EditorApplication.currentScene == waitScene ) {
EditorApplication.isPlaying = true;
}
if ( null == sceneNames ) return;
targetScene = EditorGUILayout.Popup(targetScene,sceneNames);
if(GUILayout.Button("Play")) {
lastScene = EditorApplication.currentScene;
waitScene = scenes[targetScene].path;
EditorApplication.SaveCurrentSceneIfUserWantsTo();
EditorApplication.OpenScene(waitScene);
}
}
public string AsSpacedCamelCase(string text) {
System.Text.StringBuilder sb = new System.Text.StringBuilder(text.Length*2);
sb.Append(char.ToUpper(text[0]));
for(int i=1; i<text.Length;i++) {
if ( char.IsUpper(text[i]) && text[i-1] != ' ' )
sb.Append(' ');
sb.Append (text[i]);
}
return sb.ToString();
}
}
give Tonyli a bump up too if this works for ya, i totally stole his words
[Editor's note.. and here's the original "super-elegant" solution. I have found it very useful:]
// class doesn't matter, add to anything in the Editor folder
// for any beginners reading, this is c#
[MenuItem("Edit/Play-Stop, But From Prelaunch Scene %0")]
public static void PlayFromPrelaunchScene()
{
if ( EditorApplication.isPlaying == true )
{
EditorApplication.isPlaying = false;
return;
}
EditorApplication.SaveCurrentSceneIfUserWantsTo();
EditorApplication.OpenScene("Assets/whatever/YourPrepScene.unity");
EditorApplication.isPlaying = true;
}
Sorry for the extreme delay in ticking. This is awesome, thanks! You've performed your "Help a non-Editor programmer" kind act for the day :)
Curiosities:
(1) is there an easy way in Editor to open an scene by index number? (as one can just use LoadLevel with an index, when running.) Or is it a $$anonymous$$nown Nuisance in Editor program$$anonymous$$g?
(2) Here's a challenge - in that code, how would you know "when the Play run has finished" .. one could then revert to the level that the developer was working on before hitting Apple-Zero.
I could not find either of these by searching
Thank you again !!!
Retaliatory delay! (or... just busy x_x)
There's this one by Yoyo on the wiki: http://wiki.unity3d.com/index.php/SceneAutoLoader
Then there's this one I just put together because it works the way I WANT IT TO GAGAGAGAHAGHGH AND I A$$anonymous$$ ALWAYS RIGHT (it's slightly different and appeals to my usage needs, maybe it's good for learnin or whatever)
$$anonymous$$ine will only play from scenes that are in the build settings, because I need control over some of my designers (e_e). Yoyo's uses a file dialog to pick a scene, could be worth combining them.
(code added to answer)
that'll be tree-fiddy
Please be aware that when building a game, the compiler has problems with inEditorCode when referencing the "using UnityEditor;"
You can simply avoid this by just wraping the whole code in between of the folowing compile code:
#if UNITY_EDITOR
//Code
#EndIf
That should do the trick, the code won't be compiled when you build the game.
Greetings
Chillersanim
Well, it's an EditorWindow, it has to go in the Editor folder. But yes.
Thanks for that - thanks. I did re-include the original "super short, super simple" solution.
I have found it incredibly useful. Everyone I show it to adopts use of it!
Answer by YinXiaozhou · Sep 26, 2016 at 04:08 AM
After 5.2, Unity introduced RuntimeInitializeLoadType.BeforeSceneLoad. So we can get a perfect version of this. What does the perfect mean?
Hook into the play button. So you don't need use the menu item.
Automatically use the currently scene changes in runtime, you don't even need to save them.
After exit play mode, scene go back to previously editing scene. And the all the unsaved changes still there.
So here's the code,
public static class PlayFromTheFirstScene
{
const string playFromFirstMenuStr = "Edit/Always Start From Scene 0 &p";
static bool playFromFirstScene
{
get{return EditorPrefs.HasKey(playFromFirstMenuStr) && EditorPrefs.GetBool(playFromFirstMenuStr);}
set{EditorPrefs.SetBool(playFromFirstMenuStr, value);}
}
[MenuItem(playFromFirstMenuStr, false, 150)]
static void PlayFromFirstSceneCheckMenu()
{
playFromFirstScene = !playFromFirstScene;
Menu.SetChecked(playFromFirstMenuStr, playFromFirstScene);
ShowNotifyOrLog(playFromFirstScene ? "Play from scene 0" : "Play from current scene");
}
// The menu won't be gray out, we use this validate method for update check state
[MenuItem(playFromFirstMenuStr, true)]
static bool PlayFromFirstSceneCheckMenuValidate()
{
Menu.SetChecked(playFromFirstMenuStr, playFromFirstScene);
return true;
}
// This method is called before any Awake. It's the perfect callback for this feature
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
static void LoadFirstSceneAtGameBegins()
{
if(!playFromFirstScene)
return;
if(EditorBuildSettings.scenes.Length == 0)
{
Debug.LogWarning("The scene build list is empty. Can't play from first scene.");
return;
}
foreach(GameObject go in Object.FindObjectsOfType<GameObject>())
go.SetActive(false);
SceneManager.LoadScene(0);
}
static void ShowNotifyOrLog(string msg)
{
if(Resources.FindObjectsOfTypeAll<SceneView>().Length > 0)
EditorWindow.GetWindow<SceneView>().ShowNotification(new GUIContent(msg));
else
Debug.Log(msg); // When there's no scene view opened, we just print a log
}
}
Enjoy! Oh, also, there's some other useful tricks like how to make a checkable menu item correctly, or how to show a compile error like message in the scene view. Check them out in the code if you interested.
Thank you very much! Excellent solution with modern code, thanks for sharing.
(Had to change line 35 from Count() to Length - I am using Unity 5.5).
Answer by TonyLi · Apr 19, 2013 at 02:53 PM
How about this?
In scene 0, create a game object called "Scene 0 Object" and attach this script:
public class DontDestroyOnLoad : MonoBehaviour {
void Start() {
DontDestroyOnLoad(this.GameObject);
}
}
In your other scenes, create a game object called "Play Scene 0" and attach this script:
public class PlayScene0 : MonoBehaviour {
void Start() {
if (Application.isEditor && (GameObject.Find("Scene 0 Object") == null)) {
Application.LoadLevel(0);
}
}
}
You can make this a prefab and just add the prefab to every scene.
This way you can continue using your regular play button. If you really want to run from scene 4 instead of auto-loading scene 0 on play, just disable the "Play Scene 0" object.
Your best bet is to create a custom EditorWindow with a Play button that would run this:
EditorApplication.SaveCurrentSceneIfUserWantsTo();
EditorApplication.OpenScene(scene0path);
EditorApplication.isPlaying = true;
where scene0path is the path to your scene 0.
http://docs.unity3d.com/Documentation/ScriptReference/EditorApplication.html
This is a very ugly solution. Not needed objects in the scenes, just to make some editor utility functionality.
Answer by Jamora · Oct 07, 2013 at 02:13 PM
My approach would be to directly hook into the user pressing the play button with the following code:
using UnityEngine;
using UnityEditor;
using System.Collections;
[InitializeOnLoad]
public static class LoadSceneOnPressingPlay{
[SerializeField]
public static string oldScene;
static void LoadSceneOnPressingPlay(){
EditorApplication.playmodeStateChanged += StateChange;
}
static void StateChange(){
if(EditorApplication.isPlaying){
EditorApplication.playmodeStateChanged -= StateChange;
if(!EditorApplication.isPlayingOrWillChangePlaymode){
//We're in playmode, just about to change playmode to Editor
Debug.Log("Loading original level");
EditorApplication.OpenScene(oldScene);
}else{
//We're in playmode, right after having pressed Play
oldScene = EditorApplication.currentScene;
Debug.Log("Loading first level");
Application.LoadLevel(0);
}
}
}
}
For the record, the alternative to LoadLevel in the Editor is
EditorApplication.OpenScene(EditorBuildSettings.scenes[levelIndex].path);
This script - or rather, Application.LoadLevel
- won't work if the scene in which Play is pressed is not in the build setting scene list. I have no idea why.
"directly hook into the user pressing the play button"
Sorry had a good LOL at that. Gave me thoughts of Pinhead.
The constructor shouldn't return void
try:
static LoadSceneOnPressingPlay()
{
EditorApplication.playmodeStateChanged += StateChange;
}
Answer by Xevoius · Apr 15, 2014 at 07:14 AM
I just went through setting up Fattie's Grid system with the Scene Auto Loader by User_Yoyo in a little video I put together.
Note: This movie shows how to rig it so when you press play, the pre-game scene gets loaded and then the scene you are working on gets automatically loaded after that.
https://www.youtube.com/watch?v=lWmk2QXy4wU
I also put a download link to the package in the movie details.
Let me know if you find any mistakes.
Share and enjoy.
Thanks! This is very helpful and I really appreciate it :)