Wie lade ich Stapelstücke im laufenden Betrieb?

8

Ich arbeite derzeit an einer unendlichen Welt, die hauptsächlich von Minecraft inspiriert ist.
Ein Chunk besteht aus 16x16x16 Blöcken. Ein Block (Würfel) ist 1x1x1.

Dies läuft sehr reibungslos mit einem ViewRange von 12 Chunks (12x16) auf meinem Computer. Fein.
Wenn ich die Chunk-Höhe auf 256 ändere, wird dies offensichtlich zu einer unglaublichen Verzögerung.

Grundsätzlich möchte ich also Brocken stapeln. Das heißt, meine Welt könnte [∞, 16, ∞] Chunks groß sein.

Die Frage ist nun, wie man Brocken im laufenden Betrieb erzeugt.
Im Moment generiere ich nicht vorhandene Brocken, die kreisförmig um meine Position liegen (nah bis fern). Da ich noch keine Chunks staple, ist dies nicht sehr komplex.

Als wichtige Randnotiz hier: Ich möchte auch Biome mit unterschiedlicher Min / Max-Höhe haben. In Biome Flatlands wäre die höchste Schicht mit Blöcken 8 (8x16) - in Biome Mountains wäre die höchste Schicht mit Blöcken 14 (14x16). Nur als Beispiel.

Was ich tun könnte, wäre zum Beispiel 1 Stück über und unter mir zu laden.
Aber hier wäre das Problem , dass Übergänge zwischen verschiedenen Biomen größer als ein Stück auf y sein könnten.




Übergänge zwischen Biomes




Mein aktueller Chunk wird in Aktion geladen

Beispiel für das Laden von Chunks



Der Vollständigkeit halber hier mein aktueller Chunk-Lade- "Algorithmus"

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;
    }
}
Brettetete
quelle

Antworten:

1

Was Sie beachten müssen, um einen Block über und unter der Oberfläche in einem bestimmten Stapel zu laden / zu erstellen, wenn sich der Player auf der Oberfläche befindet. Daher muss sich Ihr Generierungsalgorithmus eher um Stapel auf der obersten Ebene als um Blöcke kümmern ... wenn sich der Spieler befindet Unterirdisch ist einer über und unter dem aktuellen Chunk-Level in Ordnung. Zur Verdeutlichung ist ein Stapel eine vertikale Säule von Brocken vom Grundgestein bis zur Stratosphäre :)

Eine andere Sichtweise wäre zu sagen, ob sich die Oberfläche unter dem aktuellen Chunk-Level des Spielers befindet - generieren Sie die Oberfläche und eine darunter, andernfalls generieren Sie die aktuelle Ebene und eine darüber und darunter.

Nehmen wir also an, Ihre Welt wird 256 Blöcke hoch sein (* 16 = 4096 Voxelblöcke), und wenn sich ein Stapel innerhalb des Sichtbereichs befindet, haben Sie jederzeit 1 bis 3 Blöcke in diesem Stapel, die tatsächlich geladen und gerendert werden.

Biomes führen zu einem zusätzlichen Problem beim Mischen von Höhen an den Rändern. Sie können dies jedoch im biomspezifischen Code behandeln, der aufgerufen wird, um die Oberflächen- und Untergrundmerkmale zu generieren. Wenn Sie Perlin- / Simplex-Rauschen verwenden, um Höhen zu erzeugen, und wenn ein Block an einen Block grenzt, der ein anderes Biom ist, können Sie die Rauschwerte erhalten, die beide Biomtypen erzeugen würden, und diese dann mitteln.

Aufstieg
quelle
0

Was Sie tun können, ist, einen Block 256 in y-Richtung hoch zu machen und ihn in 16 Abschnitte mit jeweils 16 Blöcken Höhe zu unterteilen. Anschließend generieren Sie die Daten für den Block und erstellen die Geometrie innerhalb der Abschnitte.

Ein Vorteil wäre, dass Sie Zugriff auf die Daten eines vollständigen Blocks haben, was den Zugriff auf die Daten über und unter einem Abschnitt erleichtert.

Dies hat auch den Vorteil, dass viele Geometrien, die nicht im Betrachtungsstumpf liegen, leicht entfernt werden können. Es wird auch viele Abschnitte geben, die überhaupt keine Geometrie haben.

