Initialization at runtime from a configuration asset

Often when working with Unity I have the need to automatically initialize “controller” objects which live across scenes. In the past I have typically created an initialization scene and used DontDestroyOnLoad. Whilst this works it makes debugging scenes a lot tricky since you cannot just hit the “Play” button.

It would be really great if Unity provided a runtime equivalent of InitializeOnLoad or better still a way to define scriptable objects which automatically spring into life when a certain runtime criteria is met (please vote here!).

I have been thinking of ways to circumvent this limitation and feel that I have come up with a pretty good solution. I would like to tip my hat to @Superpig for suggesting use of a ScriptableObject for game configuration.

So, in a nutshell… Each scene has a simple “InitGame” behavior that is associated with a game configuration asset. Upon awaking this object can initialize the game using the configuration asset, otherwise it can immediately self-destruct. This allows the game controller to properly initialize from any stage; be it the main menu screen or level 3.

First let’s take a look at the configuration asset. In my case I want to automatically instantiate a game controller prefab upon loading the game. Since I may want to experiment with different game controllers throughout development it is imperative that I can quickly switch to and fro and the game configuration asset seems like an ideal place to do this. This is way nicer than manually loading and adjusting every scene in my game!!

// An asset which contains game configuration data.
public sealed class GameConfiguration : ScriptableObject
{
    // The one-and-only game configuration instance.
    public static GameConfiguration Instance { get; private set; }
 
    // Prefab for game controller which will be automatically
    // instantiated upon loading game.
    public GameController controllerPrefab;
 
    private void OnEnable()
    {
        Instance = this;
    }
}

There should only ever be one game controller at any given time and this should be easily accessed, therefore the singleton design pattern is a good fit:

public class GameController : MonoBehaviour
{
    // Gets the current game controller.
    public static GameController Instance { get; private set; }
 

    // Initialize game - should only occur once during lifetime of game!!
    public static void Init(GameConfiguration configuration)
    {
        if (configuration == null) {
            throw new ArgumentNullException("Game configuration required.");
        }
        if (!Application.isPlaying) {
            throw new InvalidOperationException("Cannot initialize in edit mode.");
        }
        if (Instance != null) {
            throw new InvalidOperationException("Game has already been initialized.");
        }
 
        Instance = Instantiate(configuration.controllerPrefab) as GameController;
        DontDestroyOnLoad(Instance.gameObject);
 
        Instance.StartNewGame();
    }
 

    // Can be called to start/restart game as needed.
    public void StartNewGame()
    {
        // Game initialization logic...
    }
}

Finally we need a behavior class which must be attached to an “Init Game” object for each scene within our game. It is a good idea to save this object as a prefab so that its configuration reference can be easily updated if necessary.

public sealed class InitGameBehaviour : MonoBehaviour
{
    // The associated configuration asset.
    public GameConfiguration configuration;
 
    private void Awake()
    {
        if (GameController.Instance == null) {
            GameController.Init(this.configuration);
        }
 
        // This game object is no longer needed since the game should
        // now be properly initialized.
        DestroyObject(this.gameObject);
    }
}

A simple editor utility class can be created to create our game configuration asset:

public static class MyGameTools
{
    private const string ConfigurationAssetPath = "Assets/GameConfiguration.asset";
 
    [MenuItem("Tools/Create/Game Configuration")]
    private static void CreateGameConfiguration() {
        var configuration = ScriptableObject.CreateInstance<GameConfiguration>();
        AssetDatabase.CreateAsset(configuration, ConfigurationAssetPath);
    }
}