Certified studio & training center
ENFR
Lab

Retrieving the names of your scenes at runtime with Unity

Edit: this script is awesome, and if you combine it with a ScriptableObject you're ready to go! Retrieving the names of your scenes at runtime with Unity is a common problem. The…

Edit: this script is awesome, and if you combine it with a ScriptableObject you’re ready to go!

Retrieving the names of your scenes at runtime with Unity is a common problem. The API of Unity doesn’t provide an easy way do to so. With the quite recent SceneManager, I thought Unity had finally provided something to get information about your scenes you have added in the Build Settings. No way !

I faced this problem when implementing a little configuration GUI, where the user has the possibility to indicate the order the scenes will be played. Yes, I could have hard-coded this, but it’s not the elegant way in my opinion. Thus, I made some investigations to get once for all the names of my scenes.

Editor Build Settings

First of all, I needed the names of my scenes, and the only place I found them was inside the Editor Build Settings which is not documented by Unity . Oh gosh, it starts badly. This class provides a static property to get the information about the scenes available in your Build Settings (enabled AND disabled). Then, simply loop into the array and get the name of your scenes in the same order they have been added to the Build Settings !

[c-sharp]EditorBuildSettingsScene[] scenes = EditorBuildSettings.scenes ; for( int i = 0 ; i < scenes.Length ; ++i) { if( scenes[i].enabled ) Debug.Log( “Scene #” + i + “ : “ + scenes[i].path + “ is enabled“ ); else Debug.LogWarning( “Scene #” + i + “ : “ + scenes[i].path + “ is not enabled“ ); }[/c-sharp]

Oh, you thought you would be able to copy-paste this code directly in your script ? I am sorry, it’s not that easy, I would not have written this post for only 6 lines of code ! ;-)

As you may have noticed, the class is called EditorBuildSettingsScene, then, it belongs to the UnityEditor namespace, and, as you surely know, the classes in this namespace can’t be used in a standalone build.

In the editor, we can easily display this list using the MenuItem attribute like this :

[c-sharp]using UnityEngine; using UnityEditor;

public class GetScenesNamesFromEditor { [MenuItem( “Scenes Names/Save Scenes Names” )] private static void GetScenesNames() { EditorBuildSettingsScene[] scenes = EditorBuildSettings.scenes; for ( int i = 0 ; i < scenes.Length ; ++i ) { if( scenes[i].enabled ) Debug.Log( “Scene #” + i + “ : “ + scenes[i].path + “ is enabled“ ); else Debug.LogWarning( “Scene #” + i + “ : “ + scenes[i].path + “ is not enabled“ ); } } }[/c-sharp]

Now, you will tell me “That doesn’t solve the problem of retrieving the names of my scenes in my game, I can only call this function in my editor !” , yes, indeed ! Since we can’t use directly this function in our game, let’s use them indirectly ! How ? One word : Serialization !

Serializing a Scriptable Object

Let’s start by defining what is serialization  :

Serialization is the process of converting an object into a given format  in order to store this object (into a file, inside a database, …) or transmit it across a network connection link. Its main purpose is to save the state of an object in order to be able to recreate it when needed, eventually in another program context. The reverse process is called deserialization.

Source : MSDN & Wikipedia

You surely have guessed what I want to do here :

  1. Save the names of my scenes from the editor
  2. Retrieve them at runtime

There are so many ways to do this, how about using a database ? No, I am joking, here, around ten strings must be saved. I can use a XML, JSON, YAML file or even a simple text file with one scene name per line, but I wanted to take a look at Unity’s scriptable objects.

ScriptableObject is a class that allows you to store large quantities of shared data independent from script instances.

Source : Unity Manual

Most often, scriptable objects are used as assets which are only meant to store data. Nice, it’s our case here !So let’s define our data structure, containing only a simple array of strings. Feel free to add another array if you want to know whether the scenes are enabled or not.

[c-sharp]using UnityEngine;

public class ScenesList : ScriptableObject { public string[] scenesNames; }[/c-sharp]

Now, we will be able to create a new ScriptableObject using the ScriptableObject.CreateInstance, the only way to instantiate such object (the new operator doesn’t work). Then, we will fill the array of the scriptable object and store it in a place we will be able to retrieve it later to deserialize it.

Scriptable object have been designed to be easily serialized and deserialized under the form of assets. Thus, we will use the static functions provided in the AssetDatabase class.

[c-sharp][MenuItem( “Scenes Names/Save Scenes Names” )] private static void SaveScenesNames() { EditorBuildSettingsScene[] scenes = EditorBuildSettings.scenes;

// First, try to load the list if already exists ScenesList list = (ScenesList) AssetDatabase.LoadAssetAtPath(“Assets/Resources/ScenesList.asset”, typeof(ScenesList)) ;

// If doesn’t exist, create it ! if( list == null ) { list = ScriptableObject.CreateInstance<ScenesList>() ; AssetDatabase.CreateAsset( list, “Assets/Resources/ScenesList.asset” ); }

// Fill the array list.scenesNames = new string[scenes.Length]; for ( int i = 0 ; i < scenes.Length ; ++i ) { list.scenesNames[i] = scenes[i].path ; }

// Writes all unsaved asset changes to disk AssetDatabase.SaveAssets(); }[/c-sharp]

If you want to only save the enabled scenes, use a temporary List (in the System.Collections.Generic namespace) :

[c-sharp][MenuItem( “Scenes Names/Save Scenes Names” )] private static void SaveScenesNames() { EditorBuildSettingsScene[] scenes = EditorBuildSettings.scenes; List<string> scenesNames = new List<string>(); // First, try to load the list if already exists ScenesList list = (ScenesList) AssetDatabase.LoadAssetAtPath(“Assets/Resources/ScenesList.asset”, typeof(ScenesList)) ;

// If doesn’t exist, create it ! if( list == null ) { list = ScriptableObject.CreateInstance<ScenesList>() ; AssetDatabase.CreateAsset( list, “Assets/Resources/ScenesList.asset” ); }

// Fill the array for ( int i = 0 ; i < scenes.Length ; ++i ) { if( scenes[i].enabled ) scenesNames.Add( scenes[i].path ); } asset.scenesNames = scenesNames.ToArray(); // Writes all unsaved asset changes to disk AssetDatabase.SaveAssets(); }[/c-sharp]

Retrieving the names of the scenes

In the last script, I decided to store the list inside my Resources folder to easily retrieve it using a single line of code in any of my scripts :

[c-sharp]ScenesList list = Resources.Load<ScenesList>( “ScenesList” );[/c-sharp]

However, you could have stored the asset in an other folder, then, declared a public ScenesList in the script needing the list of scenes’ names, and dragged & dropped the created asset in the inspector.

[c-sharp]public class RetrieveScenesNames : MonoBehaviour { public ScenesList list ;

private void Start() { // Here I suppose you have saved the List in your Resources folder if( list == null ) list = Resources.Load<ScenesList>( “ScenesList” );

for( int i = 0 ; i < list.scenesNames.Length ; ++ i ) { Debug.Log( list.scenesNames[i] ); } } }[/c-sharp]

And … voilà ! You have the names of your scenes at runtime ! Or the path of your scenes to be more precise. If you want only the name, I suggest you to use the magic of the Regular expressions (in the System.Text.RegularExpressions namespace) . Use it when you store the data or when you use it :

[c-sharp]private void Start() { Regex regex = new Regex( @“([^/]*/)*([\w\d\-]*)\.unity” ) ; for( int i = 0 ; i < list.scenesNames.Length ; ++i ) { // Will replace the full path by the second capturing group // corresponding to the name of the scene Debug.Log( regex.Replace( list.scenesNames[i], “$2” ) ); } }[/c-sharp]

Warning

With the last script used to store into the scriptable object, keep in mind that the information won’t be dynamically updated if you make modifications of your scenes in the Build Settings or if you rename your scenes. You will have to call the SaveScenesNames using the Menu item.

If you don’t want to do this by hand, you could listen for the event EditorApplication.playmodeStateChanged and save the names of the scenes when you click on the Play Button of the editor. Take a look here for an example of how to use it.

DVC
The DVC team
Studio & R&D
// STAY IN TOUCH

Studio news,
straight to your inbox.

One email a month at most: our technical articles, project debriefs and new training sessions. No spam, one-click unsubscribe.