How I Initialize Scenes In Unity

When creating anything larger than the simplest game, you will probably need some way to segment the information your player sees at once into discrete "screens". Unity calls these "Scenes". Scenes are stored in your project as .unity files and the GameObject hierarchy for anything that needs to be displayed or run at one time. In your build settings, you elect a single screen to be an entry point, and you can programmatically load other screens.

Let's say we have a main menu scene with two buttons labeled "Options" and "New Game". When you click on each of them, they load the options and the main game scenes respectively. A simple script you might have to attach to the click handlers for these buttons may look like this:

public class MainMenuManager : MonoBehaviour {
    public void LoadOptionsMenu() {
        Application.LoadLevel("options");
    }

    public void LoadGame() {
        Application.LoadLevel("main_game");
    }
}

Assuming you've attached the functions to these scripts to the click handlers for the buttons, when the user clicks, they will be immediately presented with the main menu scene or the options scene. Unity will destruct and discard all GameObjects from the main menu scene, then populate the runtime with all GameObjects configured in your options scene or game scene. The instant transition works, but ideally you'd want something a little nicer to make it less jarring for the user.

Let's assume for a moment that we have an object that implements this interface:

public interface SceneTransitioner {
    IEnumerator TransitionOut();
    IEnumerator TransitionIn();
}

These methods are coroutines that asynchronously do whatever scene transition you decide to implement. It could be a simple fade to black, or a star wipe, or a big explosion. What matters is there is a notion of transitioning into and out of a scene.

In order to make our scene transitions feel nice and consistent, when the user initiates a scene transition, somehow SceneTransitioner.TransitionOut() should be called. Once that transition is complete, the next level should be loaded. (This step becomes harder if your levels become long enough to warrant loading screens.) After the level is loaded, SceneTransitioner.TransitionOut() should be called. Let's centralize that behavior into a SceneManager script.

public class SceneManager : MonoBehavior {
    // How this is initialized is up to you. Dependency-injection would be ideal! (http://strangeioc.github.io/strangeioc/)
    private SceneTransitioner _transitioner;

    public void Start() {
        /**
         * This ensures Unity allows this object to persist between scenes
         * Find out more at: http://docs.unity3d.com/ScriptReference/Object.DontDestroyOnLoad.html
         */
        DontDestroyOnLoad(this);
    }

    public void TransitionToScene(string sceneName) {
        StartCoroutine(PerformTransitionSequence(sceneName));
    }

    private IEnumerator PerformTransitionSequence(string sceneName) {
        yield return StartCoroutine(_transitioner.TransitionOut());
        Application.LoadLevel(sceneName);
        yield return StartCoroutine(_transitioner.TransitionIn());
    }
}

Let's take stock of the moving parts here:

  • MainMenuManager - The scene-specific script that handles triggering scene loads from button clicks
  • SceneTransitioner - An (unimplemented) object that will use coroutines to perform some aesthetically-pleasing transition between levels
  • SceneManager - The singleton-ish object that mediates between Unity's Application class and our SceneTransitioner.

In order to wire these things together, MainMenuManager should now look like this:

public class MainMenuManager : MonoBehaviour {
    // Once again, how this is initialized is totally up to you.
    private SceneManager _sceneManager;

    public void LoadOptionsMenu() {
        _sceneManager.TransitionToScene("options");
    }

    public void LoadGame() {
        _sceneManager.TransitionToScene("main_game");
    }
}

Great! Now we've got our main menu waiting for a nice transition out before loading the level, and performing a transition in when the next level is loaded. However, there is one additional feature I believe is very important.

Given how we've structured things, when the game scene loads, all GameObjects immediately begin doing what they are scripted to do. The user probably would appreciate some sort of warning or countdown that the game is about to begin. However, by default that will appear and begin counting down while your SceneTransitioner is still performing TransitionIn(), depending on the length of your transition. 

