Ioannis Panagopoulos blog

Tutorials on HTML5, Javascript, WinRT and .NET

XNA Game Development (Scenes and Game Services)

by Ioannis Panagopoulos

In the previous post, we have talked about decentralizing our XNA application by taking advantage of the GameComponent and DrawableGameComponent classes. In this post, we will explore two things:

 

  • The way to decouple the GameComponents' interfaces from the main game logic by using "Game Services".
  • The way to create "Scenes". Scenes in games are those displays that occur before the game starts with th game menu, the help screen, etc etc.

 

Let's start with Game Services. In the project of the previous post (also available here) in the CrazyIvanSprite class in the constructor we have a call to the game.Services.GetService method as follows:

public CrazyIvanSprite(Game game)
    : base(game)
{
    _ivanPosition = new Vector2(400, 300);
    _game = game;
    _ivan = _game.Content.Load<Texture2D>("Ivan");
    _spriteBatch = (SpriteBatch)game.Services.GetService(typeof(SpriteBatch));
}

Note that this call retrieves the SpriteBatch object in order to use it later when rendering the sprite. Where does it find it? Well in the main game loop (GameMainLoop class), in the LoadContent method, where all the initializations take place, we find a call to Services.AddService method

protected override void LoadContent(){
    spriteBatch = new SpriteBatch(GraphicsDevice);
    Services.AddService(typeof(SpriteBatch), spriteBatch);
    ...
}

In other words, the main game loop has placed an object in the game's Services and this object was retrieved by its type by the sprite that needed it. You may be wondering at this point why is this different from passing the desired object as a method parameter or via a public accessor property to the sprite. Well, technically there is no difference. 

The thing is that by using game services the main game loop does not have to know the interfacing details of each sprite. On the contrary it places the objects that may be needed to the services pool and announces that they are there and can be retrieved by anybody that wants to use them. Moreover,any sprite that wants to make something available to anybody can add an object to the services pool.

Now you may be wondering why this functionality holds the name of "Services" when it seems that it is merely a way to make objects available to game components. This is because the explanation so far has used GameServices as a mean to pass objects between sprites. The ideal design and implementation of a game specifies that the Game Services mechanism should be used as follows:

 

  • Design your game and decide which functionality will be needed to be provided by the main game loop to your sprites.
  • Write an Interface defining the methods for this functionality (for example IGraphicsServices).
  • Write a class implementing this interface (for example myGraphicsService).

 

Having those at some point during initialization of the game instanstiate the class and provide it as a service:

protected override void LoadContent(){
    myGraphicsService GService = new myGraphicsService ();
    Services.AddService(typeof(IGraphicsServices), GService );
    ...
}

Now the term "Services" makes more sense since the game announces that there is a service in the game that provides the methods of the IGraphicsService interface. The consumer sprites of the service can now retrieve it as follows:

public ASprite(Game game)
    : base(game)
{
    ...
    IGraphicsService GService = (IGraphicsService )game.Services.GetService(typeof(IGraphicsService ));
}

Lets move now to the way we implement "Scenes" in our game. As previously mentioned, scenes are the game screens that appear during th lifecycle of the game (eg the main menu scene, the main game scene, the game over scene, the help scene etc). Also note that in XNA we do not have the GUI goodies that we are used to in WinForms or WPF meaning that we have to implement everything from scratch.

As a reminder, from the previous post we have seen that everything in the main game scene is a DrawableGameComponent. In fact, the same holds for every other scene in the game. For example a flashing text string in a typical game menu saying "Press any key to begin" is a DrawbleGameComponent having a timer that in specific intervals changes the color of the text. Also, clicking on a menu selection is not caught by any mouse handler but on the contrary the mouse position is polled during the Update phase of the main game loop and the programmer has to check to see whether the mouse pointer falls into the area where the specific menu item is.

So other scenes just like the main game scene consist also of a set of sprites that interact to each other and to the user's input. Since we can control when a DrawableComponent will be shown on screen by its Visible property, we can make the DrawableComponents that compose the game scene all visible and updatable when we are at the game scene and make all other compoents not visible and vice versa when we are at another scene.

This is a solution but there is a better one. We will follow the Main game loop's architecture by creating a class Scene as follows:

public class Scene:DrawableGameComponent
{
    public List<GameComponent> SceneComponents { get; set; }
    public Scene(Game game) : base(game){
        SceneComponents = new List<GameComponent>();
        Visible = false;
        Enabled = false;
    }
    public void Show(){
        Enabled = true;
        Visible = true;
    }
    public void Hide(){
        Enabled = false;
        Visible = false;
    }
    public void Pause(){
        Enabled = false;
        Visible = true;
    }
    public override void Update(GameTime gameTime){
        foreach (GameComponent Component in SceneComponents)
        {
            if (Component.Enabled) Component.Update(gameTime);
        }
        base.Update(gameTime);
    }
    public override void Draw(GameTime gameTime){
        foreach (GameComponent Component in SceneComponents)
        {
            if (Component is DrawableGameComponent)
            {
                if ((Component as DrawableGameComponent).Visible)
                (Component as DrawableGameComponent).Draw(gameTime);
            }
        }
        base.Draw(gameTime);
    }
}

As you can see your scene has a list of DrawableGameComponent objects and is a DrawableGameComponent itself. In its Update method the scene calls the Update methods of all the DrawableGameComponent objects and the same for its Draw method. This means that now to create a scene we just create a class inheriting from the Scene class and insert its sprites and its logic in this class. In the previous post's example everything that was included in the main game loop, can be transferred to a scene named GameScene that inherits from Scene.

In the main game loop now we do not manage the sprites but we manage which scene will be enabled and visible.

In other words Game Services along with Scenes are two additional mechanisms that allow us to further harness the complexity of a game application.

The full application of the previous post with basic scene management can be downloaded here.

Shout it

kick it on DotNetKicks.com
blog comments powered by Disqus
hire me