Wie kann ich schwimmende Landmassen für eine Minecraft-ähnliche Maschine erzeugen?

19

Ich erstelle eine Minecraft-ähnliche Engine in XNA. Ich möchte schwimmende Inseln erstellen, die der in diesem Video gezeigten ähneln:

http://www.youtube.com/watch?v=gqHVOEPQK5g&feature=related

Wie würde ich dies mit einem Weltgenerator replizieren? Muss ich einen Perlin-Rauschalgorithmus verwenden? Ich weiß nicht, wie das mir helfen würde, solche Landmassen zu schaffen.

Hier ist der Code für den Perlin-Rauschgenerator, den ich verwende:

    private double[,] noiseValues;
    private float amplitude = 1;    // Max amplitude of the function
    private int frequency = 1;      // Frequency of the function

    /// <summary>
    /// Constructor
    /// </summary>
    /// 
    public PerlinNoise(int freq, float _amp)
    {
        Random rand = new Random(System.Environment.TickCount);
        noiseValues = new double[freq, freq];
        amplitude = _amp;
        frequency = freq;

        // Generate our noise values
        for (int i = 0; i < freq; i++)
        {
            for (int k = 0; k < freq; k++)
            {
                noiseValues[i, k] = rand.NextDouble();
            }
        }
    }

    /// <summary>
    /// Get the interpolated point from the noise graph using cosine interpolation
    /// </summary>
    /// <returns></returns>
    public double getInterpolatedPoint(int _xa, int _xb, int _ya, int _yb, double x, double y)
    {
        double i1 = interpolate(
            noiseValues[_xa % Frequency, _ya % frequency],
            noiseValues[_xb % Frequency, _ya % frequency]
            , x);

        double i2 = interpolate(
            noiseValues[_xa % Frequency, _yb % frequency],
            noiseValues[_xb % Frequency, _yb % frequency]
            , x);

        return interpolate(i1, i2, y);
    }

    public static double[,] SumNoiseFunctions(int width, int height, List<PerlinNoise> noiseFunctions)
    {
        double[,] summedValues = new double[width, height];

        // Sum each of the noise functions
        for (int i = 0; i < noiseFunctions.Count; i++)
        {
            double x_step = (float)width / (float)noiseFunctions[i].Frequency;
            double y_step = (float)height / (float)noiseFunctions[i].Frequency;

            for (int x = 0; x < width; x++)
            {
                for (int y = 0; y < height; y++)
                {
                    int a = (int)(x / x_step);
                    int b = a + 1;
                    int c = (int)(y / y_step);
                    int d = c + 1;

                    double intpl_val = noiseFunctions[i].getInterpolatedPoint(a, b, c, d, (x / x_step) - a, (y / y_step) - c);
                    summedValues[x, y] += intpl_val * noiseFunctions[i].Amplitude;
                }
            }
        }
        return summedValues;
    }

    /// <summary>
    /// Get the interpolated point from the noise graph using cosine interpolation
    /// </summary>
    /// <returns></returns>
    private double interpolate(double a, double b, double x)
    {
        double ft = x * Math.PI;
        double f = (1 - Math.Cos(ft)) * .5;

        // Returns a Y value between 0 and 1
        return a * (1 - f) + b * f;
    }

    public float Amplitude { get { return amplitude; } }
    public int Frequency { get { return frequency; } }

