Search

Advertising

Home Articles Architecture Using Dependency Injection in XNA
Using Dependency Injection in XNA Print E-mail
Written by Markus Ewald   
Tuesday, July 28 2009 19:20

If you haven't heard about dependency injection yet, it's programming style where you write classes in way that they get any objects they access handed to them instead of going shopping in your object tree. This promotes decoupling, reusability and allows you to easily isolate classes for unit testing.

Combined with an Inversion of Control container, you actually have less work organizing a project and wiring your components up to each other than if you didn't use dependency injection.

To get started, assume I had written an FpsComponent for displaying my game's current frames per second with a constructor like this...

public FpsComponent(
  IGraphicsDeviceService graphicsDeviceService,
  ISharedContentService sharedContentService
) { /* ... */ }

...I could now ask an IoC container for an instance of the FpsComponent and it would determine that to construct an FpsComponent, it first needs to construct the component that provides the IGraphicsDeviceService, so it would look up or create a GraphicsDeviceManager and hand it over to my constructor.

What this means in practice is that you don't have to pass around global game services (like that GraphicsDeviceManager) any longer. If you need access to a service, add it to your constructor and you're done.

And because now all your dependencies are listed neatly in the constructor, it's easy to write unit tests that mock those services so your components can be tested in isolation. That's much more test-friendly than letting the FpsComponent grab the dependencies itself, eg. by handing it your Game class instance and letting it rummage around for whatever it needs ;-)

For a more in-depth introduction, check out my Quick Introduction to Dependency Injection!

Dependency Injection and XNA

Wiring up dependency injection in an XNA game is not so easy. The GraphicsDeviceManager, for example, wants an instance of the Game class. But the Game class has to construct the GraphicsDeviceManager in its constructor, so it can register its IGraphicsDeviceService before Initialize() is called and other components try to access it.

The built-in ContentManager and GameServiceContainer are created by the Game class, so you would have to tell your dependency injector to create a Game and query its Services property when a component is dependent upon the IServiceProvider.

What I've done is take Ninject, wire it up as good as I could and then find a workaround for the above issues. Which was to explicitely tell Ninject about the Game instance in the constructor, before the GraphicsDeviceManager is created (which can then happen cleanly through Ninject).

/// <summary>Binds the provided type to this instance</summary>
/// <param name="kernel">Kernel the binding will be registered to</param>
/// <param name="serviceType">Service to which this instance will be bound</param>
private void bindToThis(IKernel kernel, Type serviceType) {
  StandardBinding binding = new StandardBinding(kernel, serviceType);
  IBindingTargetSyntax binder = new StandardBinder(binding);

  binder.ToConstant(this);
  kernel.AddBinding(binding);
}

Next, to get the Game's IServiceProvider and ContentManager to show up in Ninject, I created two adapters that depend on the Game class so it looks to Ninject as if the two adapters were components requiring the Game class, whereas actually, the adapters delegate their work to the Game class!

private class ServiceProviderAdapter : IServiceProvider {

  /// <summary>Initializes a new service provider adapter for the game</summary>
  /// <param name="game">Game the service provider will be taken from</param>
  public ServiceProviderAdapter(Game game) {
    this.gameServices = game.Services;
  }

  /// <summary>Retrieves a service from the game service container</summary>
  /// <param name="serviceType">Type of the service that will be retrieved</param>
  /// <returns>The service that has been requested</returns>
  public object GetService(Type serviceType) {
    return this.gameServices;
  }

  /// <summary>Game services container of the Game instance</summary>
  private GameServiceContainer gameServices;

}

The good thing is that these tweaks are only required because the Game class didn't anticipate dependency injection. Once in place, Ninject will work in an XNA game without further complications. So I can just request any GameComponent from Ninject, and it will happily construct it, without the component even knowing that it's taking part in an elaborate dependency injection scheme =)

Even better is that Ninject works flawlessly on the XBox 360. It's one of the few dependency injection frameworks that can cope with the Compact Framework and selecting the right compilation flags for the XBox 360's XNA Runtime was a breeze.

Example Project

I've put together a small example game that makes use of Ninject to construct a game component (called FpsComponent): Ninject XNA Demo Project

This is what you'll find in the example project:

  • Program.cs - Wires up the dependencies and creates the Game instance using Ninject.
  • MyGame.cs - Game class which creates the GraphicsDeviceManager through dependency injection as well as the FpsComponent.
  • FpsComponent - Example component that displays the current frame rate. Constructed using dependency injection.
  • Scaffolding/GameModule.cs - Configures the dependencies (eg. which class provides which services) for Ninject.
  • Scaffolding/NinjectGame.cs - Custom Game class from which you need to derive instead to make Ninject work with XNA.
  • Scaffolding/ISharedContentService - An example service that can be pulled in as a dependency by components that want to access the game's global ContentManager.

Ninject builds for PC and XBox 360 are included in the download, as well as an XBox 360 solution you can use to test things out on your console!

 


Joomla Template by Joomlashack