Warum kann ich mein prozedurales Terrain nicht zusammenfügen?


7

Ich habe herausgefunden, wie man einen Algorithmus zur Verschiebung des Mittelpunkts implementiert, um eine Karte für mein Spiel zu erstellen. Ich wollte eine unendlich große Welt erschaffen, also habe ich versucht, zwei Karten zusammenzufügen, aber sie sahen nicht sehr nahtlos aus. Ich erinnerte mich an den Diamond-Square-Algorithmus ...

Auf der Wikipedia-Seite für den Diamond-Square- Algorithmus heißt es:

Bei Verwendung mit konsistenten Anfangseckenwerten können mit dieser Methode auch generierte Fraktale ohne Unterbrechungen zusammengefügt werden

Nachdem ich gelesen hatte, dass ich mich entschlossen hatte, meine Mittelpunktverschiebungsmethode in eine meiner Meinung nach richtige Diamant-Quadrat-Methode umzuwandeln . Es sieht jedoch immer noch nicht nahtlos aus, obwohl ich "konsistente anfängliche Eckwerte" verwende.
Die Methode funktioniert ansonsten einwandfrei.
Hier sind zwei generierte Karten nebeneinander:Zwei generierte Karten nebeneinander

Wie Sie sehen können, gibt es große Diskontinuitäten (obwohl es so aussieht, als könnten sie fast zusammenpassen). Was ich geschrieben habe, darf keine "echte" Diamant-Quadrat-Methode sein, sonst verstehe ich den Wikipedia-Artikel falsch.

Mit anderen Worten, meine Frage lautet: Was ist falsch an meinem Code oder meinem Verständnis, das mich daran hindert, Karten zusammenzufügen?
Vielen Dank!

public static float[][] generateMap(int power, float topLeft, float topRight, float bottomLeft, float bottomRight, float error, float persistence, boolean normalize, long seed){
Random random = new Random(seed);

int size = (int)Math.pow(2, power) + 1;

float[][] data = new float[size][size];

// these are for the normalization at the end.
// does it even do anything?
float min = MathHelper.min(topLeft, topRight, bottomLeft, bottomRight);
float max = MathHelper.max(topLeft, topRight, bottomLeft, bottomRight);

// set the corners to the initial values.
data[0][0] = topLeft;
data[size-1][size-1] = bottomRight;
data[0][size-1] = topRight;
data[size-1][0] = bottomLeft;

for (int i = 0; i < power; i++){

    int square = size / (int)Math.pow(2, i);
    int half = square / 2;

    for (int x = 0; x < size - square; x += square){
        for (int y = 0; y < size - square; y += square){

            // find the values of the corners of the square.
            float tl = data[y][x];
            float bl = data[y + square][x];
            float tr = data[y][x + square];
            float br = data[y + square][x + square];

            // find the values of the corners of the diamond (if they exist).
            Float xt = (y - square - half >= 0) ? data[y-square-half][x+half] : null;
            Float xb = (y + square + half < size) ? data[y+square+half][x+half] : null;
            Float xl = (x - square - half >= 0) ? data[y+half][x-square-half] : null;
            Float xr = (x + square + half < size) ? data[y+half][x+square+half] : null;

            // set the square's center to the average of the square's corners plus a random error.
            float centerVal = (tl + bl + tr + br) / 4.0f;
            centerVal += ((random.nextFloat() * 2) - 1) * error;
            data[y+half][x+half] = centerVal;

            // set the diamonds' centers to the average of the diamonds' corners (that exist) plus a random error.
            float leftVal = (tl + bl + centerVal + (xl != null ? xl : 0)) / (3.0f + (xl != null ? 1.0f : 0.0f));
            leftVal += ((random.nextFloat() * 2) - 1) * error;
            data[y+half][x] = leftVal;

            float rightVal = (tr + br + centerVal + (xr != null ? xr : 0)) / (3.0f + (xr != null ? 1.0f : 0.0f));
            rightVal += ((random.nextFloat() * 2) - 1) * error;
            data[y+half][x+square] = rightVal;

            float topVal = (tl + tr + centerVal + (xt != null ? xt : 0)) / (3.0f + (xt != null ? 1.0f : 0.0f));
            topVal += ((random.nextFloat() * 2) - 1) * error;
            data[y][x+half] = topVal;

            float bottomVal = (bl + br + centerVal + (xb != null ? xb : 0)) / (3.0f + (xb != null ? 1.0f : 0.0f));
            bottomVal += ((random.nextFloat() * 2) - 1) * error;
            data[y+square][x+half] = bottomVal;

            max = MathHelper.max(max, centerVal, leftVal, rightVal, topVal, bottomVal);
            min = MathHelper.min(min, centerVal, leftVal, rightVal, topVal, bottomVal);

        }
    }

    // reduce random error.
    error *= persistence;
}

// does this even do anything?
if (normalize) {
    float div = max - min;
    for (int i = 0; i < size; i++)
        for (int j = 0; j < size; j++)
            data[i][j] /= div;
}

return data;

}}


