Un oggetto in un gioco 2D dovrebbe essere riprodotto da solo?


16

Sto realizzando un gioco 2D simile a un combattente di strada che non è basato su tessere. Di solito le persone raccomandano che le entità vengano date a un renderer che le renda, non loro stesse, ma sembra che l'inverso sia migliore,

Perché uno è migliore rispetto all'altro?

Grazie


Perché pensi che l'inverso sia migliore?

1
@Martin perché l'oggetto suggerirà quale bitmap usare comunque, quindi perché non fare solo object-> render ();
jmasterx,

Risposte:


11

Un paio di considerazioni:

  • come hai detto, ogni sprite dovrebbe "accennare" a quale bitmap usare, ma se l'entità deve renderizzarsi. Quale sarebbe questo 'suggerimento'? Se si tratta di un riferimento a una bitmap, foglio di sprite, ecc ... diversi per ogni sprite, potresti finire per utilizzare più memoria del necessario o avere problemi a gestirla. Un vantaggio di un renderer separato è che hai una sola classe responsabile di qualsiasi gestione patrimoniale. Detto questo, in un gioco di combattimento simile a SF2, potresti avere solo due sprite;)

  • come menzionato altrove, ogni volta che vuoi cambiare la tua API grafica, devi cambiare il codice per tutti i tuoi sprite.

  • il rendering viene eseguito raramente senza riferimento ad un contesto grafico. Quindi o c'è una variabile globale che rappresenta questo concetto, o ogni sprite ha un'interfaccia con render (GraphicalContext ctx). Questo mescola l'API grafica e la logica del tuo gioco (che alcune persone troveranno non eleganti) e potrebbe causare problemi di compilazione.

  • Personalmente trovo che separare il rendering dalle singole entità sia un primo passo interessante nella direzione di vedere il tuo gioco come un sistema che non ha necessariamente bisogno di grafica. Quello che voglio dire è che quando togli il rendering dalla strada, ti rendi conto che gran parte del gameplay avviene in un "mondo non grafico" in cui le coordinate delle entità, i loro stati interni, ecc ... sono importanti. Questo apre le porte a test automatizzati, sistema più disaccoppiato, ecc ...

Tutto sommato, tendo a preferire i sistemi in cui il rendering viene eseguito da una classe separata. Ciò non significa che i tuoi sprite non possano avere alcuni attributi "graficamente correlati" (nome animazione, cornice animazione, altezza x larghezza, ID sprite ecc ...), se ciò rende il renderer più facile da scrivere o più efficiente.

