This project is read-only.

Change Set 689

Sep 7, 2010 at 3:04 AM

Ciao,
ho cominciato a lavorare ed ecco quello che ne è venuto fuori...

Credo di dover principalmente lavorare molto sul funzionamento di Game/Services/Components, per capire al meglio come interagiscono fra loro.
Come da task ho cercato di creare uno StateManager statico che si occupi della logica: non so se l'implementazione sia corretta, perchè ci sono alcune cose che non mi convincono: non sapendo dove richiamare le funzioni in esso contenute, le ho fatte partire dall'Update del Game principale. In questo modo però vengono chiamate ad ogni iterazione del GameLoop, e mi sono trovato ad inventare un modo per non creare di continuo components dello stesso tipo (assolutamente inutili): ho provato a effettuare un controllo sul numero di components presenti, e mi è sembrato efficace: se ci sono già components nella lista sarebbe inutile crearne ulteriori, in quanto lo StateManager, ogni volta che cambia lo stato del gioco, effettua una pulizia di tutti i Components presenti. E' un buon modo?
Come potete notare, ho anche iniziato a destreggiarmi con i components: per ora ne ho due, uno per il menù e uno per la schermata di gioco. So benissimo che ce ne saranno bisogno di molti di più (per esempio, un component MenuItem... sarebbe comodo) ma non volevo che passasse altro tempo per aggiornare i miei progressi. Sicuramente li riprenderò in mano successivamente.
Come potete vedere, ho optato per rendere tutti e due questi component Drawable, perchè mi sembravano entità necessarie di Rendering.
Non ho ancora un Component che si prenda la briga di gestire gli Input: e se devo essere sincero, vorrei chiedervi maggiori consigli al riguardo, in quanto non credo di aver chiara la sua finalità... Non sono sufficienti le istruzioni già disponibili?

Per quanto riguarda i Services, li ho interpretati come un ottimo metodo per poter condividere tra i vari Components variabili utili: non so se ne abbia abusato o se sia un giusto utilizzo, in fondo è un mio tentativo: fatemi sapere.

Scusatemi il tema e la lentezza nei progressi: voglio essere sicuro di aver le idee chiare su questi elementi base prima di continuare.

Stefano

Sep 7, 2010 at 11:58 AM

Hola,

Ho scaricato e guardato il codice. Su alcune cose ci siamo, su altre si può migliorare.

  1. L'istanziazione del primo GameComponent (il menù, ad esempio) non la fai nel GameLoop (non lo si fa praticamente mai, se non rare eccezzioni) ma bensì nell'Initialize o nel LoadContent del component Game.
  2. L'InputManager serve per storare l'input dell'utente in un unico posto. Deve inoltre esporre degli eventi a cui associare i rispettivi gestori che vengono definiti all'interno dei GameComponent competenti. Ad esempio un possibile evento che si può verificare è l'OnTap. Ora tu devi decidere quale EventHandler associare, e sarà diverso in base allo stato del gioco. Se sei nel menù, associerai un metodo on_tap che prenderà la posizione di tap, controllerà se è stato premuto qualche bottone e agirà di conseguenza.
  3. Non abusare dei GameComponent: sono comodi ma pesanti. Non è la scelta ideale per creare dei meri bottoni ;).
  4. I services sono, come hai capito, un comodo modo per passare risorse tra diversi components. Non mi è piaciuta molto la classe che wrappa in un contenitore alcune risorse che ti servono. Il modo giusto per usarli è il seguenti: ti definisci un'interfaccia che indichi cosa espone quel servizio. Implementi quell'interfaccia in una classe X. Aggiungi il servizio che ti interessa che sarà del tipo (typeof(InterfacciaDefinita)) e con quel tipo la recuperi quando ti serve. Ad esempio, ti definisci un'interfaccia IGraphicsDeviceManager che ha come corpo una proprietà (che per convenzione si scrivono con la prima lettere maiuscola ;)) che restituisce un GraphicsDeviceManager. Fai implementare questa interfaccia al Game e aggiungi il game stesso come servizio di tipo IGraphicsDeviceManager. Ora hai il GraphicsDeviceManager reperibile da per tutto, in maniera flessibile ed efficiente.
