Dovrei usare i fogli di calcolo, a causa o nonostante il mio vasto numero di immagini?


13

Sto sviluppando un gioco 2D e ho molti sprite. Ho usato animazioni e modelli 3D per renderizzare in 2D, per dare loro l'aspetto di "Fallout" o "Diablo". È anche più facile che disegnare a mano, lol.

Ho già dovuto ridurre il framerate a 15 fps, che era il più basso che potevo abbassare senza farli sembrare mossi. Tuttavia, è stato triste a causa dell'aspetto incredibilmente fluido di 24 fotogrammi.

Ci sono due ragioni per cui ho fatto questo:

1) Ridurre lo spazio su HDD. Meno sono le immagini, più piccolo sarà il mio gioco totale.

2) Riduzione del consumo di RAM. Meno immagini si caricano, più è probabile che io eviti problemi di rigonfiamento della mia limitazione RAM.

Tuttavia, se esistesse un modo per comprimere le immagini sia nello spazio su disco rigido che nella RAM, lo farei. L'ho provato prima e la maggior parte non riceve alcun cambiamento di qualità quando si passa da RGBA8888 a RGBA5555 e solo un piccolo colpo quando si converte in RGBA4444 nel mio programma TexturePacker. Non lo faccio attualmente, perché SFML sembra usare la stessa quantità di memoria indipendentemente dal tipo di immagine .PNG che è. Ho cercato di caricarlo in modo diverso, ma non sono riuscito a trovare nulla sull'argomento.

Ho letto molto su come gestire i videogiochi 2D. Il consenso è travolgente: metti i tuoi Sprite in una trama più grande per grandi prestazioni! Quindi impacco i miei piccoli folletti in un foglio di ingrandimento molto più grande usando TexturePacker.

Tuttavia, ho in programma di avere 10-15 animazioni per personaggio, 5 direzioni di spostamento e 15-40 fotogrammi per animazione (probabilmente una media di 24). Con 15 animazioni, 5 direzioni e una media di 24 fotogrammi per animazione; Cioè 1800 frame individuali per personaggio. Se confezionato in un foglio sprite, sono invece solo 75 immagini. (Un foglio sprite per animazione, per direzione. 15 * 5)

Per l'enorme personaggio del boss nel gioco, non posso usare uno spritesheet e devo programmare un modo per caricare semplicemente un'immagine alla volta. Non so ancora se posso farlo per le prestazioni.

Per i personaggi, li ho già confezionati in un foglio di calcolo. Per un singolo personaggio che cammina, questo sembra funzionare la maggior parte del tempo, anche se a volte si blocca. Tuttavia, lo attribuisco al mio codice mal concepito che scambia trame invece di precaricare tutte le trame per quel personaggio.

Se dovessi precaricare le trame, ha senso per i fogli sprite. Immagino solo che sia una cattiva idea precaricare 1800 piccole immagini per ogni personaggio.

Tuttavia, immagino che riprodurli in streaming e in memoria uno alla volta sarebbe estremamente veloce, quindi avrei solo bisogno di avere una sola immagine in memoria alla volta. Questo non significherebbe che in un dato momento avrei solo ogni personaggio consumare qualche KB invece di 45 + MB?

Immagino che questo ucciderebbe le mie prestazioni, dato che lo streaming dovrebbe essere incredibilmente veloce (15 immagini in entrata e in uscita dalla memoria e dal rendering, al secondo) e sebbene le immagini sarebbero molto piccole, potrebbe essere un'idea migliore caricare fogli di calcolo dei personaggi nella memoria invece. Ma dovrò comunque codificare un sistema di rendering simile a un flusso di immagini singole per il mio personaggio boss più grande.

Ho sperimentato, ma non è un processo semplice. Soprattutto dato il fatto che sto lavorando su altre parti del motore di gioco che al momento non si occupano di grafica.


1. Non hai specificato i tuoi vincoli RAM o HDD. Quanti personaggi devono avere un accesso veloce? 2. Ci sono diverse domande lungo il testo, forse potresti focalizzarle in grassetto o persino dividere le domande in parti?
Kromster dice di sostenere Monica il

Oh mi dispiace. Non molti. Immagino che il numero massimo di singoli personaggi sullo schermo in qualsiasi momento sia di circa 40. Se le persone hanno fatto un punto per provare a mandare in crash i loro clienti, allora ... 130 è il massimo assoluto. In genere, dovrebbe essere solo 10 per il massimo tipico e il massimo assoluto non sarà superiore a <40. Qualsiasi cosa sopra i 40 sarebbe una rarità estrema, estrema, con gli utenti che cercano intenzionalmente di stipare i personaggi per nessun motivo diverso da uno screenshot o per il divertimento di stipare i personaggi. Qualcosa al di sopra di 10 è raro e qualsiasi cosa su 40 è estremamente, estremamente rara.
Carter81,

Il gioco è un rpg 2D solo per PC (non mobile), tuttavia non vorrei escludere una piattaforma mobile a meno che non sia semplicemente fattibile. Immagino che lo spazio RAM che ho sia limitato a qualunque RAM sia sul PC dell'utente e alla VRAM dell'utente. Dubito fortemente che sarà molto grande HDD saggio. È solo che è sempre vero per il consumo di HDD che più piccolo è, meglio è.
Carter81,

