Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial
  • Lezione 14 di 53
  • livello principiante
Indice lezioni

Creare componenti: GameComponent e DrawableGameComponent

Razionalizzare il codice lavorando con componenti e servizi
Razionalizzare il codice lavorando con componenti e servizi
Link copiato negli appunti

Da un punto di vista di Ingegneria del Software, è ben noto che una ragionevole modularizzazione delle nostre applicazioni è una delle chiavi di una base di codice chiara e di facile manutenzione. Nel caso di XNA peró, sembra quasi che siamo costretti ad usare esclusivamente la classe Game in cui dobbiamo infilare l'intero codice del nostro gioco.

Ciò non è del tutto vero, fortunatamentein XNA abbiamo un sistema di componenti pensato per separare le funzionalità che richiedono caricamento (Initialize/LoadContent), aggiornamento (Update) e disegno (Draw).

In questo modo possiamo pensare di avere dei componenti diversi, interessati solo ad alcune funzionalità di XNA:

Componente Funzionalità
Menu (caricamento, aggiornamento, disegno)
AI/fisica/logica (caricamento, aggiornamento)
rendering della scena (caricamento, aggiornamento, disegno)
rendering della UI (caricamento, aggiornamento, disegno)

Possiamo creare un componente in due modi: se ci interessano solo i metodi Initialize e Update, ereditiamo la classe GameComponent, mentre se ci interessano anche i metodi LoadContent e Draw ereditiamo la classe DrawableGameComponent.

Dobbiamo poi scrivere i metodi per le rispettive classi e, nel costruttore del nostro gioco, attiviamo questi componenti: i loro metodi saranno così invocati assieme a quelli del Game aggiungendoli alla collezione Components, tramite il suo metodo Add.

Un esempio di semplice GameComponent che si limita a stampare sulla console diagnostica le invocazioni dei suoi metodi è questo:

public class DiagnosticsComponent : Microsoft.Xna.Framework.GameComponent
{
  public DiagnosticsComponent(Game game) : base(game) { }
  public override void Initialize()
  {
    System.Diagnostics.Debug.WriteLine("Initialize Component");
    base.Initialize();
  }
  public override void Update(GameTime gameTime)
  {
    System.Diagnostics.Debug.WriteLine("Update Component");
    base.Update(gameTime);
  }
}

Aggiungiamo questo componente nel costruttore del nostro gioco :

Components.Add(new DiagnosticsComponent(this));

e l'output risultante dall'esecuzione dell'applicazione diventa:

Initialize
Initialize Component
LoadContent
Update
Update Component
Update
Update Component
Draw
Update
Update Component
Draw
...
Update
Update Component
Draw
Update
Update Component
Draw
Update
Update Component
Servizi

I componenti, per come li abbiamo visti finora, non offrono una grande funzionalità: un componente non è in grado di comunicare in modo strutturato e ordinato con il resto dell'applicazione, e questo ci costringe a:

  • creare componenti che svolgono il loro lavoro in modo del tutto indipendente dal resto del gioco, quindi con funzionalità piuttosto limitata;
  • creare componenti che comunicano in modo completamente ad-hoc con il resto del gioco e tra di loro, con perdita di flessibilità.

I servizi in XNA

Il sistema di servizi che XNA ci mette a disposizione serve proprio per risolvere questo problema in modo brillante, ossia avere componenti in grado di comunicare con il resto del sistema senza perdita di flessibilità e con uno schema standard e pulito.

Un componente eredita una serie di interfacce dotate ciascuna dei metodi, degli eventi e delle proprietà pertinenti ad una certa funzionalità (detta servizio) che il componente offre. Il componente registra che è lui ad offrire quel servizio aggiungendo alla proprietà Services del Game il binding (collegamento) tra lui e il servizio tramite il metodo AddService.

Rispetto al nostro esempio corrente, vediamo com aggiungere un servizio al nostro sistema. Creiamo una interfaccia che rappresenta il servizio di diagnostica:

public interface IDiagnostics
{
  bool Logging { get; set; }
}

implementiamo questa interfaccia nella classe del nostro componente:

public class DiagnosticsComponent : Microsoft.Xna.Framework.GameComponent, IDiagnostics
{
  public bool Logging { get; set; }

nel costruttore del componente registriamo il servizio:

public DiagnosticsComponent(Game game) : base(game)
  {
    game.Services.AddService(typeof(IDiagnostics), this);
  }

e trasformiamo le chiamate di diagnostica in:

System.Diagnostics.Debug.WriteLineIf(Logging, [messaggio]);

Nel metodo Dispose del componente rimuoviamo il binding tra il componente e il servizio, per evitare che rimanga spazzatura non ripulita:

protected override void Dispose(bool disposing)
{
  Game.Services.RemoveService(typeof(IDiagnostics));
  base.Dispose(disposing);
}

Quando vogliamo ottenere il servizio di diagnostica dall'interno del nostro game, non dovremo fare altro che:

var diagnostics_service = Services.GetService(typeof(IDiagnostics)) as IDiagnostics;

dove il cast finale è necessario perché il metodo GetService ritorna un Object.

Ti consigliamo anche