Click or drag to resize
DigitalRuneStep 3: Input service
Add the input service

Let's add the DigitalRune input service to the game and use it to exit the game when ESC is pressed.

Game1.cs
using DigitalRune.Game.Input;                                   // NEWnamespace MyGame
{
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        …
        public InputManager _inputManager;                      // NEW
        …

        protected override void Initialize()
        {
            _inputManager = new InputManager(false);            // NEW

            base.Initialize();
        }

        protected override void Update(GameTime gameTime)
        {
            _inputManager.Update(gameTime.ElapsedGameTime);     // NEW

            if (_inputManager.IsDown(Keys.Escape))              // NEW
                Exit();                                         // NEW

            base.Update(gameTime);
        }
        …

Here you can see a common pattern: A service/manager is created in Game.Initialize. It is updated in Game.Update. Most services/managers in the DigitalRune Engine are used like this.

If you run the game, you should be able to exit by pressing ESC on the keyboard.

Add a game component

Personally, I prefer to keep my Game class clean and move have any game-specific update logic and input handling (like handling the ESC key) in a separate class.

Let's add an item MyGameComponent.cs to the project and move the input handling into this class:

MyGameComponent.cs
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;

namespace MyGame
{
    public class MyGameComponent : Microsoft.Xna.Framework.GameComponent
    {
        public MyGameComponent(Game game)
            : base(game)
        {
        }

        public override void Update(GameTime gameTime)
        {
            if (((Game1)Game)._inputManager.IsDown(Keys.Escape))
                Game.Exit();

            base.Update(gameTime);
        }
    }
}

In Game1.cs we create an instance of this game component. The input handling is removed.

Game1.cs
public class Game1 : Microsoft.Xna.Framework.Game
{
    …

    protected override void Initialize()
    {
        _inputManager = new InputManager(false);

        Components.Add(new MyGameComponent(this));          // NEW

        base.Initialize();
    }

    protected override void Update(GameTime gameTime)
    {
        _inputManager.Update(gameTime.ElapsedGameTime);
        
        //// Allows the game to exit                        // REMOVE
        //if (_inputManager.IsDown(Keys.Escape))            // REMOVE
        //  Exit();                                         // REMOVE
        
        base.Update(gameTime);
    }
    …

The Game class is now cleaner. However, the game component uses following code to access the input manager:

C#
if (((Game1)Game)._inputManager.IsDown(Keys.Escape))

That means, every class in the game that wants to use the input service must have a reference to the Game instance. This is fine for small project. However, in more complex projects we want to decouple classes. This makes the whole code more reusable and maintainable. A design pattern that can be used for this is the service provider pattern.

Add a service container

Let's use the service provider pattern in our game using the DigitalRune.ServiceLocationServiceContainer class.

Add following in Game1.cs:

Game1.cs
using DigitalRune.ServiceLocation;                                                  // NEW
using Microsoft.Practices.ServiceLocation;                                          // NEWnamespace MyGame
{
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        …
        private ServiceContainer _services;                                         // NEW
        private InputManager _inputManager;                                         // NEW: private instead of public
        …

        protected override void Initialize()
        {
            _services = new ServiceContainer();                                     // NEW
            ServiceLocator.SetLocatorProvider(() => _services);                     // NEW

            _services.Register(typeof(Microsoft.Xna.Framework.Game), null, this);   // NEW
            _services.Register(typeof(Game1), null, this);                          // NEW

            _inputManager = new InputManager(false);
            _services.Register(typeof(IInputService), null, _inputManager);         // NEW

            Components.Add(new MyGameComponent(this));

            base.Initialize();
        }
        …
    }
}

This code creates a ServiceContainer instance and uses the ServiceLocator to make it globally available. The input service is registered in the service container. The field Game1._inputManager doesn't have to be public any more. We also register the Game instance itself in the service container.

From now on MyGameComponent and any other code in your game can access the input service like this:

MyGameComponent.cs
using DigitalRune.Game.Input;                                                     // NEW
using Microsoft.Practices.ServiceLocation;                                        // NEW
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;

namespace MyGame
{
    public class MyGameComponent : Microsoft.Xna.Framework.GameComponent
    {
        private IInputService _inputService;                                      // NEW

        public MyGameComponent(Game game)
            : base(game)
        {
            _inputService = ServiceLocator.Current.GetInstance<IInputService>();  // NEW
        }

        public override void Update(GameTime gameTime)
        {
            //if (((Game1)Game)._inputManager.IsDown(Keys.Escape))                // REMOVE
            if (_inputService.IsDown(Keys.Escape))                                // NEW
                Game.Exit();

            base.Update(gameTime);
        }
    }
}

Another pattern that is used througout the DigitalRune libraries: The manager class, e.g. InputManager, implements an interface, e.g. IInputService. The Game class creates and uses the manager instances. Only the interfaces are registered in the service container. The rest of the game code uses only the interface IInputService. This interface contains everything that is needed by other game components.

The service provider pattern is very helpful – however, it is completely optional. You do not need to use it. All DigitalRune libraries will work fine without a service provider.