To solve this problem, I decided to decouple when a scene actually initializes from the logic needed to kick things off. At a high level,. the idea is that the SceneManager issues a global event on a shared channel to inform anyone who may be interested in the scene we just loaded that things are starting. Any script that requires synchronizing with the true 'start point' of a scene extends a class and implements an abstract method that will be called when our scene is truly ready. That abstract class is quite small:

public abstract class SceneEntryPoint {
    /**
     * StrangeIoC (http://strangeioc.github.io/strangeioc/) provides a very robust publish-subscribe system. 
     * This is a pub/sub channel with zero event data.
     */
    public static readonly Signal SceneStart = new Signal();
    abstract void OnSceneStart();

    public void OnEnable() {
        SceneStart.AddListener(OnSceneStart);
    }

    public void OnDisable() {
        SceneStart.RemoveListener(OnSceneStart);
    }
}

Also, we must make a minor addition to our SceneTransitioner to ensure the pub/sub channel is dispatched upon when the transition is done.

/// SceneManager.cs
private IEnumerator PerformTransitionSequence(string sceneName) {
  yield return StartCoroutine(_transitioner.TransitionOut());
  Application.LoadLevel(sceneName);
  yield return StartCoroutine(_transitioner.TransitionIn());
  SceneEntryPoint.SceneStart.Dispatch();
}

Now when we would like to have our main game scene greet the user or alert them that the game is about to begin, we can do the following.

public class MyGame : SceneEntryPoint {
    override void OnSceneStart() {
        // Do whatever you want in here!
        // This will be callled only after the transition is complete.
        stateMachine.trigger("game_started");
    }
}

The primary omission from this walkthrough was the use of Singletons to make these objects a bit easier to use in a larger system. I would recommend making the SceneManager a singleton using the base class provided on the wiki. As always, be careful not to overuse singletons, and to limit access to them as much as possible, but always having the SceneManager available decreases the number of things you have to remember when creating a new scene!

On Web Application State

I've been thinking a bit lately on the tractability of the single-page/client-side/javascript application (or whatever you want to call it) testing problem. When trying to figure out nice ways to set up test suites and catch regressions in Squarespace, I've been kind of at a loss for complete solution. Our first attempts were, in my opinion, illustrative but misguided. Granted, it was just an experiment and not difficult to cook up, but we took the approach of attempting to verify things like animations completing successfully and pixel-perfection after simulating a series of clicks, drags and inputs. This did work for very simple cases, but fell apart when we tried to generalize. 

However, it was not misguided because it did not produce the correct results. It was misguided because it was testing the wrong thing. At Squarespace, we place a focus on achieving absolute perfection in the beauty of our presentation. We seek to leave a lasting impression and consistently sleek widgets is a big part of that. In the pursuit of such goals it's very easy to lose track of the key elements of the experience that make the application really usable.

If you view your application as an object with multiple layers, where the base layer is just the basic interactions modeled with the final HTML structure you intend to keep, you realize most applications remain surprisingly usable with just basic styling for layout and little 'pizzaz'. Clearly, these are the elements you cannot allow to regress.

In a complex applicaton, making sure that these elements are on-screen when they need to be and the user's state in your application is always consistent is _key_. Those transitions are often the highest-level state in your application. In an ideal world, your state-management code would be as ignorant of the interface as possible. So much so that you could simply instantiate and 'use' your application from code like the following:

Having your application be just a plain old object that may delegate to sub-components (that can be replaced with mocks/dummy instances for testing) may not always be practical, but it makes things a heck of a lot simpler. Because web apps are just big state-machines, In theory, if you had some object that encapsulated the current state of your application (Much like Ember's router), it would be incredibly useful to verify that whenever you call app.navigateTo('library') whatever internal state representation was updated correctly. That seems obvious, but it's never occurred to me so clearly when actually testing this behavior would involve calling the methods, then string matching on 'window.location'.

I'm aware that decoupling view from state and testing from the bottom up are nothing new, but it's very easy to forget that we can apply the same principles applied in 'regular' development to the occasionally wild world of the front-end.

Decouple your state, test your state, then move on to the pretty stuff.