Aber die Sache ist, dass der Autor des Codes das Folgende verwendet, um Rauschen zu erzeugen, und ich verstehe es nicht im geringsten.

    private Block[, ,] GenerateLandmass()
    {
        Block[, ,] blocks = new Block[300, 400, 300];

        List<PerlinNoise> perlins = new List<PerlinNoise>();
        perlins.Add(new PerlinNoise(36, 29));
        perlins.Add(new PerlinNoise(4, 33));

        double[,] noisemap = PerlinNoise.SumNoiseFunctions(300, 300, perlins); 

        int centrey = 400 / 2;

        for (short x = 0; x < blocks.GetLength(0); x++)
        {
            for (short y = 0; y < blocks.GetLength(1); y++)
            {
                for (short z = 0; z < blocks.GetLength(2); z++)
                {
                    blocks[x, y, z] = new Block(BlockType.none);
                }
            }
        }

        for (short x = 0; x < blocks.GetLength(0); x++)
        {
            for (short z = 0; z < blocks.GetLength(2); z++)
            {
                blocks[x, centrey - (int)noisemap[x, z], z].BlockType = BlockType.stone; 
            }
        }

        //blocks = GrowLandmass(blocks);

        return blocks;
    }

Und hier ist die Seite, die ich benutze: http://lotsacode.wordpress.com/2010/02/24/perlin-noise-in-c/ .

Und ich versuche, Perlin-Rauschen wie von Martin Sojka beschrieben umzusetzen.

Ok, so weit bin ich gekommen:

Bildbeschreibung hier eingeben

Darestium
quelle

Antworten:

21

Erstellen Sie für das Basisland zwei kontinuierliche 2D-Rauschfelder (Perlin, Simplex, Wavelet, eine Kombination davon - was auch immer für Sie funktioniert), eines mit größtenteils niedriger Frequenz. Teile mit niedriger Amplitude für die obere Grenze des Landes, die andere mit sowohl Teilen mit hoher Frequenz und hoher Amplitude als auch Teilen mit niedriger Frequenz und hoher Amplitude für die untere Grenze des Landes. Wenn die Untergrenze über der Obergrenze liegt, schließen Sie keine Landvoxel ein (oder was auch immer Ihr Spiel verwenden wird, um das Gelände darzustellen). Das Endergebnis sieht ungefähr so ​​aus ...

Martin Sojka
quelle
Aber das ist für 2D, nicht wahr?
Darestium
Aber es
gefällt
4
2D / 3D - das Gleiche
Gavin Williams
OK, ich versuche es morgen umzusetzen ... Wünsch mir Glück;)
Darestium
@Darestium: Dies ist ein 2D-Beispiel zur einfacheren Visualisierung. Die gleiche Methode funktioniert für eine beliebige Anzahl von (algebraischen) Dimensionen, die größer als eine sind.
Martin Sojka
15

Wäre so etwas genug?

Bildbeschreibung hier eingeben

Wenn ja, überprüfen Sie diesen Artikel . Zitieren der wichtigsten Teile:

Um interessanteres Rauschen zu erzielen, können mehrere Oktaven Simplex-Rauschen addiert werden. [...] Da ich eine Art ungefähr kugelförmiges schwimmendes Gestein haben möchte, muss ich das Rauschen mit seinem Abstand vom Zentrum multiplizieren. [...] Ich möchte auch, dass das Gestein oben flacher als unten ist, daher ist ein zweiter Multiplikationsfaktor ein Gradient in y-Richtung. Indem wir diese kombinieren und y für Rauschen dehnen, während wir x und z ein bisschen komprimieren, erhalten wir so etwas wie einen schwebenden Stein. [...] Das Ausgraben von Höhlen mit ein wenig Lärmschutz macht es auch interessanter.

  • Sie beginnen also im Grunde genommen mit einem Datensatz, der aus Simplex- oder Perlin- Rauschen generiert wurde (oder vielmehr mehreren Oktaven von Rauschen, die zusammenaddiert werden ) .
  • Formen Sie es dann zu etwas, das einer schwimmenden Landmasse näher kommt, indem Sie mehr daraus machen sphärischer machen (indem Sie das Rauschen mit seinem multiplizieren) Abstand von der Mitte ).
  • Und schaffen Sie Boden, indem Sie ihn machen flacher machen in der Nähe der Oberseite (indem Sie ihn mit einem vertikalen Gradienten multiplizieren, dh mit niedrigen Werten oben beginnen und nach unten hin höher werden).
  • Kombinieren Sie diese drei und passen Sie die Form durch Skalierung an das Rauschen entlang der X / Y / Z-Achse (im Artikel wird empfohlen , auf der Y-Achse zu dehnen und auf der X- und Z-Achse zu komprimieren ).
  • Ein zusätzlicher Lärmpass kann zum Ausheben von Höhlen verwendet werden .