Sep 8, 2010 at 6:37 PM

Ciao,

comincia ad essere più chiara l'idea dell'InputManager: sarà sicuramente il mio prossimo obiettivo!

Invece ho ancora qualche dubbio sul punto 4 da te elencato.
Hai detto che non è molto buona la classe studiata appositamente per condividere dati, e in effetti ho notato, nei vostri esempi, che fate un grande uso delle interfacce.
Mi dici di creare un'interfaccia per poter recuperare dai Services un'ipotetica istanza di una classe X che ho definito: ma anche senza interfacce, il mio oggetto è tranquillamente disponibile con typeof(X)... e dalla tua frase non vedo altra utilità a quest'interfaccia se non per tipare una classe e ritrovarla nella collezione.

E ancora: perchè inserire nei services il game stesso? I components possono accedere all'oggetto game in qualsiasi momento... O mi sbaglio?

Sep 8, 2010 at 7:16 PM

Non riesco a trovare la mia tesi, altrimenti ti avrei passato un estratto di quella in cui spiego come funzionano. Come te la cavi con l'inglese? Se la risposta è lo mastico ti passo questo link che spiega per bene cosa sono e come funzionano:

http://www.nuclex.org/articles/4-architecture/6-game-components-and-game-services

 

Per quanto riguarda il caso specifico, ci tengo a sottolineare che non è sbagliato, è solo una cosa che non mi è piaciuta ;). Si tratta unicamente di buona programmazione ad oggetti. Se ti definisci la tua classe wrapper per racchiudere alcune risorse che vuoi condividere, sia la stessa classe che quelle che lo usano perdono moltissimo in riusabilità (ed eleganza :D). Se invece ti definisci un'interfaccia IGraphicsDeviceManager che sai contenere una proprietà che ritorni tale oggetto, non ti interessa sapere chi la mette a disposizione, dove, perchè. Sai semplicemente che c'è, la recuperi e la usi. Il tuo obbiettivo dovrebbe essere quello di limitare l'accoppiamento tra le classi il più possibile, non andando a creare dipendenze inutili. Altrimenti finito di lavorare su un progetto butti via tutto invece di riusare quello che hai scritto in maniera il più generica possibile.

Quindi l'utilità non sta nel tipare una classe (che di per se era già tipata anche come facevi tu ;)) ma bensì limitare le dipendenze tra i vari component. Se ciò non avviene, per poter utilizzare risorse tra component diversi devi castarli al loro tipo (per utilizzare magari risorse o metodi pubblici). Per concludere, è vero che tutti i component possono accedere al Game, ma vai a vedere di che tipo è Game. Scoprirai che è di tipo Game e che non ha tutte le cose pubbliche che hai aggiunto alla tua versione (che di fatto lo va ad estendere). Quindi non recuperi QUEL Game.

Vado un po' di fretta ora, spero di essere stato abbastanza chiaro, in caso contrario ovviamente scrivi pure :D.

Sep 9, 2010 at 10:01 PM
Edited Sep 9, 2010 at 10:07 PM

Assolutamente!
Sei stato molto chiaro, e l'esempio con il Game è stato lampante... Proverò a modificare il mio codice nel modo opportuno! E grazie per il link, è stato molto chiaro, non preoccuparti per le fonti in inglese, sono assolutamente le migliori. Se trovi la tua tesi, in ogni caso, sarà interessante darci un'occhiata.

Per quanto riguarda gli input: l'InputManager è evidentemente un GameComponent non disegnabile... che lo StateManager aggiunge ogni qual volta che il gioco necessita di interazione con l'utente (se dovessi fare una sequenza filmata che l'utente non può saltare non lo caricherei nemmeno, giusto?). A questo punto però, è lo stesso StateManager che deve prendersi la briga di aggiungere i riferimenti agli handlers, scegliendo quelli opportuni dai metodi degli altri components. Credo di essere sulla strada giusta!

Entro domani posto sicuramente nuovo codice!

Sep 9, 2010 at 10:12 PM

Non necessariamente. Una volta registrato l'InputManager come servizio (di tipo IInputManager) puoi reperirlo negli altri GameComponent e aggiungere il gestore dell'evento direttamente da li (tutto a vantaggio del disaccoppiamento).