XNA GameComponents and GameServices

The XNA Game class is a solid foundation to build your game on. While you're free to decide not to use the GameComponents and GameServices part of this class, using it provides you with a good starting point to organize small to moderate projects in. But let's first explore what this is all 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:

Illustration of the game components for a simple XNA application

GameServices

But wait! The TextRenderingComponent needs to access the GraphicsComponent. And the SceneGraphComponent needs access to the GraphicsComponent and the EffectManagerComponent. Was 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:

Illustration of game components accessing each other

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 headstart and promote clean seperation of responsibilities in your code.

Example Code

Need 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</summary>
/// <remarks>
///   <para>
///     A scene graph bascially 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 childs to the sun, while the moons would be childs to their
///     respective planets. Each moon would then only have to remember its position
///     relative to the planet, and whereever 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 ahold 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)
      );

/* Example
    // 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);
*/

  }

}
JuanK's picture

cool sample

Weell,

you know, this is the only sample that i found inthe web about game services implementation,

thanks for your help.

If you want and can, please make an extention of this article in next time.

see ya.

Anonymous's picture

thanks!!

thanks a lot man, great read. Only sample to add a service.

Jason's picture

Nice

This really came in handy. Not sure why msft doesn't have more tuts for this kind of stuff. Thanks for writing this up!

JSipko's picture

Thanks!

Indeed, thanks for the writeup on GameServices! It was pretty clear and concise, and perhaps the only one of it's kind on the internet. Microsoft should really have documentation like this in the XNA package, but luckily there is a good community out there!

Anonymous's picture

NEED HELP......

Hi.. ur tut wa grt..
i need some help regarding gameComponents...
i created an interface IScoreBoard for a GC ScoreBoard.
I added it to my game ...

but when i try to access its services in my other components i keep gettin the exception

NULLREFERENCE Exception!!

cud u help plzzz

Cygon's picture

Debug It

Cud u wrt prop. englsh plz?

My best guess is that you're querying for other GameComponents in your constructor. Only register your services in the constructors, do not query for other services before the Initialize() method was called.

My second best guess is that you simply forgot to actually add your service to the services collection. Might also be that you're adding the service under another Type than what you're querying for.

Anonymous's picture

That happened to me too,

That happened to me too, after hours of figuring out what went wrong, it turns out that the problem was that in the game.cs file I instantiated the component that needed the other component first, when infact you're supposed to instantiate the component that is needed by other components first.

hope this helps

Cygon's picture

You aren't supposed to...

No, you aren't supposed to instance the other component first. The game components are designed in a way that the order in which you create them does not matter.

In their constructor, game components should only add their own services, not query for services from other components.

After all components are added, the Game class calls Initialize() on all components. That's when they should query for other services.

All this is explained in my article, btw.

Hugo Riley's picture

Just what I needed

Thanks

Simon Foster's picture

You're a born teacher. You

You're a born teacher. You have the knack of getting inside a learner's head and explaining things clearly. I've bookmarked your page and hope that you continue to write these articles. XNA could really benefit from your explanations.

Cygon's picture

Norwegian? Read This!

If you can understand Norsk, here's another article that explains GameComponents in detail:

XNA Game Components Tutorial

Post new comment

The content of this field is kept private and will not be shown publicly.
  • Lines and paragraphs break automatically.
  • Allowed HTML tags: <br> <a> <em> <strong> <u> <i> <b> <cite> <blockcode> <code> <ul> <ol> <li> <dl> <dt> <dd> <p> <pre> <span>
  • You can highlight code with any of the following tags: <blockcode>

More information about formatting options

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.