E non so se ciò si applicherebbe al 3D (dove la nozione di mesh e la variabile di coordinate che useresti potrebbero essere legate all'API 3D; mentre x, y, h, w è praticamente indipendente da qualsiasi 2D API).

Spero che questo aiuti.


11

Volete che il sistema di rendering abbia il controllo di ciò che viene disegnato quando. Se invece gli sprite hanno il controllo del rendering, perdi molti guadagni di efficienza e flessibilità. Sono dell'opinione che avere il sistema di rendering sotto controllo porti a un codice più pulito.

Alcuni vantaggi del rendering centralizzato:

  • Z-ordering:
    se gli oggetti di gioco stessi sono responsabili del rendering, dovrai assicurarti di chiamarli nell'ordine corretto. In caso contrario, gli oggetti di sfondo potrebbero essere disegnati su oggetti in primo piano.
    Con il sistema di rendering in controllo, può scegliere di ordinare tutti gli oggetti di rendering, rilevare overloap al momento del rendering e renderizzarli o semplicemente rinunciare a ordinare tutti insieme. Il punto è che la decisione può essere presa facilmente ora.
  • batch:
    l'altro ovvio vantaggio di consentire il controllo del sistema di rendering è il batch. Anche in questo caso il sistema di rendering deve l'opzione di batch sprite per rendere la condivisione una trama. Può utilizzare la suddivisione in triangoli per eseguire il rendering di tutto con una sola chiamata. Potrebbe essere in grado di memorizzare nella cache alcuni calcoli di rendering. O potrebbe semplicemente rendere ogni sprite a sua volta senza nessuna di quelle cose fantasiose. (Nota: è possibile eseguire il batch quando ogni oggetto viene visualizzato automaticamente, ma il problema è meno efficiente e più complesso).

Il modo in cui lo implemento nei miei giochi per far sì che gli oggetti di gioco registrino gli sprite che vogliono disegnare con il sistema di rendering. Quando l'oggetto non vuole più essere disegnato, annulla la registrazione dello sprite o lo contrassegna come inattivo.

Detto questo. Se è più facile far sì che i tuoi oggetti di gioco si riproducano da soli, fallo in quel modo. È molto più importante fare progressi e ottenere qualcosa / qualcosa di disegnato piuttosto che avere un'architettura perfetta.


A proposito dell'ordine z se gli oggetti si disegnano da soli non può un sistema decidere sull'ordine di chiamare il loro metodo di disegno? Intendo dire che centralizzato e non centralizzato sembrano non fare alcuna differenza sull'ordinamento z.
GorillaApe

3

Disclaimer: la tua domanda non fornisce molti dettagli, quindi sto rispondendo con un principio generale. Per favore, scusami se ho frainteso il tuo uso o "render".

In genere utilizzo un oggetto esterno per rendere vari attori in una scena come un modo per incapsulare proprietà e metodi a livello di scena al di fuori dei singoli "oggetti attore". Gli oggetti nella scena devono contenere solo metodi e proprietà interne; dovrebbero solo sapere cosa sono e cosa fanno. Presumibilmente, saranno influenzati da altri oggetti nel gioco e dall'input dell'utente. Ciò influenzerà il modo in cui / se vengono visualizzati sullo schermo. Un "oggetto regista" può, ad esempio, tradurre il tasto "w" per saltare, quindi dire all'oggetto attore .jump (). Tale logica a livello di regista può anche dire agli attori di entrare o uscire completamente dalla scena.

Saluti, David


Ma in questo senso il regista non poteva semplicemente dire acton-> setVisible (false); ?
jmasterx,

Anche nel caso setVisible (false), è un'entità esterna che esegue il rendering controllando la variabile visibile dell'attore e rendendola solo se è vera.
Nav

Il fatto di rendere invisibile un attore non lo rimuove dalla scena. Deve anche smettere di partecipare a collisioni ecc.
finnw,

3

Che cosa succede se un giorno vuoi trasferire il tuo gioco a una risoluzione diversa (ad es. IPhone e amici). Pertanto, una proprietà globale relativa al rendering delle modifiche, come si aggiorna facilmente il codice?


3

Quello che ho usato era un design basato sull'osservatore. Quando ho creato un'istanza di una classe che volevo renderizzare, allora un puntatore era memorizzato nella classe Renderer centrale. Quando chiami RenderFrame(), il renderer ha già tutti gli oggetti esistenti necessari per il rendering e accede alle loro proprietà per farlo. Le classi stesse non avevano idea che sarebbero state rese affatto. Questa API era bella, pulita e facile da usare.


1
+1 interessante. Ho usato questo approccio per il suono mentre usavo il Visitor Pattern per la grafica. Ho pensato che questo avesse più senso per il suono perché mentre la grafica e l'intelligenza artificiale sono in esecuzione sullo stesso clock, il mixer audio è in esecuzione su un altro, quindi un modello di eventi è più facile. Inoltre, non è critico se un evento di movimento (che fa cambiare il pan / riverbero di un canale audio) arriva con qualche millisecondo di ritardo, ma è fondamentale se uno sprite viene disegnato nello stato sbagliato.
finnw,

2

In generale, si tratta sempre di quanto sia facile mantenere ed espandere il codice. Domani capirai che non ti piace l'API grafica che stai utilizzando attualmente e vuoi cambiare. Dovrai ora esaminare tutte le classi degli oggetti e cambiare tutto o hai ancora bisogno di cambiare il codice in un punto centrale del progetto?

Dipende da cosa stanno realmente facendo i tuoi oggetti quando chiami render (). Fintanto che avvolgono semplicemente le chiamate del metodo attorno al tuo motore grafico, va benissimo, dato che verrà comunque data la distinzione grafica <->.

Ad esempio, se i metodi render () sono fondamentalmente metodi di convenienza e hanno un aspetto simile al seguente:

void MyClass::render(const Graphics &g)
{
    g.draw(this);
}

o

void MyClass::render()
{
   mySprite->render();
}

o

void MyClass::render()
{
    mySprite->UseShader(thatshader);
    mySprite->render();
}

o vicino a quello, non penso che sia un problema.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.