2010-06-08 8 views
16

Quali sono i motivi pro e contro l'oggetto di un disegno di gioco e l'aggiornamento stesso? Ad esempio, se si dispone di un gioco in cui il giocatore ha una posizione sullo schermo, perché non hanno una classe onnicomprensiva:Cosa c'è che non va nell'architettura di un oggetto di gioco che si disegna e si aggiorna da solo?

public class Player { 
    private int x, y, xVelocity, yVelocity; 
    private Sprite s; 
    //... 

    public Player() { 
     // load the sprite here, somehow? 
    } 

    public void draw(CustomGraphicsClass g) { 
     g.draw(s, x, y); 
    } 

    public void update(long timeElapsed) { 
     x += (xVelocity * timeElapsed); 
     y += (yVelocity * timeElapsed); 
    } 
} 

Cosa c'è di sbagliato con questo disegno? Quali sono le cadute o le preoccupazioni? Come faresti meglio a scrivere qualcosa del genere, o meglio ad architettare questo tipo di cose in un gioco?

Inoltre, un po 'collegato, come implementereste il caricamento dell'immagine Sprite?

E inoltre, come implementeresti la collisione tra due Player s?

(forse dovrei separare questi extra di due domande in nuove domande, eh?)

risposta

18

Unisce tutto il codice di rendering e logico quando hanno poco in comune oltre a essere concettualmente legati alla stessa "entità". Man mano che la tua classe diventa più grande, puoi trovarti con un enorme monolitico Player che è un incubo da mantenere. Divisione lungo i confini del dominio (rendering, AI) lo rende più gestibile senza dover rinunciare molto dato che quei domini non si sovrappongono molto.

Al di là di manutenibilità, ci sono alcune altre considerazioni:

  1. Se si desidera elaborare il rendering e intelligenza artificiale su diversi thread, mescolando il loro stato nella stessa classe è solo chiedere l'elemosina per i problemi di multi-threading brutto.

  2. Se si utilizza un linguaggio come C++, classi altamente accoppiate come questa possono uccidere i tempi di compilazione.

  3. A seconda del modo in cui il codice e gli oggetti sono disposti in memoria, suddividere gli oggetti in componenti separati per ciascun dominio può offrire una migliore coerenza della cache e prestazioni migliori.

Ecco il numero lots more info se siete curiosi.

+1

Mi piace la pagina a cui ti sei collegato con "molte più informazioni" !! Ci sto ancora pensando, ma è sicuramente ** utile non solo per dirti cosa fare, ma dato un esempio decente e una discussione sulle insidie ​​e altre considerazioni. – Ricket

+0

Grazie! Questo è un libro su cui sto lavorando, ma ora è più o meno in pausa. Fammi sapere se qualcosa non è chiaro nel capitolo. – munificent

+1

+1 Solo per il punto 3, che è il motivo principale, specialmente sulle console. L'incapsulamento può portare a prestazioni scadenti della cache esp quando si memorizzano flag come IsVisible per oggetto, quindi si carica l'intera riga della cache per quell'oggetto solo per controllare se deve disegnarlo ecc. – zebrabox

2

E 'ok, ma se si hanno molti oggetti diversi, tutti con sprites, posizioni e velociites che hanno bisogno di aggiornamento, quindi non ci sarà un sacco di repliche. Potresti spingere la posizione, la velocità in una classe base, ma questo hard-wire il tipo di movimento. Meglio è separare il movimento dall'oggetto stesso.

Ad esempio, si consideri una palla che rimbalza su e giù. Non è facile da modellare solo con la velocità. Invece, crea una classe Motion, con ConstantMotiion (per velocity) e BounceMotion per il bouncing. La classe di movimento si occupa quindi di aggiornare lo stato della posizione dell'oggetto da una cornice all'altra.

+0

Questo è interessante. C'è un nome per questo modello che hai descritto o un link dove potrei ottenere maggiori informazioni? –

+0

È la separazione delle preoccupazioni. Lo schema più vicino è probabilmente il modello di strategia. – mdma

3

Un possibile inconveniente potrebbe essere una violazione della separazione delle preoccupazioni. Penserei che, idealmente, gli oggetti non dovrebbero sapere come sono renderizzati, in modo che il motore di rendering possa essere ottimizzato separatamente dagli oggetti del gioco ... anche se, in pratica, potrebbe essere che queste cose siano spesso troppo interdipendenti per essere separati in questo modo.

+1

+1 Questo è chiamato AOP - Aspect Oriented Programming. –

3

Supponiamo che ogni volta che l'oggetto (cioè Player) viene aggiornato dovrebbe

1) ridisegnare se stesso

2) informare le altre unità che è aggiornato

3) "update-azione" alla storia/log/qualsiasi altra cosa (forse vorrai avere la possibilità di riprodurre l'intero gioco dopo che è finito, come un film).

....

n) ogni altra interazione tra il Lettore-oggetto e il suo ambiente.

In tutti questi casi si dovrà modificare il metodo di aggiornamento, come:

public void update(long timeElapsed) { 
    dosmth(); 

    redraw(); 
    notifyUnits(); 
    updateHistory(); 
} 

è molto fastidioso. Ecco perché in questi casi dovrebbe essere usato il modello di Osservatore. Il tuo oggetto dovrebbe notificare a tutti i listener che è stato aggiornato. Gli ascoltatori (GraphicsContext, Storia, unità) reagiranno in modo corretto e il tuo programma rimarrà facilmente gestibile poiché tutte le sue parti saranno responsabili solo di 1 attività concreta.

+1

nota che il ridisegno() non dovrebbe ridisegnare direttamente, ma è semplicemente un'indicazione che l'oggetto deve essere disegnato. Un renderer esterno si occupa di iterare attraverso gli oggetti che necessitano di essere ridisegnati e disegnandoli nella sequenza corretta, applicando il ritaglio appropriato ecc. – mdma

1

È utile considerare la possibilità che la maggior parte degli oggetti in un sistema debba solo modificare i descrittori specifici del motore (velocità, animazione attiva) e consentire al motore di gestire l'effetto effettivo. Ci sono alcune eccezioni a questo (di solito pallottole e altri effetti single-tick), ma in generale questi sistemi si basano su un singolo tick universale che aggiorna il gioco.La ragione di questo è principalmente, come mdma ha notato nel suo commento sulla risposta di Roman, che il motore reale fa molto più delle semplici operazioni di rendering individuali. Allo stesso modo un motore fisico aggiornerà il mondo intero, calcolando i volumi di collisione tenendo conto del movimento sottotandaglio.

Il caricamento di uno sprite è solitamente una questione di caricamento di tutte le risorse di una mappa e quindi di indirizzarlo per nome all'interno del repository delle risorse della mappa.

La collisione viene generalmente gestita da un motore fisico separato, vedere sopra.

1

essere caduto per questo motivo prima di qui è il mio ingresso:

Per una partita, la prestazione sarà il più grande svantaggio. Probabilmente vuoi mantenere tutti i contesti, gli oggetti, ecc. Di disegni in un unico posto e ottimizzare un blocco di codice più grande.

Se per qualche motivo si voleva rendering su piattaforme diverse, quindi questo approccio non riesce a causa della mancanza di separazione, ancora una volta un renderer plug-in è un approccio migliore

Problemi correlati