Using Ninject with SunBurn

First, let me say sorry if I seemed to be absent for the past months. I was quite burned out and didn’t want to do much with computers during that time :).

My “XNA Game Architecture” series was left hanging, and right when it got interesting, too. I’ll try to find the time to continue where I left off. For now, here’s a small appetizer:

The letter N constructed from overlapping blue wave functions

Some weeks ago, Synapse Gaming offered their SunBurn Lighting and Rendering Engine for half the price. This was too good an offer to pass and so I now find myself being able to do much better lighting effects than I had ever hoped to achieve in my game.

The first thing I did was, of course, to adapt SunBurn to Ninject, a very tidy dependency injector that works on the XBox 360 and even on Windows Phone 7, into the SunBurn example application. This article gives a short overview about the overall structure and provides you with an example application if you want to give Ninject a try yourself.

The design of SunBurn was a pleasant surprise. It’s a set of modular components you can use in a game (not unlike the design principles my Nuclex Framework), without forcing you into a specific architecture. I admit that I was at first fearing to find a monolithic, tightly integrated engine that has you attach your game logic and stuff in a few predefined places – but luckily, this is not the case at all.

This made it very easy to wire it up to Ninject. If you don’t know what dependency injection is or what advantages it brings, check out my Quick Introduction to Dependency Injection or hunt for more articles on the web. The game class now effectively becomes this:

/// <summary>Sets up global services and runs the game loop</summary>
public class SunBurnTestGame : NinjectGame {

  /// <summary>Initializes a new game</summary>
  /// <param name="kernel">Kernel that is managing the game's services</param>
  public SunBurnTestGame(IKernel kernel) :
    base(kernel) {

    setWindowProperties();
    setupGraphicsDeviceManager();
  }

  /// <summary>Assigns the properties of the game's main window</summary>
  private void setWindowProperties() {
    Window.Title = "Ninject/SunBurn Test Game";
    Window.AllowUserResizing = false;
  }

  /// <summary>Creates and initializes the graphics device manager</summary>
  private void setupGraphicsDeviceManager() {
    this.graphics = new GraphicsDeviceManager(this) {
      MinimumVertexShaderProfile = ShaderProfile.VS_3_0,
      MinimumPixelShaderProfile = ShaderProfile.PS_3_0,
      PreferredDepthStencilFormat = DepthFormat.Depth24Stencil8
    };
  }

  /// <summary>Manages the graphics device</summary>
  private GraphicsDeviceManager graphics;

}

All the services you normally instantiate at the top of the game class are now automatically wired up by Ninject as needed. To tell Ninject that, for example, an IGraphicsDeviceManager is obtained from the Game class or that SunBurn’s ILightManager comes from the SceneInterface, I built a few modules that you can load into Ninject like so:

/// <summary>Contains the program's startup code</summary>
static class Program {

  /// <summary>The main entry point for the application</summary>
  [STAThread]
  static void Main(string[] args) {
    using (IKernel kernel = new StandardKernel()) {

      // Tell SunBurn how we're going to render
      configureSceneOptions(kernel);

      // Initialize the service bindings for the game        
      setupBindings(kernel);

      // Everything is ready, run the game loop!
      using (var game = kernel.GetService<IGame>()) {
        game.Run();
      }

    }
  }

  /// <summary>Configures the scene rendering options for SunBurn</summary>
  /// <param name="kernel">Kernel the scene options are set for</param>
  private static void configureSceneOptions(IKernel kernel) {
    var sceneOptions = new SceneOptions() {
      UseDeferredRendering = false,
      UseAvatars = false,
      UsePostProcessing = false
    };

    kernel.Bind<SceneOptions>().ToConstant(sceneOptions);
  }

  /// <summary>Binds all dependencies to their implementations</summary>
  /// <param name="kernel">
  ///   Ninject kernel to which the bindings will be added
  /// </param>
  private static void setupBindings(IKernel kernel) {
    kernel.Bind<IGame>().To<SunBurnTestGame>().InSingletonScope();
    kernel.Load<XnaModule>();
    kernel.Load<SunBurnModule>();
    kernel.Load<TestModule>();
  }

}

From then on, just add your actual game code as GameComponents (you could add it into the SunBurnTestGame class as well, of course, but I think this violates the SRP), request any external services as parameters from your component’s constructors and the rest will take care of itself.

By stating which services a component consumes in its constructor (instead of fishing for those services in the Game.Services collection), the code becomes shorter and it is much clearer what services a component relies on. This is by far the most important advantage, because, if you’re writing a unit test for that component, you’ll want to know exactly which services you need to mock.

Download

You can find all SunBurn/Ninject integration code in my Subversion repository at https://devel.nuclex.org/framework/svn/Nuclex.Ninject.Sunburn/trunk.

If you just want a quick peek, you can also browse the sources online:
Nuclex.Ninject.Xna
Nuclex.Ninject.Sunburn

2 thoughts to “Using Ninject with SunBurn”

  1. Hi guy,

    Thank you for this great post.
    I try to use Ninject inside a wp7 project and I cannot see where to put the composition root.
    For xbox/windows you can use the Main method of the Program class to setup Ninject but it isn’t available for wp7…
    Any ideas ?
    Regards

  2. Hi!

    I haven’t really done much with WP7, but I know about its omission of the Main() method. Your only choice is probably to set up and shut down the Ninject kernel in the Game class and not use constructor injection there.

    That design is probably not as bad as it sounds. I found myself keeping less and less code in my Game derived classes and doing everything in GameComponents instead – and this design turned out pretty neat: the Game class only has the responsibility to keep the game loop going while any game-driving code sits isolated in a GameStateManager‘s GameState, a GuiManager and so on.

Leave a Reply

Your email address will not be published. Required fields are marked *

Please copy the string ZoK9G9 to the field below:

This site uses Akismet to reduce spam. Learn how your comment data is processed.