Components and Services

The XNA Game class is a solid foundation to build your game on. To help you organize your game code into manageable modules, XNA also provides a small service framework that you can use to build reusable components that provide services to the rest of the game.

This framework comes in the form of the GameComponents collection in the game class and its GameServices registry. If you don’t know how all this works yet, don’t worry, that’s what this article is about!

Exploring GameComponents

Games typically consist of several logical modules that could ideally be reused between different games. For example, a lot of 3D games internally employ a scene graph to manage the visual entities in a game (models, decals and so on). This scene graph could be written in a general manner so that two very different games could use the same scene graph module.

This is exactly what the GameComponent class is about. Instead of spreading your graphics, scene graph and text rendering code all over the place, you can cleanly seperate these parts of your games into components that you can then give to other XNA developers or reuse in your next project. Here’s an example of which GameComponents you might decide to create for a simple application:

Image illustrating four game components registered to the game components collection

GameServices

But now the TextRenderingComponent needs to access the GraphicsComponent. And the SceneGraphComponent needs access to the GraphicsComponent and the EffectManagerComponent. Was all the separation in vain? Will all these components need to know of each other and develop a tightly woven net of interactions that makes it impossible to take one of them and put it in another project?

Not quite. That’s where the GameServices collection comes into play. Basically, a game service is just an interface under which you access a component. The built-in GraphicsComponent, for example, has the built-in IGraphicsDeviceService service which allows access to the GraphicsDevice that is managed by the component.

It works like this:

  • First, all GameComponent-derived classes that you added to your game are created and put into the GameComponents collection.
  • Each time a component is added to the collection, its OnGameChanging() method is called. Here the component will register its services to the GameServices collection.
  • After all components have been added, the second stage initialization calls the Start() method of all components. Here components can pick the services of other components they require.

In the end, your game will not rely on the one and only GraphicsComponent being there, but on some component that provides your game with the IGraphicsDeviceService. And that could be a different component from game to game.

Your code might even choose to just skip graphics altogether if there’s no IGraphicsDeviceService, but keep running neverthelesss (in a dedicated server, for example).

GameComponent Interaction

Components that need to access each other do so only through well-defined interfaces that could be provided by a different component in another game, or not at all. See this illustration to get a grasp of the concept:

Image illustrating how a component accesses another component through its service

Take note of the red line. This is how the SceneGraphComponent accesses the GraphicsDevice. It does not rely on the GraphicsDevice being provided by the GraphicsComponent class, it only expects that the IGraphicsDeviceService exists in your game.

When to Create GameComponents

Well, that’s really up to you. As a rule of thumb, any time you’re writing something you’re likely to be needing in your next game, too, create a GameComponent from it. Even if you need to modify it for your next game, it will give you a head start and promote clean separation of responsibilities in your code.

There are two ways you can create your own game components:

You can create a game component by deriving a new class from either the GameComponent or the DrawableGameComponent classes. These, however, require a reference to the Game instance in their constructor. Which means that you can’t use them unmodified if you ever plan to use WinForms together with XNA (for example, to create a level editor).

The other option is to just implement the IUpdateable and optionally the IDrawable interfaces in your class. You can then decide yourself if you want to depend on the Game class, only the GameServiceContainer to register your services or nothing at all!

Of course it's annoying to be implementing those two interfaces over and over again. That's why I wrote myself these two base classes which behave exactly like XNA's GameComponent and DrawableGameComponent classes, but don't require the Game instance to be constructed. See my blog post titled To GameComponent or not to GameComponent for more info and a download of those classes.

Example Code

Need some code? Here is the source code for an example component, a SceneGraphComponent that publishes a service named ISceneGraphService that can be queried for by the game class or by other components. If also shows how to use the IGraphicsDeviceService interface to access the game's GraphicsDevice.

/// <summary>Interface for the scene graph service</summary>
public interface ISceneGraphService {

  /// <summary>The root node of the scene tree</summary>
  SceneNode Root { get; }

  /// <summary>Matrix used to transform 3D to screen coordinates</summary>
  Matrix Projection { get; set; }

  /// <summary>Matrix that defines the viewer's location in the scene</summary>
  Matrix View { get; set; }

}

