XNA – Les composants et les services – Partie 2

Partie 2 : Les services

Dans la partie précédente, nous avons vu le coté réutilisable des composants. Une fois le composant développé, il suffit juste de le rajouter à la collection de composants de la classe principale de jeu (Game1 par défaut).

Maintenant nous allons voir comment partager des informations entre les composants. Cela est possible grâce aux Services. Mais “C’est quoi ces Services ?” me demanderez vous. C’est un Dictionary<Type, Object>, qui est mis à disposition par la classe Game via la propriété Services (de type GameServiceContainer).

Avant de rentrer dans un cas de nouveau service, je vais déjà vous montrer comment partager un élément déjà existant. Vous vous souvenez, dans la partie 1 je vous ai parlé de la création d’un nouveau SpriteBatch, qui pouvait être améliorer par les services. Et bien, pour cela, nous allons juste partager le SpriteBatch de la classe Game1 en tant que Service.

Partage d’un SpriteBatch en tant que Service

Reprenons la solution de la partie 1, et ajoutons notre SpriteBatch au dictionnaire de services :

protected override void Initialize()
{
    // Créer un SpriteBatch, qui peut être utilisé
    // pour dessiner des textures.
    spriteBatch = new SpriteBatch(GraphicsDevice);

    // Ajout du composant au jeu 
    this.Components.Add(new DateViewer(this));

    // Ajout du SpriteBatch au dictionnaire de Services 
    this.Services.AddService(typeof(SpriteBatch), this.spriteBatch);

    base.Initialize();
}

Maintenant qu’il est ajouté, changeons la méthode Draw de la classe Game1 :

protected override void Draw(GameTime gameTime)
{
    GraphicsDevice.Clear(Color.CornflowerBlue);

    // Initialise le Sprite Batch 
    this.spriteBatch.Begin();

    base.Draw(gameTime);

    // Restaure le périphérique graphique à son état de départ 
    this.spriteBatch.End();
}

C’est donc maintenant la classe Game1 qui ouvre et ferme le spriteBatch. Entre l’appelle de Begin et de End, la méthode Draw, de chaque composant de la collection, sera appelée.

Du coup, maintenant, il y a quelques petites choses à changer dans notre composant DateViewer.

Tout d’abord, il ne faut plus créer un nouveau SpriteBatch, mais récupérer celui disponible dans le dictionnaire de Service. Il faut donc remplacer la ligne :

this.spriteBatch = new SpriteBatch(this.Game.GraphicsDevice);

par celle ci :

this.spriteBatch = (SpriteBatch)this.Game.Services.GetService(typeof(SpriteBatch));

Cette ligne nous permet de récupérer le SpriteBatch. Pour simplifié la récupération de service, j’ai crée une méthode d’extension pour utiliser les génériques :

public static class GameServiceContainerExtension {
    /// <summary> 
    /// Récupère le Services de type T 
    /// </summary> 
    /// <typeparam name="T">Type du service</typeparam> 
    /// <param name="services"></param> 
    /// <returns></returns> 
    public static T GetService<T>(this GameServiceContainer services)
    {
        // Renvoi le service du type demandé 
        return (T)services.GetService(typeof(T));
    }
}

Nous pouvons donc maintenant écrire :

this.spriteBatch = this.Game.Services.GetService<SpriteBatch>();

Il nous reste maintenant à épurer la méthode Draw du composant vu qu’il n’a plus besoin d’ouvrir, fermer le SpriteBatch :

public override void Draw(GameTime gameTime)
{
    // Affiche notre date à l'écran 
    this.spriteBatch.DrawString(this.spriteFont,
                                date.ToString("dd/MM/yyyy HH:mm:ss"),
                                new Vector2(0, 0),
                                Color.White);
}

Un autre exemple de service

Prenons un autre exemple, de service. Par exemple, il est très probable, que plusieurs composants de mon jeu veuillent lire l’état du clavier. Appeler la méthode Keyboard.GetState() est possible dans chaque composant, mais pas utile vu qu’on demanderai plusieurs fois la même chose (je ne parle même pas des pertes de performance…). Faisons donc un service qui diffuse les informations du clavier.

Déclarons le “contrat” du service :

public interface IKeyboardService {
    // Etat du Clavier 
    KeyboardState State { get; }
}

Nous allons maintenant implémenter ce “contrat” avec un composant. Ce composant s’inscrira automatiquement en tant que service :

public class KeyboardService : GameComponent, IKeyboardService {
    private KeyboardState _state;

    /// <summary> 
    /// Etat du clavier 
   /// </summary> 
   public KeyboardState State
    {
        get { return this._state; }
    }

    public KeyboardService(Game game) : base(game)
    {
        // Inscription automatique du composant en tant que service 
        this.Game.Services.AddService(typeof(IKeyboardService), this);
    }

    public override void Update(GameTime gameTime)
    {
        // Récupération de l'état du clavier 
        _state = Keyboard.GetState();
    }
}

Nous pouvons maintenant ajouter notre composant à la collection :

// Ajout du composant KeyboardService au jeu 
this.Components.Add(new KeyboardService(this));

Le service peut maintenant être récupérer par n’importe quel composant de la manière suivante :

this.Game.Services.GetService<IKeyboardService>();

Admettons que nous voulions que le rafraichissement du temps de notre DateViewer ne se fasse qu’à l’appui de la touche espace.

Il nous faudrait d’abord récupérer le service, en ajoutant d’abord une propriété à notre composant DateViewer :

// Référence au service de gestion du clavier 
private IKeyboardService keyboardService;

Ensuite récupérer le service dans la méthode Initialize :

// Récupération du KeyboardService this.keyboardService = this.Game.Services.GetService<IKeyboardService>();

Enfin, changeons la méthode update pour qu’elle ne mette à jour la date uniquement à la pression de la barre espace  :

public override void Update(GameTime gameTime)
{
    // Mise à jour de la date seulement 
    // Si la touche espace est pressée 
    if(this.keyboardService.State.IsKeyDown(Keys.Space))
            this.date = DateTime.Now;

    base.Update(gameTime);
}

Un petit F5, et maintenant la date ne s’actualise qu’après une pression sur la barre espace.

Téléchargez les sources de la partie 2.

One comment on “XNA – Les composants et les services – Partie 2

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Vous pouvez utiliser ces balises et attributs HTML : <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>