David Gouveia
quelle
Ja, ich denke so etwas ist trotzig was ich will. Die Sache ist, dass ich wenig Erfahrung mit Perlin-Rauschen habe, so dass das einzige, was ich erzeugen kann, wirklich einfache Berge sind und keine Idee haben würde, wie man "mehrere Oktaven Rauschen zusammenfügen kann". Für die Perlin-Rauscherzeugung verwende ich den Code dass ich von stackoverflow.com/questions/4753055/… ausgestiegen bin und es nach C # portiert habe. Ich füge meine Version in den ursprünglichen Beitrag ein ... Wären Sie bereit, mir ein Beispiel zu geben, wie ich damit eine solche Landmasse erreichen würde Code?
Darestium
2
Deshalb habe ich den Artikel verlinkt. Es enthält eine Erklärung aller Schritte und den Quellcode am Ende. Sie sollten versuchen, das zu studieren.
David Gouveia
4
  1. Entscheiden Sie anhand Ihres vorhandenen 3D-Gitters, auf welcher Höhe sich die Inseloberseiten befinden sollen. Erstellen Sie eine Reihe von Inseln in dieser 2D-Ebene (nennen wir sie die XY-Ebene), indem Sie Punkte durch die Ebene streuen und dann Würfel an diesen Punkten platzieren. Verwenden Sie den Zusammenhalt, um sie in Klumpen enger zusammenzuziehen. Füllen Sie alle Löcher aus und Sie haben eine Reihe von Inselspitzen.
  2. Verwenden Sie eine Zertifizierungsstelle-ähnliche Methode, um die Inseln nach unten zu wachsen. (a) Bestimmen Sie ausgehend von der Z-Ebene, in der Sie Ihre Anfangspunkte aufgezeichnet haben, für jede Zelle in dieser aktuellen Z-Ebene die Chance, sich angesichts der Anzahl der Nachbarn in der XY-Ebene von 0 bis 8 bis zur nächstniedrigeren Ebene zu erstrecken ( diagonale Nachbarn sind eingeschlossen), z. B. weisen Sie jedem Nachbarn eine Chance von 10% zu, bis zu maximal 80%. Berechnen Sie dies für jede Zelle in der Startebene. (b) Dann randomisieren Sie gegen diese Chance und erweitern Sie nach unten, wenn Sie sich innerhalb des prozentualen Bereichs befinden. Spülen, Schritt 2 wiederholen (zum nächsten Level gehen, Nachbarn für jedes Voxel bestimmen, für dieses Voxel nach unten strecken), bis keine Verlängerungen mehr auftreten. Ihre nach unten gerichtete Erweiterung sollte aufgrund der Anzahl der Nachbarn einen Kegel bilden, da diese Voxel in Richtung des XY-Zentrums der Insel in der Regel mehr Nachbarn haben.

Pseudocode für Schritt 2:

int planeNeighbours[x][y]; //stores how many neighbours each voxel in this plane has

for each z level (starting at level where you plotted your points)
    for each x, y voxel in z level
        for each neighbour space bordering this voxel
            if neighbour exists
                ++planeNeighbours[x][y];
    for each x, y voxel in z level
        chance = random(0,8); //depends on your RNG implementation
        if chance < planeNeighbours[x][y]
            worldGrid[x][y][z+1] = new cube

Sobald die Generierung Ihrer Inseln abgeschlossen ist, können Sie sie optional im Raum nach oben und unten verschieben, um sie auf verschiedenen Höhen zu haben.

Ingenieur
quelle
OK, ich hatte einen Riss in Ihrer Methode und es scheint, als würde das Gelände nach außen anstatt nach innen wachsen. Ich
poste