Data Management and avoiding Singletons

A common question comes to mind when using Unity to develop games: How can I make variables from one scene accessible from another scene?

As an example, imagine you have a character selection screen where the player selects a character that is going to be use in the gameplay scene.

We came across this issue when developing Mathland. We had a level selection screen where the player selected the level that was going to be load in another scene.

Levels in Mathland are generated at runtime, thus all levels “live” in the same scene. After loading this scene, we had to check what was the selected level and load it.

Let’s use this case as an example. In the LevelSelection scene the player chooses the level with id = 2. This variable is store in a MonoBehaviour called LevelSelectorManager. Then, in the Game scene, we have to check this variable to load the map with id = 2.

There are a lot of ways to solved this problem, the most common one is to use the Singleton design pattern.

I’m not going to explain what it is or how to write it, but what it does is that: At runtime (play mode on) once the scene where a GameObject resides loads ups, it makes that GameObject persistent when changing scenes, meaning the GameObject does not get destroyed. Also it makes the GameObject unique, deleting any GameObjects with the same script if one has already load up.

If we apply the Singleton pattern to the script LevelSelecterManager, we could check the selected map id variable at the start of a script in the Game scene. Like this:

<code>

void Start()

{

CreateLevelWithID(LevelSelectorManager.Instance.selectedMapID);

}

void CreateLevelWithID(int mapID)

{

//….

}

</code>

One of the most annoying thing about this method is that it forces us to play test our game starting always at the scene where the script LevelSelectorManager resides.

If we hit Play directly at the Game scene, because there isn't any instance of LevelSelectorManager, it will crash and throw a null pointer exception.

A workound would be to also have a GameObject with this script in the Game scene and let the Singleton pattern do its job. But if our LevelSelectorManager has references that we need to set in the Inspector, or variables that designers need to adjust, it would bring us trouble in the long run.

To solve all of this mess, we are going to use ScriptablesObjects (SO).

SO can be used for many things. We are going to focus on using them to share data between scenes.

Just like any other file in the Assets window (Audios, Textures, etc), a SO is a file that contains the information that you set it up to have: ints, floats. strings, etc..

Also, just like a file, a SO can be drag and drop in a public variable in the the inspector to get it’s reference.

Because of it data persistence and easiness to obtain it references, we can use SO to store the selected level id.

To create a SO you have to, create a new script and make it a child of ScriptableObject (not MonoBehaviour) and add an attribute to the class (CreateAssetMenu) so you can create the instance as a file in the Assets window.

<code>

using UnityEngine;

[CreateAssetMenu(fileName = “GameStatus”, menuName = “Didactoons/GameStatusSO”)]

public class GameStatusSO : ScriptableObject

{

public int selectedLevelID;

}

</code>

Nota: You don’t have to use the suffix SO. We add it for readability.

With this script done, you can create an instance of this SO by going to Assets > Create > Didactoons > GameStatusSO

Finally, we only need to obtain the reference of the instance of the SO in the script LevelSelectorManager and in the script that resides in the Game scene:

<code>

public GameStatusSO gameStatus;

void Start()

{

CreateLevelWithID(gameStatus.selectedMapID);

}

void CreateLevelWithID(int mapID)

{

//….

}

</code>

If you use this method to share data between scene, you will see an overall improvement in your project. Your scenes will be cleaner and you will save a lot of time when playtesting. If you want to test another map, you can just change the value of the SO instance, you don’t have to go back to the level selector scene.

July 31, 2020

Tags: