Ordering a Menu? Get the Manager!

This weeks quote from the XNA Guru, flashed: “You either learn how to code or you code” – 15/1/2012

The sun’s a shining and your games looking Pro but every time you hit F5 the game starts running, no nice intro video, no menu and no high score options – what gives?

A menu is in order!

But how do you implement a menu after the fact? SunBurn seems to take care of all of the loading and on a small scene there isn’t a long delay from getting the content up on the screen.

Meet The Manager

Manager’s are SunBurn’s method for implementing core functionality at a System Level in a component fashion. It is used behind the scenes to handle the Physics (CollisionManager), Avatars (AvatarManager), Audio (AudioManager) etc. This is awesome because it makes SunBurn really extensible. If you want to build your own Physics engine (or replace it with BEPU) you just need to support the functionality exposed in the corresponding Manager and then load it in to your game to replace the existing Manager.

Additionally if you want to change the flow of your game, or build your own set of System Level Functionality, Managers provide a great framework to support these processes.

You can add a template manager class to your game Via the Solution Explorer > Add > New Item

image

Then chose the SunBurn Manager from the installed SunBurn templates:

image

I mention this, with screen shots, because, until recently, for what-ever-reason I didn’t realise SunBurn actually installed these templates installed..

We are going to model a simple set of interactions for the Managers to support Menu’s and Gameplay, as such, add 3 Managers – One for “Press Start” to work out which is the controller for Navigation, a Menu Manager and a Game Manager (For the actual game play)

Scene Introductions

The Scene in SunBurn is like an act from a play – it contains all the information that the Act needs to be able to convey a story to the audience. In the non metaphor instance, SunBurn scenes encapsulate lighting, scene objects, their positioning, attached components to scene objects, etc. As such, we can use scenes to separate the display of objects and lighting that we will be using for those objects and depending on how extensively we use components, the functionality that those objects will be exhibiting. However what a Scene doesn’t encapsulate, is the functionality that specifically has been coded in to your game.cs file’s update loop – which is where our Manager gracefully introduces himself.

Setting up a 1:1 relationship between the managers and the scenes we have created allows us, the functionality and flexibility benefits of both with only the most minor of overheads.

We can move any code that needs to be triggered in Update(); within a manager that will be associated to the scene.

Press Start

In the most basic of examples we need a press start screen, to define which is the active controller on an XBLIG. For this, we will create a new scene, a new manager to be associated with the scene and a new state for the game.

Creating a new SunBurn Manager, via the installed template provides the frame work. To this we will add in a method to store the associated scene.

Scene scene;
public void LoadScene(Scene managerScene)
{
    scene = managerScene;
}

We then need to add a way to deal with the scene when we want it to be active

public Boolean Visible = false;
public void Display()
{
    // Get a reference to the games scene interface via the OwnerSceneInterface // Provided by the manager template SceneInterface thisSceneInterface = (SceneInterface)OwnerSceneInterface;

    // Add the scene thisSceneInterface.Submit(scene);

    // Thunder(Birds/Cats) are go! Visible = true;
}

The Visible variable will be used to control whether or not this manager is considered active. To make this useful, we need to prevent the Manager from executing anything in the update loop when the Manager / Scene is not being used. Add a simple check in the update loop.

