Come caricare blocchi impilabili al volo?


8

Attualmente sto lavorando a un mondo infinito, per lo più ispirato a Minecraft.
Un Pezzo consiste di 16x16x16 blocchi. Un blocco (cubo) è 1x1x1.

Funziona in modo molto fluido con un ViewRange di 12 blocchi (12x16) sul mio computer. Belle.
Quando cambio l'altezza di Chunk a 256 questo diventa - ovviamente - incredibile ritardo.

Quindi quello che sostanzialmente voglio fare è impilare blocchi. Ciò significa che il mio mondo potrebbe essere grande [∞, 16, ∞].

La domanda è adesso come generare blocchi al volo?
Al momento, generi blocchi circolari non esistenti attorno alla mia posizione (vicino al lontano). Dal momento che non impilare ancora blocchi, questo non è molto complesso.

Come nota a margine importante qui: voglio anche avere biomi, con diversa altezza min / max. Quindi in Biome Flatlands lo strato più alto con blocchi sarebbe 8 (8x16) - in Biome Mountains lo strato più alto con blocchi sarebbe 14 (14x16). Proprio come esempio.

Quello che potrei fare sarebbe caricare 1 pezzo sopra e sotto di me, per esempio.
Ma qui il problema sarebbe che le transizioni tra diversi biomi potrebbero essere più grandi di un pezzo su y.




Transizioni tra biomi




Il mio pezzo attuale si sta caricando in azione

Esempio di caricamento del blocco



Per completezza qui il mio attuale "algoritmo" di caricamento di blocchi

private IEnumerator UpdateChunks(){
    for (int i = 1; i < VIEW_RANGE; i += ChunkWidth) {
        float vr = i;
        for (float x = transform.position.x - vr; x < transform.position.x + vr; x += ChunkWidth) {
            for (float z = transform.position.z - vr; z < transform.position.z + vr; z += ChunkWidth) {

                _pos.Set(x, 0, z); // no y, yet
                _pos.x = Mathf.Floor(_pos.x/ChunkWidth)*ChunkWidth;
                _pos.z = Mathf.Floor(_pos.z/ChunkWidth)*ChunkWidth;

                Chunk chunk = Chunk.FindChunk(_pos);

                // If Chunk is already created, continue
                if (chunk != null)
                    continue;

                // Create a new Chunk..
                chunk = (Chunk) Instantiate(ChunkFab, _pos, Quaternion.identity);
            }
        }
        // Skip to next frame
        yield return 0;
    }
}

Risposte:


1

Cosa devi considerare caricando / creando un pezzo sopra e sotto la superficie in un dato stack quando il giocatore è in superficie, quindi l'algoritmo di generazione deve preoccuparsi di pile al livello più alto piuttosto che blocchi ... quando il giocatore è sotto terra uno sopra e sotto l'attuale livello del pezzo va bene. Per chiarire, una pila è una colonna verticale di pezzi da roccia fresca a stratosfera :)

Un altro modo di osservarlo sarebbe quello di dire se la superficie è al di sotto dell'attuale livello del giocatore - genera la superficie e una sotto, altrimenti genera il livello corrente e una sopra e sotto.

Supponiamo quindi che il tuo mondo sia alto 256 blocchi (* 16 = 4096 blocchi voxel) e in qualsiasi momento se uno stack si trova nel raggio di vista, avrai da 1 a 3 blocchi in quello stack effettivamente caricati e renderizzati.

I biomi presentano un ulteriore problema di fusione delle altezze ai bordi, ma è possibile gestirlo nel codice specifico del bioma che verrà chiamato per generare le caratteristiche di superficie e sottosuolo. Se si utilizza il rumore perlin / simplex per generare altezze, se un blocco confina con un blocco che è un bioma diverso, è possibile ottenere i valori di rumore generati da entrambi i tipi di bioma, quindi fare una media.


0

Quello che puoi fare è rendere un pezzo alto 256 in direzione y e dividerlo in 16 sezioni, ognuna alta 16 blocchi. Quindi si generano i dati per il blocco e si crea la geometria all'interno delle sezioni.

