Come posso ottimizzare un mondo voxel in stile Minecraft?


76

Ho trovato i meravigliosi grandi mondi di Minecraft estremamente lenti da navigare, anche con un quad core e una scheda grafica carnosa.

Presumo che la lentezza di Minecraft provenga da:

  • Java, poiché il partizionamento spaziale e la gestione della memoria sono più veloci nel C ++ nativo.
  • Partizionamento del mondo debole.

Potrei sbagliarmi su entrambe le ipotesi. Tuttavia, questo mi ha fatto pensare al modo migliore per gestire grandi mondi voxel. Come è un vero mondo 3D, dove un blocco può esistere in qualsiasi parte del mondo, è fondamentalmente una grande matrice 3D [x][y][z], dove ogni blocco nel mondo ha un tipo (cioè BlockType.Empty = 0, BlockType.Dirt = 1etc.)

Suppongo che per far funzionare bene questo tipo di mondo, dovresti:

  • Usa un albero di qualche varietà ( oct / kd / bsp ) per dividere tutti i cubi; sembra che un ott / kd sarebbe l'opzione migliore, dato che puoi semplicemente partizionare a livello di cubo e non di triangolo.
  • Usa alcuni algoritmi per capire quali blocchi sono attualmente visibili, poiché i blocchi più vicini all'utente potrebbero offuscare i blocchi dietro, rendendo inutile renderli.
  • Mantieni l'oggetto blocco stesso leggero, quindi è veloce aggiungerlo e rimuoverlo dagli alberi.

Immagino che non ci sia una risposta giusta a questo, ma sarei interessato a vedere le opinioni delle persone sull'argomento. Come miglioreresti le prestazioni in un grande mondo basato su voxel?



2
Quindi cosa stai chiedendo? Stai chiedendo buoni approcci per la gestione di grandi mondi o feedback sul tuo approccio particolare o opinioni sull'argomento della gestione di grandi mondi?
doppelgreener,

1
Buone cose finora, si tratta più di qual è l' approccio comune più comune a questo tipo di cose. Non sono specificamente alla ricerca di feedback sul mio approccio in quanto tutto ciò che ho proposto è ciò che mi aspetterei logicamente che accada. Volevo solo maggiori informazioni sull'argomento e non ci sono state troppe notizie dopo un paio di ricerche. Immagino che la mia domanda non riguardi solo il rendering delle prestazioni, ma anche come gestire un così grande volume di dati ... come la
suddivisione in

2
Quindi sii chiaro e aggiungi una domanda al tuo post in modo da sapere a quale domanda stiamo rispondendo. ;)
doppelgreener

3
Cosa intendi con "estremamente lento nella navigazione"? C'è sicuramente un po 'di rallentamento quando il gioco genera nuovi terreni, ma dopo ciò, Minecraft tende a gestire il terreno abbastanza bene.
thedaian

Risposte:


106

Voxel Engine Rocks

Voxel Engine Grass

Per quanto riguarda Java vs C ++, ho scritto un motore voxel in entrambi (versione C ++ mostrata sopra). Scrivo anche motori voxel dal 2004 (quando non erano di moda). :) Posso dire con poca esitazione che le prestazioni del C ++ sono di gran lunga superiori (ma è anche più difficile da programmare). Dipende meno dalla velocità di calcolo e dalla gestione della memoria. Giù le mani, quando stai allocando / deallocando tutti i dati di ciò che è in un mondo voxel, C (++) è il linguaggio da battere. però, dovresti pensare al tuo obiettivo. Se le prestazioni sono la tua massima priorità, scegli C ++. Se vuoi solo scrivere un gioco senza prestazioni all'avanguardia, Java è sicuramente accettabile (come evidenziato da Minecraft). Esistono molti casi banali / marginali, ma in generale ci si può aspettare che Java funzioni circa 1,75-2,0 volte più lentamente del C ++ (ben scritto). Puoi vedere una versione del mio motore scarsamente ottimizzata in azione qui (EDIT: versione più recente qui ). Mentre la generazione di blocchi può sembrare lenta, tieni presente che sta generando volumetricamente diagrammi 3D di voronoi, calcolando normali di superficie, illuminazione, AO e ombre sulla CPU con metodi a forza bruta. Ho provato varie tecniche e riesco a ottenere una generazione di blocchi circa 100 volte più veloce utilizzando varie tecniche di memorizzazione nella cache e istanziazione.

