Developing GameComponents for XNA

The XNA framework is a solid foundation for writing a modular and cleanly structured game engine. In this post I'll go through the GameComponent class and how it can be used to provide services to the rest of the engine.

Even though XNA is a simple wrapper around DirectX 9.0, it provides several helper classes and some infrastructure that could / should help developers in building a modular game engine without much effort. One of the most important building blocks XNA provides is the GameComponent class.

Most of this post is inspired by this article on Nuclex.org.

Modular engine design via GameComponent

XNA provides two Component classes. Both of them can be registered on the Game instance and will then be automatically called by the main loop.

  • GameComponent: provides an "Initialize" and an "Update" method you can override. The "Update" method will be called by the Game class once at each frame.
  • DrawableGameComponent: subclass of GameComponent, provides an overridable "Draw" method that will be called at each frame, after the Game's "Draw" method has returned.

As you can see, GameComponents feature a similar structure to the Game class: you can simply define your own subclass and override the methods you need. You can register a GameComponent on your Game instance with this short code:

class MyGame : Microsoft.Xna.Framework.Game { public MyGame() { Components.Add(new MyGameComponent(this)); } }

Ideally you'll develop all subsystems of your engine as a separate GameComponent class. This forces you to "think in modules" and to limit the interaction between classes. Some examples of modules you may need are:

  • Renderer: a complex module that keeps a queue of render jobs (for instance, a model and a material). When Draw() is called the queue is flushed and all the models are rendered to screen. You can optionally sort all render jobs before rendering them, minimizing switches in effects and materials (improves performance) or rendering solid objects before transparent ones.
  • Scene organization: a module that keeps a reference to all your entities and of their position/rotation in space. If you define a nice interface for this module, you can simply replace it in the future when you'll need complex scene management (BSP, Octrees or such).
  • Audio module: loads audio cues, handles music and effects, while providing a simple (and abstract) interface for all other modules.
  • Special effects: for instance bloom, motion blur and so on. Writing these effects as modules also allows you to enable or disable them at runtime with ease.

How Components communicate: Services

Writing modular components is ok, but how do these separate modules communicate and interact? Ideally they shouldn't know about each other (or at least not about their implementation): otherwise we'll have a tight net of references and interactions between components, which substantially defies the purpose of the GameComponent class.

The solution is easy: XNA provides a nice way to register the "services" your components provide on the Game and then referencing them through their interfaces. This also allows you to replace a component very easily.

Unfortunately XNA doesn't provide sample interfaces for common services (it would have simplified sharing of components on the Internet), therefore you'll have to define your own. For instance, a Component implementing the Bloom post-processing effect could be written this way:

class BloomComponent : DrawableGameComponent, IBloomService { public BloomComponent(Game game) : base(game) { //Register game.Services.AddService(typeof(IBloomService), this); } }

Of course each service will provide its own properties and methods through its interface. If you need to call a service's method, you'll simply have to load its interface from any other GameComponent class (or from the main Game class). For instance:

IBloomService bloom = (IBloomService)Services.GetService(typeof(IBloomService));

This way you'll always have a clean separation between interface (the registered services) and implementation (the actual GameComponents registered on your Game instance). Additionally, you won't need to manually update your components: XNA handles everything automatically by calling the overridden Update() and Draw() methods at each frame.

Tomorrow I'll post the complete example of my BloomComponent class. Stay tuned!  :)