Wenn Sie es nicht bereits verwenden, können Sie durch Laden der Chunks in einen anderen Thread auch bessere Frameraten erzielen, während Sie die Daten für jeden Chunk generieren.

user000user
quelle
1
Nun, vielleicht habe ich mich ein bisschen vage erklärt. Ich habe bereits geplant, einen Teil in 16 Schichten aufzuteilen. Aber wie entscheide ich, welche Ebenen geladen werden sollen? Jedes Biom hat seine eigene minimale / maximale Höhe. Stellen Sie sich Biom A [100,20] Biom B [160,200] vor - ich bin am Rande beider Biome. Sie haben einen reibungslosen Übergang. Wie entscheide ich, welche Ebenen geladen werden sollen? Aber beim Schreiben denke ich, ich muss nur jede Ebene (von oben nach unten) überprüfen und erstellen, wenn die darüber liegende Ebene leer / transparent ist - und aufhören, wenn die erste Ebene erstellt wird. Hoffe du bekommst das;) Es ist ärgerlich, dass du keine leeren Zeilen in Kommentare
einfügen kannst
3
Ich weiß, worauf du hinauswillst. Wenn Sie jedoch einen Algorithmus wie Greedy Meshing implementieren, sollten Sie in der Lage sein, alle Ebenen ohne großen Leistungsverlust zu laden. Einen Artikel über gieriges Ineinandergreifen finden Sie hier . Ich mache das gleiche in meinem Voxel-Motor und es funktioniert bisher gut.
user000user
Diese Technik ist einfach unglaublich. Ich werde versuchen, dies in meine Engine zu implementieren. Der angegebene Code ist etwas seltsam. Würden Sie Ihre Implementierung teilen? :)
Brettetete
2
Ich habe meine C ++ - Implementierung hier eingefügt und einige Kommentare hinzugefügt. Hoffe das hilft :)
user000user
Genial, das scheint sehr hilfreich zu sein. Vielen Dank! Ich werde jetzt versuchen, dies zu übersetzen. Aber auf # 67 - # 68 gibt es ein doppeltes && - haben Sie versehentlich etwas zum Kopieren verpasst / verdoppelt? :) Und könntest du kurz die SHIFT_ Methoden erklären / posten ? Auch ich sehe keine Erklärung / Verwendung von tmp - Nun, sory für all diese Fragen
Brettetete
0

Ich glaube nicht, dass Sie dies tun können, indem Sie aufgrund des Problems der Übergänge einfach bestimmte Ebenen laden.

Meine Neigung wäre es, einige Metadaten mit jedem Block zu speichern:

1) Ist der Block vollständig luftig? Wenn ja, müssen Sie es nicht rendern.

2) Für jede Seite des Blocks ist es undurchsichtig. Ein undurchsichtiges Gesicht bedeutet, dass Sie den nächsten Block nicht berücksichtigen müssen. (Beachten Sie jedoch, dass je nachdem, wo sich der Block befindet, bis zu drei Gesichter betroffen sein können - ein Block muss gerendert werden, wenn eines der drei nicht undurchsichtig ist. Ich vermute, dass dies am besten vorberechnet ist - so lange rendern entweder b1 ist sichtbar und hat eine nicht undurchsichtige Fläche f1 oder b2 ist sichtbar hat eine nicht undurchsichtige Fläche f2 oder b3 ist sichtbar hat eine nicht undurchsichtige Fläche f3.)

Es gibt leider über 7000 Chunks in Ihrem Sichtbereich von 12 Chunks. Ich würde jedoch erwarten, dass nur wenige Standorte mehr als drei vertikale Chunks haben, die tatsächlich mit diesem Ansatz gerendert werden müssen, wodurch die Chunk-Anzahl auf wahrscheinlich nicht mehr als 1500 reduziert wird.

Ich würde dieselbe Art von Logik innerhalb eines Blocks anwenden - wenn Sie einen Block laden, berechnen Sie, welche Übergänge transparent und welche Übergänge undurchsichtig sind und sich undurchsichtig berühren - müssen Sie nur Gesichter rendern, bei denen jemand sie sehen kann. (Beachten Sie, dass Sie in Minecraft drei Arten von Blöcken haben - transparent, undurchsichtig und das Sehvermögen verändernd - Glas, Türen, Zäune usw. Sie können nur transparent-transparent und undurchsichtig-undurchsichtig überspringen.)

Loren Pechtel
quelle