Per rispondere al resto della domanda, ci sono molte cose che puoi fare per migliorare le prestazioni.

  1. Caching. Ovunque sia possibile, è necessario calcolare i dati una volta. Ad esempio, creo l'illuminazione nella scena. Potrebbe utilizzare l'illuminazione dinamica (nello spazio dello schermo, come post-processo), ma cuocere l'illuminazione significa che non devo passare le normali per i triangoli, il che significa ...
  2. Passa il minor numero di dati possibile alla scheda video. Una cosa che le persone tendono a dimenticare è che più dati passi alla GPU, più tempo ci vuole. Passo in un unico colore e in una posizione di vertice. Se voglio fare cicli diurni / notturni, posso semplicemente eseguire la classificazione dei colori, oppure posso ricalcolare la scena mentre il sole cambia gradualmente.

  3. Poiché il trasferimento di dati alla GPU è così costoso, è possibile scrivere un motore nel software che è più veloce per alcuni aspetti. Il vantaggio del software è che può fare tutti i tipi di manipolazione dei dati / accesso alla memoria che semplicemente non è possibile su una GPU.

  4. Gioca con la dimensione del lotto. Se si utilizza una GPU, le prestazioni possono variare notevolmente in base alla dimensione di ciascun array di vertici che si passa. Di conseguenza, gioca con le dimensioni dei pezzi (se usi pezzi). Ho scoperto che i blocchi 64x64x64 funzionano abbastanza bene. Non importa cosa, mantieni i tuoi cubi cubici (senza prismi rettangolari). Ciò renderà la codifica e le varie operazioni (come le trasformazioni) più facili e, in alcuni casi, più performanti. Se memorizzi un solo valore per la lunghezza di ogni dimensione, tieni presente che si tratta di due registri in meno che vengono scambiati durante il calcolo.

  5. Considera gli elenchi di visualizzazione (per OpenGL). Anche se sono alla "vecchia" maniera, possono essere più veloci. Devi inserire un elenco di visualizzazione in una variabile ... se chiami le operazioni di creazione dell'elenco di visualizzazione in tempo reale, sarà ungodly lento. Come è più veloce un elenco di visualizzazione? Aggiorna solo lo stato, rispetto agli attributi per vertice. Questo significa che posso passare fino a sei facce, quindi un colore (contro un colore per ogni vertice del voxel). Se stai usando GL_QUADS e voxel cubici, questo potrebbe risparmiare fino a 20 byte (160 bit) per voxel! (15 byte senza alfa, anche se di solito si desidera mantenere le cose allineate a 4 byte.)

  6. Uso un metodo a forza bruta per il rendering di "blocchi" o pagine di dati, che è una tecnica comune. A differenza di octrees, è molto più facile / veloce leggere / elaborare i dati, anche se molto meno compatibile con la memoria (tuttavia, in questi giorni è possibile ottenere 64 gigabyte di memoria per $ 200- $ 300) ... non che l'utente medio abbia questo. Ovviamente, non è possibile allocare un enorme array per tutto il mondo (un set di voxel 1024x1024x1024 è 4 gigabyte di memoria, supponendo che per voxel venga utilizzato un int a 32 bit). Quindi allocare / deallocare molti piccoli array, in base alla loro vicinanza al visualizzatore. È inoltre possibile allocare i dati, ottenere l'elenco di visualizzazione necessario, quindi scaricare i dati per risparmiare memoria. Penso che la combinazione ideale potrebbe essere quella di utilizzare un approccio ibrido di ocre e array: archiviare i dati in un array durante la generazione procedurale del mondo, l'illuminazione, ecc.

  7. Rendering vicino al lontano ... un pixel tagliato viene risparmiato tempo. La gpu genererà un pixel se non supera il test del buffer di profondità.

  8. Rendering solo blocchi / pagine nella finestra (autoesplicativo). Anche se la gpu sa come tagliare i poligoni al di fuori della finestra, passare questi dati richiede ancora tempo. Non so quale sarebbe la struttura più efficiente per questo ("vergognosamente", non ho mai scritto un albero BSP), ma anche un semplice raycast su una base per blocco potrebbe migliorare le prestazioni, e ovviamente testare contro il frustum di visualizzazione sarebbe risparmia tempo.

  9. Informazioni ovvie, ma per i neofiti: rimuovi ogni singolo poligono che non si trova in superficie, ovvero se un voxel è composto da sei facce, rimuovi le facce che non vengono mai visualizzate (toccano un altro voxel).

  10. Come regola generale di tutto ciò che fai in programmazione: CACHE LOCALITY! Se riesci a mantenere le cose nella cache locale (anche per un breve periodo di tempo, ciò farà una differenza enorme. Ciò significa mantenere i tuoi dati congruenti (nella stessa area di memoria) e non cambiare le aree di memoria per elaborarle troppo spesso. , idealmente, lavora su un blocco per thread e mantieni quella memoria esclusiva per il thread. Questo non si applica solo alla cache della CPU. Pensa alla gerarchia della cache in questo modo (dalla più lenta alla più veloce): rete (cloud / database / ecc.) -> disco rigido (ottieni un SSD se non ne hai già uno), ram (ottieni un canale tripple o RAM maggiore se non lo hai già), cache (s) della CPU, registri. Cerca di mantenere i tuoi dati attivi quest'ultimo fine, e non scambiarlo più del necessario.

  11. Threading. Fallo. I mondi Voxel sono adatti per il threading, poiché ogni parte può essere calcolata (principalmente) indipendentemente dagli altri ... Ho visto letteralmente un miglioramento quasi 4x (su un Core i7 a 4 core, 8 thread) nella generazione procedurale mondiale quando ho scritto il routine per l'infilatura.

  12. Non utilizzare tipi di dati char / byte. O pantaloncini. Il tuo consumatore medio avrà un moderno processore AMD o Intel (come probabilmente tu). Questi processori non hanno registri a 8 bit. Calcolano i byte mettendoli in uno slot a 32 bit, quindi riconvertendoli (forse) in memoria. Il tuo compilatore può fare ogni sorta di voodoo, ma l'uso di un numero a 32 o 64 bit ti darà i risultati più prevedibili (e più veloci). Allo stesso modo, un valore "bool" non richiede 1 bit; il compilatore utilizzerà spesso 32 bit completi per un bool. Potrebbe essere allettante eseguire determinati tipi di compressione sui dati. Ad esempio, è possibile memorizzare 8 voxel come numero singolo (2 ^ 8 = 256 combinazioni) se fossero tutti dello stesso tipo / colore. Tuttavia, devi pensare alle conseguenze di questo: potrebbe risparmiare molta memoria, ma può anche ostacolare le prestazioni, anche con un piccolo tempo di decompressione, perché anche quella piccola quantità di tempo extra si ridimensiona cubicamente con le dimensioni del tuo mondo. Immagina di calcolare un raycast; per ogni fase del raycast, dovresti eseguire l'algoritmo di decompressione (a meno che non ti venga in mente un modo intelligente di generalizzare il calcolo per 8 voxel in una fase del raggio).

  13. Come menziona Jose Chavez, il modello di design dei pesi mosca può essere utile. Proprio come useresti una bitmap per rappresentare una tessera in un gioco 2D, puoi costruire il tuo mondo da diversi tipi di tessere 3D (o blocchi). Il rovescio della medaglia a questo è la ripetizione delle trame, ma puoi migliorarlo usando trame varianza che si incastrano. Come regola generale, si desidera utilizzare l'istanza ovunque sia possibile.

  14. Evita l'elaborazione dei vertici e dei pixel nello shader quando emetti la geometria. In un motore voxel avrai inevitabilmente molti triangoli, quindi anche un semplice pixel shader può ridurre notevolmente i tempi di rendering. È meglio eseguire il rendering su un buffer, quindi fare pixel shader come post-processo. Se non riesci a farlo, prova a fare calcoli nel tuo shader di vertice. Altri calcoli dovrebbero essere inseriti nei dati del vertice ove possibile. Passaggi aggiuntivi diventano molto costosi se è necessario eseguire nuovamente il rendering di tutta la geometria (come la mappatura dell'ombra o la mappatura dell'ambiente). A volte è meglio rinunciare a una scena dinamica a favore di dettagli più ricchi. Se il tuo gioco ha scene modificabili (ad es. Terreno distruttibile) puoi sempre ricalcolare la scena man mano che le cose vengono distrutte. La ricompilazione non è costosa e dovrebbe richiedere meno di un secondo.

  15. Rilassa i loop e mantieni le matrici piatte! Non farlo:

    for (i = 0; i < chunkLength; i++) {
     for (j = 0; j < chunkLength; j++) {
      for (k = 0; k < chunkLength; k++) {
       MyData[i][j][k] = newVal;
      }
     }
    }
    //Instead, do this:
    for (i = 0; i < chunkLengthCubed; i++) {
     //figure out x, y, z index of chunk using modulus and div operators on i
     //myData should have chunkLengthCubed number of indices, obviously
     myData[i] = newVal;
    }
    

    EDIT: Attraverso test più approfonditi, ho scoperto che questo può essere sbagliato. Usa la custodia che funziona meglio per il tuo scenario. Generalmente, le matrici dovrebbero essere piatte, ma l'utilizzo di loop multi-indice può spesso essere più veloce a seconda del caso

EDIT 2: quando si usano loop multiindice, è meglio eseguire il loop nell'ordine z, y, x anziché viceversa. Il tuo compilatore potrebbe ottimizzarlo, ma sarei sorpreso se lo facesse. Ciò massimizza l'efficienza dell'accesso alla memoria e della località.

for (k < 0; k < volumePitch; k++) {
    for (j = 0; j < volumePitch; j++) {
        for (i = 0; i < volumePitch; i++) {
            myIndex = k*volumePitch*volumePitch + j*volumePitch + i;
        }
    }
}
  1. A volte devi fare ipotesi, generalizzazioni e sacrifici. Il migliore che puoi fare è supporre che la maggior parte del tuo mondo sia completamente statica e cambi solo ogni duemila frame. Per le parti animate del mondo, quelle possono essere fatte in un passaggio separato. Supponi anche che la maggior parte del tuo mondo sia completamente opaca. Gli oggetti trasparenti possono essere resi in un passaggio separato. Supponiamo che le trame varino solo ogni unità x o che gli oggetti possano essere posizionati solo con incrementi di x. Assumi una dimensione del mondo fissa ... allettante come un mondo infinito, può portare a requisiti di sistema imprevedibili. Ad esempio, per semplificare la generazione del modello di voronoi nelle rocce sopra, ho ipotizzato che ogni punto centrale di voronoi si trovasse in una griglia uniforme, con un leggero scostamento (in altre parole, hashing geometrico implicito). Supponi un mondo che non avvolge (ha i bordi). Ciò può semplificare molte complessità introdotte da un sistema di coordinate di avvolgimento, a costi minimi per l'esperienza dell'utente.

Puoi leggere di più sulle mie implementazioni sul mio sito


9
+1. Bel tocco tra cui le foto in alto come incentivo alla lettura del saggio. Ora che ho letto il saggio, posso dire che non erano necessari e ne è valsa la pena. ;)
George Duckett,