Quanti personaggi UNICI devi avere sullo schermo?
Kromster dice di sostenere Monica il

Non più di 20. Tutto quanto sopra sarebbe quasi impossibile se non avessero barato. Tipicamente 5-10.
Carter81,

Risposte:


16

Abbiamo un caso simile con il nostro RTS Remake. Tutte le unità e le case sono sprite. Abbiamo 18000 folletti per unità, case e terreno, più un altro ~ 6.000 per i colori delle squadre (applicati come maschere). A lungo allungato abbiamo anche circa 30.000 caratteri usati nei caratteri.

Quindi la ragione principale dietro gli atlanti è:

  • meno spreco di RAM (nei vecchi tempi quando si caricava NPOT su GPU lo allungava / lo riempiva su POT, ho letto che è sempre lo stesso con iOS e alcuni framework. È meglio controllare la gamma di hardware target)
  • meno interruttori di trama
  • caricamento più veloce di tutto in meno blocchi più grandi

Cosa non ha funzionato per noi:

  • trame pallettizzate. La funzione esisteva solo in OpenGL 1.x 2.x e anche allora è stata in gran parte abbandonata dai produttori di GPU. Tuttavia, se miri a OpenGL + Shader, puoi farlo anche tu con il codice shader!
  • Trame NPOT, abbiamo avuto problemi con bordi sbagliati e sprite sfocate, il che è inaccettabile nella pixel art. Anche l'utilizzo della RAM era molto più elevato.

Ora abbiamo tutto impacchettato in diverse dozzine di atlanti 1024x1024 (le moderne GPU supportano dimensioni ancora più grandi) e funzionano bene consumando solo ~ 300mb di memoria, il che è abbastanza buono per un gioco per PC. Alcune ottimizzazioni che abbiamo avuto:

  • aggiungi l'opzione utente per usare RGB5_A1 invece di RGBA8 (ombre a scacchiera)
  • evitare Alpha a 8 bit quando possibile e utilizzare il formato RGB5_A1
  • comprimere gli sprite in atlanti (vedere algoritmi di Bin Packing)
  • archiviare e caricare tutto in un unico blocco dall'HDD (i file di risorse devono essere generati offline)
  • potresti anche provare i formati di compressione hardware (DXT, S3TC, ecc.)

Quando consideri seriamente il passaggio a dispositivi mobili ti preoccuperai dei vincoli. Per ora basta far funzionare il gioco e attirare i giocatori! ;)


Questa è stata di gran lunga la soluzione migliore! I miei sprite non hanno nemmeno un aspetto diverso in RGB5_A1 o RGBA4444, ma risparmiano memoria. Un tuo suggerimento in chat per precaricare tutte le mie risorse in RAM e VRAM è perfetto. Inoltre, hai suggerito di avere livelli grafici opzionali, come client ad alta definizione per coloro che hanno la RAM, o un'opzione per ridurre della metà il framerate, ecc. Ottimi suggerimenti tutt'intorno, ed esattamente ciò di cui avevo bisogno!
Carter81,

5

Ho una risposta tangenzialmente correlata qui , ma l'idea generale è che, se stai caricando e disegnando trame in momenti diversi (non stai caricando trame aggiuntive mentre stai eseguendo il rendering), ci sono due posti in cui ciò che influenzerà le tue prestazioni:

Tempo di caricamento:

Questo è il momento in cui carichi le tue trame in memoria. L' intera quantità di dati che stai inviando a VRAM è ciò che definirà principalmente il tempo di caricamento. Rendere le tue trame di formati più piccoli, come RGBA4444, renderà questo più veloce. Tuttavia, a meno che tu non stia caricando trame in centinaia di megabyte su VRAM, probabilmente non avrai un collo di bottiglia qui. Se lo fai, una bella schermata di caricamento può facilitare l'attesa.

Unire le tue trame agli atlanti avrà un effetto limitato, poiché l'intera quantità di informazioni che invii in VRAM sarà la stessa. In effetti, se stai atlanti le tue trame e devi lasciare spazi vuoti nei tuoi atlanti, allora in realtà invierai di più dati in VRAM, e quindi questa parte sarà più lenta!

Prestazioni di rendering:

Una volta che tutte le trame sono in VRAM, la quantità di trame che hai non influisce sulle prestazioni di rendering. Esistono quattro elementi che influiscono sulle prestazioni di rendering:

  1. Cambia lo stato di rendering : ogni volta che cambi l'immagine da cui vuoi eseguire il rendering, aumenterà notevolmente il tempo necessario per renderla. In generale, si desidera ridurre al minimo la quantità di cambiamenti di stato e si può ridurre la quantità di cambiamenti di stato raggruppando diverse immagini che verranno disegnate consecutivamente in un atlante di trama.

    Solo l'atlante non è abbastanza. Devi ottenere un atlante in modo da ridurre i cambiamenti di stato, al fine di ottenere guadagni in termini di prestazioni. Ad esempio, si potrebbe pensare che avere il tuo personaggio principale in un foglio sprite ti farà ottenere un guadagno in termini di prestazioni, ma se stai disegnando solo uno sprite da quel foglio sprite per fotogramma, non otterrai alcun guadagno in termini di prestazioni rispetto al ogni sprite in un file separato.

    Il corretto atlante non è banale, ma in generale puoi raggruppare in sicurezza gli sprite dallo stesso livello. Ad esempio, avere tutti gli elementi della GUI in un foglio sprite è un'idea molto promettente, mentre raggruppare i mostri in ordine alfabetico potrebbe non esserlo.

  2. Disegna chiamate: in generale, potresti voler ridurre al minimo le tue chiamate di disegno. Una buona regola empirica è che se non ci sono cambiamenti nello stato di rendering tra due chiamate di disegno, è possibile unirle in una singola chiamata di disegno. Per ottenere prestazioni più avanzate, puoi usare, per esempio, 8 campionatori di trame e chiamate di disegno di gruppo per ogni 8 trame, quindi devi solo cambiare trame ogni 8 trame.

  3. Conteggio dei triangoli: infatti, più triangoli disegni, più tempo ci vorrà per disegnarli. Tuttavia, nei computer moderni e per la maggior parte dei giochi 2D, sarai molto lontano dal massimizzare questo. Puoi disegnare in sicurezza centinaia di migliaia di sprite per fotogramma e ottenere comunque framerate follemente buoni. Probabilmente sarai più legato alla CPU se stai disegnando quantità estreme di sprite prima di avere problemi con la tua GPU.

  4. Impostazioni API: se stai facendo tutto nel modo giusto e stai ancora ottenendo framerate stranamente bassi, controlla le impostazioni con cui stai disegnando i tuoi sprite. Non conosco SFML, ma per esempio, in Direct3D 9, la creazione di un buffer vertici con D3DUSAGE_DYNAMICo in D3DPOOL_MANAGEDpuò facilmente aumentare i tempi di rendering di dieci volte. Naturalmente, l'utilizzo di vSync limiterà il tuo framerate alla frequenza di aggiornamento del monitor. Inoltre, l'utilizzo di FVF non allineati può ridurre le prestazioni in alcune GPU. Anche questo è per Direct3D 9.

    Nel tuo caso, controlla la documentazione per l'API che stai utilizzando.

Se hai solo una quantità da bassa a moderata di trame (meno di 1 GB) e stai disegnando basse quantità di sprite (meno di un milione per fotogramma), la prima cosa che vorrei guardare è cambiare le impostazioni dell'API e quindi ridurre la quantità di stati di rendering e disegnare chiamate.


Se non mi preoccupo dei tempi di caricamento, devo presumere che, a meno che non rimanga senza RAM o VRAM, dovrei semplicemente caricare tutto in memoria in anticipo?
Carter81,

Mi preoccupo solo di tutto PERCHÉ ho paura di rimanere a corto di RAM / VRAM. Non so perché, ma mi pietrifica che gli utenti del mio gioco si blocchino ogni volta che provano a caricarsi in un'area che ha troppi sprite unici o si schiantano ogni volta che troppi personaggi entrano sul loro schermo. Se non sbaglio e ogni singolo sprite consuma 96 KB, allora se ogni personaggio unico ha 15 animazioni, 5 direzioni e una media di 24 fotogrammi per animazione, ogni singolo personaggio completamente caricato è 173 MB. Potrebbero esserci 10, forse anche di più, personaggi unici sullo schermo contemporaneamente.
Carter81,

@KromStern: se sei un atlante e devi lasciare spazi vuoti (riempimento), i dati saranno più grandi e quindi i tempi di caricamento saranno più lunghi. È chiaro che la causa dei tempi di caricamento più lunghi è lo spazio vuoto e che il tempo di caricamento totale è correlato alla quantità totale di dati e non alla quantità di trame. Non vedo nulla di fuorviante e penso che una persona con abbastanza conoscenza per comprendere la domanda originale, sarà in grado di unire i punti e trarre le proprie conclusioni per tutti i casi in cui le trame e gli atlanti sono PoT e nPoT.
Panda Pajama,

1
@ Carter81: devi scegliere la tua configurazione hardware di destinazione, come "i5, 1 gb di RAM, NVidia GT260, 400mb hdd" e lavorare da quello. Ci saranno sempre PC più deboli e con meno RAM.
Kromster dice di sostenere Monica il

Ovviamente, non hanno bisogno di tutte e 15 le animazioni in un dato momento. Tuttavia, cosa succederebbe se tutti e 10 i personaggi unici entrassero contemporaneamente in "Modalità combattimento" e quindi richiedessero 5 set di animazioni (marcia, corsa, inattività, non combattimento ecc.) Da scambiare con altri 5 (combattimento, combattimento inattivo, combattimento ecc.)? Ho paura di scambiare trame perché quando ho provato a farlo con SFML, ha creato un notevole blocco o una pausa del client quando si cambiavano atlanti texture. Non so quale sarebbe il mio collo di bottiglia con le varie strategie per gestire così tanti sprite.
Carter81,
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.