public void Update(GameTime gametime)
{
    if (Visible)
    {
        // Do Stuff }
}

When the Manager isn’t in use, we need to “Hide” it.

public void Hide()
{
    // We don't want it any more Visible = false;

    // Get the reference to the scene interface SceneInterface thisSceneInterface = (SceneInterface)OwnerSceneInterface;

    // Kapow thisSceneInterface.Remove(scene);
}

These 3 additions to the Manager Template, now provide enough functionality for us to encapsulate the scene in to the Manager and control when it’s called, when functionality will be executed and by extension when the scene is loaded and added to the scene interfaces, it’s interaction, rendering of objects etc. will be active.

Exit Scene Left

To be able to swap between the managers and scenes, we will need some additional scenes added to our game. Go to the content project and add a new scene. Right Click on Scenes > Add > New Item

image

Select the SunBurn Scene from the SunBurn Installed Templates and give it a meaningful name – well you don’t have to you could call it dog if you want to but if that’s your preference, I recommend you shut down your computer and go find something else to do. I’ve always worried I would work with some one that named their variables insanely, I think I’ve just been fortunate this far.

image

If you open up the scene after you have added it, you will get something like this

image

You sure could have typed all that out. Don’t worry, it gets more complicated.

We need to load the scene in SunBurn, for the time being we will continue to do this in the main Game Class.

Scene startScene;

Then in the LoadContent method find this line

// Load the scene and add it to the managers. 

& Load up your scene

startScene = Content.Load<Scene>("Scenes/Start");

Sure, you could load the scene up separately (we will load up the other scenes latter at this same point), when they are needed and if your game hits the super-awesome-need-access-to-as-much-performance-as-possible, then you may want to do it differently – loading them as needed – but until then, this works well.

As the scenes have been loaded through the content pipeline, we can now setup the managers and load the scenes in to the local variables in the corresponding managers.

// Create a new instance of the StartMenuManger startMenuManager = new StartManager(sceneInterface);
// Load up the scene, which has been loaded through the content pipeline startMenuManager.LoadScene(startScene);
// Add the manager to the scene interface sceneInterface.AddManager(startMenuManager);

State of the Union?

With a startScene and a coresponding startMenuManager in place; the game needs to know that we will be swapping states and going to the Start State. Using the concepts of Game States, allows us to separate the flow of the game code. We have a simple set of states for this example and they will normally flow in this order.

Press Start > Main Menu > Game Play

But, if the user hits B on the Main Menu, we want them to go back to the Press Start Screen. If the User hits Back or Start from the Game Play then we want the user to be able to go back to the Main Menu State. So the possible flow looks like this

Press Start <> Main Menu <> Game Play

Facilitating movements between these different scenarios, through the use of game states, takes out some of the hassle about coding the swapping between states – e.g. you don’t have to have each manager know about each other manager to then pass across calls between each of them.

To facilitate this simply, we will just create a set of static variables (Extra Curricular – Extended it via a Manager rather than a set of static variables). Add in a new class to hold the static variables, as such

using System;

namespace SunBurnMenuExample
{
    public static class GlobalVariables {
        public static bool stateChange = false;

        public static bool stateStart = false;
        public static bool stateMenu = false;
        public static bool stateGamePlay = false;
    }
}

We are setting up 3 possible states for this example and use a control of stateChange. To change states, we set the new state to True, set the other states to false and then set stateChange to True. We need to set the stateChange variable last to ensure that if any overlapping in update loops or multi threaded interactions occur, the game will only change states once everything else has been cleaned up.

Address the change in power

To initiate the state change, we need to have a check in the Update loop of the Game.cs file

// Check if a State Change needs to occur if (GlobalVariables.stateChange)
{
    // Ensure that this doesn't execute more than it's meant to GlobalVariables.stateChange = false;

    // If the new state is the Starting State (Press Start Screen) if (GlobalVariables.stateStart)
    {
        // Hide the other screens, by invoking the hide method in the // coresponding managers (this way we don't need to know // what the last state was) mainMenuManager.Hide();
        gameManager.Hide();

        // Display the Start Menu Manager startMenuManager.Display();
    }
}

As the stateStart is the first state we want to display, we can use this method to swap states in the LoadConent method we updated earlier, that way the changes are consistent and we don’t need anything special to handle first load. Adding the following lines after the StartMenuManager has been initiated and had the scene passed to it

GlobalVariables.stateStart = true;
GlobalVariables.stateChange = true;

Finishing the Start

The Update Loop of the Start Manager should now be updated to handle the checking for the controller input; something simple like this

/// <summary> /// Called during Game.Update() to allow processing at regular intervals. /// </summary> /// <param name="gametime"></param> public void Update(GameTime gametime)
{
    if (Visible)
    {
        //Work out which controler the player has & has pressed start with if (GamePad.GetState(PlayerIndex.One).Buttons.Start == ButtonState.Pressed)
        {
            StartPressed(PlayerIndex.One);
        }

        if (GamePad.GetState(PlayerIndex.Two).Buttons.Start == ButtonState.Pressed)
        {
            StartPressed(PlayerIndex.Two);
        }

        if (GamePad.GetState(PlayerIndex.Three).Buttons.Start == ButtonState.Pressed)
        {
            StartPressed(PlayerIndex.Three);
        }

        if (GamePad.GetState(PlayerIndex.Four).Buttons.Start == ButtonState.Pressed)
        {
            StartPressed(PlayerIndex.Four);
        }
    }
}

// Once Start has been pressed private void StartPressed(PlayerIndex thePlayer)
{
    // Save the player index for latter GlobalVariables.player = thePlayer;

    // Hide the manager & remove the scene from the scene interface this.Hide();

    // Turn off this state & turn on the menu GlobalVariables.stateStart = false;
    GlobalVariables.stateMenu = true;

    // Notify the main loop that we are ready for a state change GlobalVariables.stateChange = true;
}

I’ll leave you with that Dear Reader. If your really keen to see the full example, please let me know – this should be enough to get you started with basic game play states, SunBurn Managers and swapping scenes. It certainly was a bigger write up than I was initially expecting.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>