Grazie - un'immagine vale più di mille parole, come si suol dire. :) Oltre a rendere meno intimidatorio il mio muro di testo, volevo dare ai lettori un'idea di quanti voxel si potevano rendere ad un ritmo ragionevole usando le tecniche descritte.
Gavan Woolery,

14
Vorrei ancora che SE consentisse le risposte specifiche preferite.
joltmode

2
@PatrickMoriarty # 15 è un trucco piuttosto comune. Supponendo che il compilatore non esegua questa ottimizzazione (potrebbe srotolare il ciclo, ma probabilmente non compatterà un array multidimensionale). Si desidera conservare tutti i dati nello stesso spazio di memoria contiguo, per la memorizzazione nella cache. Un array multidimensionale può (potenzialmente) essere allocato su molti spazi, in quanto è un array di puntatori. Per quanto riguarda lo svolgimento del ciclo, pensa a come appare il codice compilato. Per il minor numero di swap di registro e cache, si desidera produrre il minor numero di vars / istruzioni. Quale pensi che compili di più?
Gavan Woolery,

2
Mentre alcuni dei punti qui sono buoni, specialmente per quanto riguarda la memorizzazione nella cache, il threading e la riduzione al minimo del trasferimento GPU, alcuni di questi sono terribilmente inaccurati. 5: SEMPRE utilizzare VBO / VAO anziché elenchi di visualizzazione. 6: più RAM richiede solo più larghezza di banda. Con conduce a 12: il contrario di EXACT è vero per la memoria moderna, per la quale ogni byte salvato aumenta le possibilità di inserire più dati nella cache. 14: In Minecraft, ci sono più vertici che pixel (tutti quei cubi distanti), quindi sposta il calcolo sul pixel shader, non DA, preferibilmente con un'ombra differita.

