Bagaimana saya bisa menghasilkan massa daratan terapung untuk mesin seperti Minecraft?

19

Saya membuat mesin mirip Minecraft di XNA. Yang ingin saya lakukan adalah membuat pulau terapung yang serupa dengan yang ditunjukkan dalam video ini:

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

Bagaimana saya mereplikasi ini menggunakan generator dunia? Apakah saya harus menggunakan beberapa algoritma noise Perlin? Saya tidak tahu bagaimana itu akan membantu saya membuat daratan seperti itu.

Berikut adalah kode untuk generator kebisingan perlin yang saya gunakan:

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

Tapi masalahnya adalah pembuat kode menggunakan yang berikut untuk menghasilkan suara, dan saya tidak memahaminya sama sekali.

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

Dan di sini adalah situs yang saya gunakan: http://lotsacode.wordpress.com/2010/02/24/perlin-noise-in-c/ .

Dan saya mencoba menerapkan perlin noise dengan cara yang ditentukan oleh Martin Sojka.

Ok, jadi ini yang saya dapatkan sejauh ini:

masukkan deskripsi gambar di sini

Darestium
sumber

Jawaban:

21

Untuk tanah dasar, buat dua bidang noise kontinu 2D (Perlin, Simplex, Wavelet, kombinasi keduanya - apa pun yang sesuai untuk Anda), satu dengan frekuensi yang sebagian besar rendah. bagian amplitudo rendah untuk batas atas tanah, yang lain dengan frekuensi tinggi, bagian amplitudo tinggi dan frekuensi rendah, amplitudo tinggi untuk batas bawah tanah. Jika batas bawah di atas batas lebih tinggi, jangan sertakan voxels darat apa pun (atau apa pun yang akan digunakan gim Anda untuk mewakili medan). Hasil akhirnya terlihat seperti ini ...

Martin Sojka
sumber
Tapi ini untuk 2D bukan?
Darestium
Tapi saya sangat suka :)
Darestium
4
2D / 3D - hal yang sama
Gavin Williams
OK, upaya buruk untuk mengimplementasikannya besok ... Doakan saya beruntung;)
Darestium
@Darestium: Ini adalah contoh 2D untuk visualisasi yang lebih mudah. Metode yang sama berfungsi untuk sejumlah dimensi (aljabar) yang lebih tinggi dari satu.
Martin Sojka
15

Apakah sesuatu seperti ini sudah cukup?

masukkan deskripsi gambar di sini

Jika demikian, periksa artikel ini . Mengutip bagian yang paling relevan:

Untuk mendapatkan noise yang lebih menarik, beberapa oktaf dari noise simplex dapat ditambahkan bersamaan. [...] Karena saya ingin mendapatkan batu apung yang berbentuk bulat, saya perlu melipatgandakan kebisingan dengan jaraknya dari pusat. [...] Saya juga ingin batu itu lebih rata di atas daripada di bawah, maka faktor penggandaan kedua adalah gradien dalam arah y. Menggabungkan ini bersama-sama dan meregangkan y untuk kebisingan sambil menekan x dan za bit, kita mendapatkan sesuatu seperti batu mengambang. [...] Menggali beberapa gua dengan sedikit noise offset juga membuatnya lebih menarik.

  • Jadi pada dasarnya Anda akan mulai dengan set data yang dihasilkan dari simplex atau perlin noise (atau lebih tepatnya beberapa oktaf suara yang ditambahkan bersamaan ).
  • Kemudian bentuk itu menjadi sesuatu yang lebih dekat ke daratan mengambang dengan membuatnya lebih bulat (dengan mengalikan suara dengan jaraknya dari pusat ).
  • Dan buat tanah dengan membuatnya lebih datar di dekat bagian atas (dengan mengalikannya dengan gradien vertikal yaitu dimulai dengan nilai-nilai rendah di atas dan semakin tinggi ke arah bawah).
  • Gabungkan ketiganya dan sesuaikan bentuk dengan menskalakan noise di sepanjang sumbu X / Y / Z (artikel ini menyarankan peregangan pada sumbu Y dan mengompresi pada sumbu X dan Z ).
  • Lewat kebisingan tambahan dapat digunakan untuk menggali gua .
David Gouveia
sumber
Ya, saya pikir sesuatu seperti ini adalah yang saya inginkan. Masalahnya adalah bahwa saya memiliki sedikit pengalaman dengan kebisingan perlin sehingga satu-satunya hal yang dapat saya hasilkan adalah pegunungan yang benar-benar dasar dan tidak akan memiliki ide tentang cara menambahkan "beberapa oktaf kebisingan ke togther). Untuk generasi kebisingan perlin saya menggunakan kode bahwa saya keluar dari stackoverflow.com/questions/4753055/… dan porting ke C #. Saya akan menambahkan versi saya di posting asli ... Apakah Anda bersedia memberi saya contoh tentang bagaimana saya akan mencapai daratan dengan itu? kode?
Darestium
2
Itu sebabnya saya menautkan artikel itu. Ini memiliki penjelasan tentang semua langkah, dan kode sumber di akhir. Anda harus mencoba mempelajarinya.
David Gouveia
4
  1. Dengan menggunakan kisi 3D yang ada, tentukan ketinggian di mana Anda menginginkan puncak pulau. Buat satu set pulau di bidang 2D itu (sebut saja pesawat XY) dengan menyebarkan titik melalui pesawat, lalu menempatkan kubus di titik-titik itu. Gunakan kohesi untuk menarik mereka lebih dekat menjadi rumpun. Isi setiap lubang dan Anda memiliki satu set pulau-puncak.
  2. Gunakan CA- Metode serupa untuk menumbuhkan pulau ke bawah. (a) Mulai pada tingkat Z di mana Anda merencanakan titik awal Anda, untuk setiap sel dalam tingkat Z saat ini, tentukan peluang untuk memperluas ke tingkat lebih rendah berikutnya mengingat jumlah tetangga di bidang XY, dari 0 hingga 8 ( tetangga diagonal disertakan), mis. tetapkan peluang 10% untuk setiap tetangga, hingga maksimum peluang 80%. Hitung ini untuk setiap sel di bidang awal. (B) Kemudian secara acak terhadap peluang ini dan memperluas ke bawah jika Anda berada dalam kisaran persentase. Bilas, ulangi langkah 2 (lanjutkan ke tingkat berikutnya, tentukan tetangga untuk setiap voxel, perpanjang ke bawah untuk voxel itu) sampai tidak ada lagi ekstensi yang muncul. Perpanjangan ke bawah Anda harus membentuk kerucut karena pendekatan jumlah tetangga, karena voxels menuju pusat XY pulau biasanya akan memiliki lebih banyak tetangga.

Kodesemu untuk langkah 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

Setelah pulau-pulau Anda selesai menghasilkan, Anda dapat menggesernya secara opsional ke atas dan ke bawah dalam ruang untuk memilikinya pada ketinggian yang berbeda.

Insinyur
sumber
OK, saya punya celah pada metode Anda dan tampaknya menumbuhkan medan bukan ke dalam. Saya akan memposting kode ...
Darestium