Un vantaggio sarebbe che hai accesso ai dati di un blocco completo, il che semplifica l'accesso ai dati sopra e sotto una sezione.

Ciò ha anche il vantaggio di essere in grado di eliminare facilmente molta geometria che non si trova all'interno del frustum visivo. Ci saranno anche molte sezioni che non hanno alcuna geometria.

Se non lo si utilizza già, il caricamento dei blocchi in un altro thread può anche offrire frame rate migliori, generando al contempo i dati per ciascun blocco.


1
Beh, forse mi sono spiegato un po 'vago. Ho già pianificato di separare un pezzo in 16 strati. Ma come faccio a decidere quali layer caricare? Ogni bioma ha la sua altezza minima / massima. Immagina Bioma A [100,20] Bioma B [160.200] - Sono ai margini di entrambi i biomi. Hanno una transizione graduale. Come decidere quali layer caricare? Ma mentre scrivo questo penso che devo solo controllare ogni livello (dall'alto verso il basso) e crearlo, quando il livello sopra è vuoto / trasparente - e fermarmi quando viene creato il primo livello. Spero che tu ottenga questo;) È fastidioso non poter aggiungere righe vuote nei commenti
Brettetete

3
Vedo il tuo punto. Ma se si implementa un algoritmo come avido meshing, si dovrebbe essere in grado di caricare tutti i layer senza enormi perdite di prestazioni. Puoi trovare un articolo sul meshing avido qui . Faccio lo stesso nel mio motore voxel e finora funziona bene.
user000user

Questa tecnica è semplicemente fantastica. Proverò ad implementarlo nel mio motore. Il codice dato è un po 'strano, condivideresti la tua implementazione? :)
Brettetete,

2
Ho incollato la mia implementazione C ++ qui e ho aggiunto alcuni commenti. Spero che ti aiuti :)
user000user

Fantastico, questo sembra essere molto utile. Grazie! Proverò a tradurre questo ora. Ma su # 67 - # 68 c'è un doppio && - ti sei perso / raddoppiato accidentalmente qualcosa da copiare? :) E potresti spiegare / pubblicare a breve i metodi SHIFT_ ? Inoltre non vedo alcuna dichiarazione / uso di tmp - Beh, Sory per tutte quelle domande
Brettetete

0

Non credo che tu possa farlo semplicemente caricando determinati livelli a causa del problema delle transizioni.

La mia inclinazione sarebbe quella di memorizzare alcuni metadati con ogni blocco:

1) Il blocco è completamente aereo? In tal caso non è necessario renderlo.

2) Per ogni faccia del blocco è opaco. Una faccia opaca significa che non è necessario considerare il prossimo pezzo. (Si noti, tuttavia, che a seconda di dove si trova il blocco potrebbero esserci fino a tre facce coinvolte - un blocco deve essere reso se uno dei tre non è opaco. Sospetto che sia meglio pre-calcolato - renderlo così a lungo o b1 è visibile e ha una faccia non opaca f1 oppure b2 è visibile ha una faccia non opaca f2 oppure b3 è visibile ha una faccia non opaca f3).

Sfortunatamente ci sono oltre 7000 blocchi nel tuo raggio visivo di 12 pezzi. Tuttavia, mi aspetto che in poche posizioni siano presenti più di tre blocchi verticali che in realtà devono essere renderizzati utilizzando questo approccio che riduce il conteggio dei blocchi a probabilmente non più di 1500.

Applicerei lo stesso tipo di logica all'interno di un blocco - quando carichi un blocco calcoli quali giunzioni sono trasparenti e quali giunzioni sono opache toccando opache - devi solo rendere effettivamente i volti dove qualcuno può vederli. (Nota che in Minecraft hai tre tipi di blocco: trasparente, opaco e che altera la visione: vetro, porte, recinzioni ecc. Puoi solo saltare trasparente-trasparente e opaco-opaco).

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.