7

Ci sono molte cose che Minecraft potrebbe fare in modo più efficiente. Ad esempio, Minecraft carica interi pilastri verticali di circa 16x16 piastrelle e li rende. Ritengo che sia molto inefficiente inviare e rendere inutilmente molte tessere. Ma non credo che la scelta della lingua sia importante.

Java può essere abbastanza veloce ma per qualcosa che è orientato ai dati, C ++ ha un grande vantaggio con un sovraccarico significativamente minore per l'accesso alle matrici e lavorare all'interno dei byte. D'altro canto, è molto più semplice eseguire il threading su tutte le piattaforme in Java. A meno che tu non preveda di utilizzare OpenMP o OpenCL, non troverai questa comodità in C ++.

Il mio sistema ideale sarebbe una gerarchia leggermente più complessa.

Tile è una singola unità, probabilmente intorno a 4 byte per conservare informazioni come il tipo di materiale e l'illuminazione.

Il segmento sarebbe un blocco di tessere 32x32x32.

  1. Le bandiere verrebbero posizionate per ciascuno dei sei lati, se l'intero lato è costituito da blocchi solidi. Ciò consentirebbe al renderer di occludere segmenti dietro quel segmento. Attualmente Minecraft non sembra eseguire test di occlusione. Ma è stato menzionato la disponibilità dell'abbattimento dell'occlusione hardware che può essere costoso ma migliore del rendering di enormi quantità di poligoni su schede di fascia bassa.
  2. I segmenti verranno caricati in memoria solo durante l'attività (giocatori, NPC, fisica dell'acqua, crescita degli alberi, ecc.). Altrimenti verrebbero inviati direttamente ancora compressi dal disco ai client.

