How to Manage Scene References in Unity with Scriptable Objects

Bridge the gap between Scenes and Strings with SceneObjects.

·

3 min read

Scenes are a fundamental building block of Unity projects. All of your game's objects, from lights to cameras to terrain to characters, all must exist in the context of a scene loaded into memory. These packaged scenes are loaded or unloaded as needed, making scene management an essential task when developing anything more complex than a Pong or other arcade clone.

Unfortunately, the way scenes are managed is fundamentally different for the editor and the build. In the editor, scenes are scene assets, a kind of YAML file filled with prefab GUIDs and setting overrides. In the build, they are converted into a special binary format and no longer exist as separate assets. In the editor, scenes are files like any other asset. But to load a scene in a build, one must use the string name of the scene.

SceneManager.LoadScene("SceneName");

This makes referencing scenes at runtime a surprisingly clumsy task. As a string, the name is hardcoded (or stored as a serialized string defined in the inspector). Any changes to the scene asset reference, such as renaming the scene, must be manually retyped at every point it appears in the codebase. This introduces a lot of opportunities for bugs.

The SceneObject

The SceneObject scriptable object solves these problems by wrapping the scene asset reference in an object. In the editor, a scene asset reference can be set in the inspector and the SceneObject automatically stores the name as a string when the object is validated. This SceneObject can then be used and referenced like any other asset. At runtime, the saved string will be used instead, and the scene can be acquired by calling sceneObject.Scene.

public class SceneObject : ScriptableObject
{
    ///Implicitly converts a scene object into a Scene reference.
    public static implicit operator Scene(SceneObject self)
    {
        return self.Scene;
    }
        /// Implicitly converts a scene object to a string containing the scene name.
    public static implicit operator string(SceneObject self)
    {
        return self.sceneName;
    }
    // -------------------------------------------------------------
    public string sceneName;
    public Scene Scene { get => SceneManager.GetSceneByName(sceneName); }

#if UNITY_EDITOR
    public UnityEditor.SceneAsset sceneAsset;
    // -------------------------------------------------------------

    public void OnValidate()
    {
        sceneName = sceneAsset.name;
    }
#endif

}

This SceneObject has implicit casting to both a string and a scene, allowing it to be used in almost any scene-calling function as-is, converting to either a scene or a string depending on the function.

You can treat a SceneObject the same way you would expect to be able to treat actual scenes, without dealing with the hassle of strings. Just be sure to update the SceneObject asset associated with your scene whenever you rename it.

Conclusion

Scene references in Unity can be annoying to work with and prone to typos. By wrapping the scene reference in our own custom object, we can make working with them easier, and make changing or reorganizing them less of a headache later in the project.

Did you find this article valuable?

Support The Bear Blog by becoming a sponsor. Any amount is appreciated!