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!

Why Doesn't the Wii U Seem Attractive?

Yesterday, Nintendo announced a fifty dollar price drop for the Wii U Deluxe edition to $299. Normally, I would be over the moon for a price drop on a console I thought was far too expensive. However, my initial reaction was disappointment. "That's it?", I thought to myself. "I could get an Xbox 360 for that price.", I grumbled. Amazed at Nintendo's gall, I decided to catalog the games I would want if I had a Wii U. As it turns out, a surprising handful have my attention! After diving back into work for another half-hour or so, I started to actually think about why $299 originally seemed like too much for a brand-new console.

If I had a Wii U today, I'd probably run full-throttle to the store and pick up Pikmin 3, Monster Hunter 3 Ultimate and New Super Mario Bros. U. In 2014, I plan to salivate excessively over Monolithsoft's New Jam, Super Smash Bros., and whatever eldritch abomination comes from mixing Shin Megami Tensei and Fire Emblem. Already, I can think of 6 games I'm more than a little excited for. Even still, my knee-jerk reaction was to scoff at a $299 asking-price. Contrarily, I pre-ordered a PS4 for $399 within 15 minutes of the announcement. I believe the difference in impression originates with how well each console sells its promise for the future.

In the game of promises, Sony has an inherent advantage. They haven't had to do much to convince us that their console is the 'next big thing'. They have next-generation hardware, next-generation OS features, and shiny next-generation trailers. (Are you sensing a pattern?) On the other hand, Nintendo launched more expensive console than the Xbox 360, with plenty of current-gen ports to fill out the ranks and (arguably) the same old Mario. While it may have been Nintendo's next-gen, the only thing a Wii U had over my Xbox and PS3 at launch was a Mario game. I still haven't finished the Wii SMB, so I could just get that instead, without missing much.

The Wii U absolutely failed at selling itself as a transformative console. Nintendo has never told us what their console will do that their others will not. It took me a bit of investigating and more than a little trust to reach a point where I was comfortable with the $299 price tag. After thinking about it, I'm equally excited for Nintendo's 2014 as I am for Sony's.

Management of consumer reactions is a marketing game. Unfortunately, if others have the same reaction I did, the Wii U may not get the attention I feel it deserves even after this price drop. It's kind of a shame given the really interesting games I see coming down the pipe.

4 Things Xenoblade Chronicles Does Right in 20 Minutes

I just started Xenoblade Chronicles so I'm reserving my full opinion for now. However, I noticed a number of great things it does to make the new player experience more palatable. Attention to properly introducing the player to gameplay mechanics and keeping them engaged is sorely missing in most JRPGs. Games of any genre could stand to take notes from XC:

Save Anywhere

Save your game anywhere in the start menu.

Xenoblade Chronicles allows the player to save anywhere, from what I've seen so far. Save points have always been a pet peeve of mine and JRPGs are notorious for them. They are unnecessarily unforgiving to players who may have to stop playing at a moment's notice when real life intervenes. They do not really impede progress or increase challenge as much as they increase the frustration of defeat. I prefer my challenges delivered via the tasks at hand.

 

Story Notes

Remind yourself of why you're doing what you're doing if you haven't played in a while.

As someone who plays a lot of games, I often take breaks during the longer experiences. This leads to me coming back to a game with no idea what just happened or why I'm doing something. This quick overview of the current state of the game will prove invaluable if I decide to take a hiatus.

 

Contextual Combat Hints

Get a quick explanation of new status effects.

Xenoblade Chronicles does an excellent job of onboarding new players by allowing them to immediately access information they need about new aspects of combat. The explanation of special status effects comes not in a long-winded, overwhelming initial tutorial. Instead, these useful nuggets of information are delivered exactly when you need them.

 

Tutorial Compendium

Read tutorials again in case you accidentally dismissed one.

Finally, being able to look through any tutorials one has missed or simply forgotten about is a great asset. The tutorial compendium also provides a non-obtrusive location for the game to deliver more advanced hints and tutorials. The player is always given only the necessary level of detail, along with the tools to delve deeper should they be inclined.

I'll probably have lots more to say about the game itself in time. However, I was pleasantly surprised by this guided experience and I feel like I have a grasp on the mechanics. The game could pretty much leave me alone at this point, and I would be just fine. As someone who frequently misses key game mechanics when I experience information overload, I am pleased!