I settori sarebbero un blocco di segmenti 16x16x8.

  1. I settori tracciano il segmento più alto per ogni colonna verticale in modo che i segmenti più alti di quello possano essere rapidamente definiti vuoti.
  2. Traccia anche il segmento occluso inferiore, in modo che ogni segmento che deve essere reso dalla superficie possa essere rapidamente afferrato.
  3. I settori seguiranno anche la prossima volta che ogni segmento dovrà essere aggiornato (fisica dell'acqua, crescita degli alberi, ecc.). In questo modo il caricamento in ciascun settore sarebbe sufficiente per mantenere vivo il mondo e caricarlo solo in segmenti abbastanza a lungo per completare i loro compiti.
  4. Tutte le posizioni delle entità verrebbero tracciate rispetto al settore. Ciò impedirebbe gli errori in virgola mobile presenti in Minecraft quando si viaggia molto lontano dal centro della mappa.

Il mondo sarebbe una mappa infinita di settori.

  1. Il mondo sarebbe responsabile della gestione dei settori e dei loro prossimi aggiornamenti.
  2. Il mondo invierebbe preventivamente segmenti ai giocatori lungo i loro potenziali percorsi. Minecraft invia in modo reattivo i segmenti richiesti dal client, causando un ritardo.

In genere mi piace questa idea, ma come mapperesti internamente i settori del mondo ?
Clashsoft,

Mentre un array sarebbe la migliore soluzione per le tessere nel segmento e i segmenti nel settore, i settori nel mondo avrebbero bisogno di qualcosa di diverso per consentire una dimensione infinita della mappa. Il mio suggerimento sarebbe di usare una tabella hash (pseudo dizionario <Vector2i, Settore>), usando le coordinate XY per l'hash. Quindi il mondo può semplicemente cercare un settore a determinate coordinate.
Josh Brown,

6

Minecraft è piuttosto veloce, anche sul mio 2 core. Java non sembra essere un fattore limitante, qui, sebbene ci sia un po 'di ritardo del server. I giochi locali sembrano fare meglio, quindi assumerò alcune inefficienze, lì.

Per quanto riguarda la tua domanda, Notch (autore di Minecraft) ha scritto un po 'di blog sulla tecnologia. In particolare, il mondo è archiviato in "blocchi" (a volte li vedi, specialmente quando uno manca perché il mondo non si è ancora compilato), quindi la prima ottimizzazione è decidere se un blocco può essere visto o meno .

All'interno di un blocco, come hai intuito, l'app deve decidere se un blocco può essere visto o meno, in base al fatto che sia o meno oscurato da altri blocchi.

Si noti inoltre che ci sono FACCE a blocchi, che possono essere considerati non visti, in virtù dell'essere oscurati (ovvero, un altro blocco copre il viso) o in quale direzione punta la fotocamera (se la fotocamera è rivolta a nord, è possibile vedere la parete nord di QUALSIASI blocco!)

Le tecniche più comuni includerebbero anche il non mantenere oggetti di blocco separati ma, piuttosto, un "blocco" di tipi di blocco, con un singolo blocco prototipo per ciascuno, insieme ad un insieme minimo di dati per descrivere come questo blocco può essere personalizzato. Ad esempio, non ci sono blocchi di granito personalizzati (che conosco), ma l'acqua ha dati per dire quanto è profonda lungo ciascuna faccia laterale, da cui è possibile calcolare la direzione del flusso.

La tua domanda non è chiara se stai cercando di ottimizzare la velocità di rendering, la dimensione dei dati o cosa. Un chiarimento sarebbe utile.


4
i "grumi" sono generalmente chiamati blocchi.
Marco,

Buona cattura (+1); risposta aggiornata. (Stava facendo per la memoria in origine, e ho dimenticato la parola giusta.)
Olie

Le inefficienze a cui ti riferisci è anche conosciuta come "la rete", che non agisce mai del tutto allo stesso modo due volte, anche con gli stessi punti finali di comunicazione.
Edwin Buck,

4

Ecco solo alcune parole di informazioni e consigli generali, che posso dare come un modder di Minecraft troppo esperto (che può almeno darti una guida).

Il motivo per cui Minecraft è lento ha molto a che fare con alcune decisioni di progettazione discutibili e di basso livello - ad esempio, ogni volta che un blocco viene referenziato dal posizionamento, il gioco convalida le coordinate con circa 7 se le istruzioni per assicurarsi che non sia fuori dai limiti . Inoltre, non c'è modo di afferrare un 'blocco' (un'unità di blocchi 16x16x256 con cui il gioco funziona) quindi fare riferimento direttamente ai blocchi in esso, al fine di aggirare le ricerche nella cache e, erm, problemi di convalida sciocca (iow, ogni riferimento di blocco comporta anche una ricerca di pezzi, tra le altre cose.) Nella mia mod, ho creato un modo per afferrare e cambiare direttamente la matrice di blocchi, che ha aumentato la generazione di dungeon massiccia da inattuabilmente inattaccabile a inavvertitamente veloce.