/// <summary>Component to manage visuals using a scene graph&l;/summary>
/// <remarks>
///   <para>
///     A scene graph basically organizes 'nodes' in a tree, where each node's position
///     is local to the parent nodes position. If you would model a solar system, the
///     planets would be children to the sun, while the moons would be children to their
///     respective planets. Each moon would then only have to remember its position
///     relative to the planet, and wherever the planet goes, the moon will be, too.
///     It is the same for the rotation of the parent node. The orientation of all
///     child nodes is relative to the orientation of the parent nodes.
///   </para>
///   <para>
///     Scene nodes are not required to actually draw anything, you can create them
///     just for the sake of categorization or in order to chain multiple independent
///     transformation matrices without concatenating the intermediate steps. If a
///     scene node wants to draw something, it can get hold of the GraphicsDevice
///     by navigating back to the game instance through its 'SceneGraph' property.
///   </para>
/// </remarks>
public class SceneGraphComponent :
  Microsoft.Xna.Framework.DrawableGameComponent, ISceneGraphService {

  /// <summary>Initializes the scene graph</summary>
  /// <param name="game">Game instance this scene graph belongs to</param>
  public SceneGraphComponent(Game game) : base(game) {
    game.Services.Add(typeof(ISceneGraphService), this);
  }

  /// <summary>Called when all game services have been registered</summary>
  public override void Initialize() {

    // Query for the graphics device service through which the graphics device
    // can be accessed
    IGraphicsDeviceService graphicsDeviceService =
      (IGraphicsDeviceService)Game.GameServices.GetService(
        typeof(IGraphicsDeviceService)
      );

#if false // Example of what you could do here
    // Register to the graphics device manager's events in order to get notified when
    // our ResourceManagement.Manual resources need to be destroyed or recreated.
    graphicsDeviceService.DeviceCreated += new EventHandler(graphicsDeviceCreated);
    graphicsDeviceService.DeviceDisposing += new EventHandler(graphicsDeviceDisposing);
    graphicsDeviceService.DeviceResetting += new EventHandler(graphicsDeviceResetting);
    graphicsDeviceService.DeviceReset += new EventHandler(graphicsDeviceReset);
#endif
  }

}

6 comments

  1. Steve says:

    Of course it’s annoying to be implementing those two interfaces over and over again. That’s why I wrote myself these two base classes which behave exactly like XNA’s GameComponent and DrawableGameComponent classes, but don’t require the Game instance to be constructed. See this article for more info.

    Clicking the link brings me back here?

  2. cygon says:

    Whoops, sorry! Looks like I forgot to actually fill in the link when I wrote that page. Should be fixed now :)

  3. Steve says:

    Thanks for updating that, an interesting read and of great help!
    Only just discovered your site and I have to say, not only am I impressed with the content but also the quick response to a mistake in the link! Great work!

  4. Marcin Seredynski says:

    Everything makes sense, but it raises another concern – how well does this approach play with Dependency Injection? Is there a way to make it known to the DI what dependencies does a component have, and what dependencies can it satisfy? Take Ninject for an example – the only dependency injection it supports is constructor parameter injection (which is great, as it enforces the dependencies to be explicit).

  5. Cygon says:

    I think the entire game services concept exists in parallel to a full-blown IoC container – if you use Ninject, you won’t use game services. And good riddance, too, since the way XNA envisioned its services is pretty much the Service Locator antipattern :)

    Game components (or, to be accurate, IGameComponent, IUpdateable and IDrawable) are still useful, even under Ninject, as adding them to the Game.Components collection lets them take part in the Update() and Draw cycles.

    For my own XNA projects, I’ve got NInject to completely take over the Game class (providing services such as IGame, IContentManager and ISpriteBatch) and my actual game code resides in GameComponents that use constructor injection to get their services. For backwards compatibility, I also bound the concrete Game, SpriteBatch and GameComponentCollection as services and my Open Source components both support constructor injection and adding themselves to the GameServiceContainer. Worked out pretty well in the end :)

    I created a small example project using those bindings for an article I haven’t yet carried over from my old website. If you want to check it out: Nuclex.XnaNinject.Demo.7z.

  6. Marcin Seredynski says:

    That is absolutely brilliant, thank you! Have you considered contacting Ninject’s authors and letting them know about your extensions? http://www.ninject.org/extensions

Leave a Reply

Your email address will not be published.

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>

Please copy the string nXb3aZ to the field below:

Social Widgets powered by AB-WebLog.com.