3
Wenn Sie bereit sind, die Algorithmen erneut zu ändern, tritt bei der Abtastung mit einer Rauschfunktion wie Perlin Noise dieses Problem nicht auf.
Chaosed0

@ Chaosed0 Ganz zu schweigen davon, dass es im Allgemeinen besser aussieht und Ihnen mehr Kontrolle über das Ergebnis gibt, indem Sie die relativen Beiträge mehrerer Frequenzen gewichten.
bcrist

Antworten:


6

Bevor Diamond-Square beginnt, müssen Sie sicherstellen, dass die äußersten Grenzen (und die maximale Anzahl der darin erzeugten potenziellen Mittelpunkte) auf beiden Seiten der Karte (in x und y) gleich sind. Nur dann können Sie mit der vollständigen Generierung des Zentrums beginnen, wenn sich etwas näherteine nahtlose Verpackung. Was sie mit "konsistent" meinten, ist "alle äußersten Ecken und Kanten benötigen dieselben Werte", da sie beim Umwickeln im Wesentlichen alle denselben Punkt (Ursprung) haben. Setzen Sie die Anfangsecken auf Null, bearbeiten Sie dann die linke Kante mit der 1D-Mittelpunktverschiebung, kopieren Sie diese an die rechte Kante und machen Sie dasselbe mit oben und unten (am Ende müssen die Ecken immer noch alle Null sein, aber die dazwischen können alles sein) . Alternativ können Sie einfach alle Grenzpunkte auf Null setzen. Jetzt haben Sie den Karten- "Rahmen". Wenn Sie also Diamond-Square ausführen, berühren Sie keine Grenzpunkte - verwenden Sie sie einfach.

ich sage Annäherung, weil dies wohlgemerkt immer noch nicht zu einem wünschenswerten Ergebnis führt (abhängig von Ihrer Sichtweise), da die Operation des Diamond-Square-Algorithmus standardmäßig nicht das Konzept der Kontinuität über Wrap-Grenzen hinweg behandelt. Es sieht also etwas erfunden aus, IIRC. Wenn dies ein Problem ist, finden Sie entweder eine Möglichkeit, eine Art Glättung über Grenzen hinweg durchzuführen, indem Sie möglicherweise zufällige lokale Maxima / Minima auf beiden Seiten der Grenze auswählen und diese über die Grenze erweitern, oder schauen Sie sich andere Algorithmen für Ihre Geländegenerierung an. .. oder verlieren die Wrap-Anforderung.


1

Anstatt zu versuchen, zwei Karten (z. B. zwei 64x64-Karten) zusammenzufügen, erstelle ich in meinem eigenen Code (nur um die prozedurale Geländegenerierung zu lernen) eine größere Karte (z. B. 128x128, die umschließt) und werfe dann die weg unterer Teil. Dadurch habe ich eine 64x128-Karte, die horizontal, aber nicht vertikal umbrochen wird (was ich möchte).


1

Ich habe darüber nachgedacht, dh passende Höhenkarten mit Diamantquadrat zu erstellen. Ich weiß nicht, wie gut dies funktionieren wird, da ich noch nie versucht habe, es zu implementieren, aber hier ist meine theoretische Postulation:

Wenn für jeden Punkt entlang einer Kante der benachbarte Kartenabschnitt vorhanden ist, machen Sie den Punkt gleich dem benachbarten Kantenpunkt.

Im Moment sieht es so aus, als würden Sie die Ecken nur auf die Ecken der angrenzenden Karte setzen, aber nicht auf die anderen Punkte folgen. Da jeder zweite Punkt zufällig ist, kann nicht garantiert werden, dass der Rest der Kante ausgerichtet ist. Aber wenn Sie eine zusätzliche Prüfung einfügen, die besagt: "Bin ich eine Kante? Ok, existiert eine benachbarte Kante auf einem anderen Kartenabschnitt? Ok, ich bin jetzt dieser Wert anstelle dieses zufälligen", sollten Sie in der Lage sein um es genau auszurichten.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.