EDIT: rimossa l'affermazione che la dichiarazione di variabili in un ambito diverso ha portato a miglioramenti delle prestazioni, questo non sembra essere il caso. Credo che nel momento in cui ho combinato questo risultato con qualcos'altro che stavo sperimentando (in particolare, rimuovendo i cast tra doppi e galleggianti nel codice relativo all'esplosione consolidando in doppi ... comprensibilmente, questo ha avuto un impatto enorme!)

Inoltre, sebbene non sia l'area in cui trascorro molto tempo, la maggior parte delle strozzature delle prestazioni in Minecraft è un problema con il rendering (circa il 75% del tempo di gioco è dedicato al mio sistema). Ovviamente non ti interessa molto se la preoccupazione è supportare più giocatori in multiplayer (il server non rende nulla), ma è importante nella misura in cui le macchine di tutti sono in grado di giocare.

Quindi, qualunque sia il linguaggio che scegli, cerca di diventare molto intimo con l'implementazione / dettagli di basso livello, perché anche un piccolo dettaglio in un progetto come questo potrebbe fare la differenza (un esempio per me in C ++ è stato "Può il compilatore funzionare staticamente in linea puntatori? "Sì, può! Ha fatto un'incredibile differenza in uno dei progetti a cui stavo lavorando, dato che avevo meno codice e il vantaggio di inline.)

Non mi piace molto quella risposta perché rende difficile la progettazione di alto livello, ma è la dolorosa verità se le prestazioni sono un problema. Spero che l'abbia trovato utile!

Inoltre, la risposta di Gavin copre alcuni dettagli che non volevo ripetere (e molto altro! È chiaramente più informato sull'argomento di me), e sono d'accordo con lui per la maggior parte. Dovrò sperimentare il suo commento su processori e dimensioni variabili più brevi, non ne ho mai sentito parlare-- Mi piacerebbe provare a me stesso che è vero!


2

Il punto è pensare a come caricare prima di tutto i dati. Se esegui lo streaming dei dati della tua mappa in memoria quando necessario, esiste un limite naturale a ciò che puoi renderizzare, questo è già un aggiornamento delle prestazioni di rendering.

Quello che fai con questi dati dipende da te. Per le prestazioni di GFX, è quindi possibile utilizzare Ritaglio per ritagliare oggetti nascosti, oggetti troppo piccoli per essere visibili, ecc.

Se poi stai solo cercando tecniche di Performance Grafica, sono sicuro che puoi trovare montagne di cose in rete.


1

Qualcosa da guardare è il modello di design Flyweight . Credo che la maggior parte delle risposte qui faccia riferimento a questo modello di progettazione in un modo o nell'altro.

Anche se non conosco il metodo esatto che Minecraft sta usando per minimizzare la memoria per ogni tipo di blocco, questa è una possibile strada da usare nel tuo gioco. L'idea è di avere un solo oggetto, come un oggetto prototipo, che contiene informazioni su tutti i blocchi. L'unica differenza sarebbe la posizione di ciascun blocco.

Ma anche la posizione può essere minimizzata: se sai che un blocco di terra è di un tipo, perché non memorizzare le dimensioni di quella terra come un blocco gigante, con un set di dati sulla posizione?

Ovviamente l'unico modo per sapere è iniziare a implementare il tuo ed eseguire alcuni test di memoria per le prestazioni. Fateci sapere come va!

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.