Minecraft benzeri bir motor için nasıl yüzen kara kütleleri oluşturabilirim?


19

XNA'da Minecraft benzeri bir motor oluşturuyorum. Ne yapmak istiyorum bu videoda gösterilene benzer yüzen adalar oluşturmaktır:

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

Bunu bir dünya üreticisi kullanarak nasıl çoğaltırım? Bazı Perlin gürültü algoritması kullanmam gerekir mi? Bunun böyle kara kütleleri yapmama nasıl yardımcı olacağını bilmiyorum.

İşte kullandığım perlin gürültü üreteci kodu:

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

Ama şey kod yazarı gürültü üretmek için aşağıdakileri kullanır ve en azından anlamıyorum.

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

Ve kullandığım site: http://lotsacode.wordpress.com/2010/02/24/perlin-noise-in-c/ .

Ve Perlin gürültüsünü Martin Sojka'nın belirlediği şekilde uygulamaya çalışıyorum.

Tamam, şimdiye kadar aldığım şey bu:

resim açıklamasını buraya girin

Yanıtlar:


21

Üs arazisi için, çoğunlukla düşük frekanslı olmak üzere iki 2D sürekli gürültü alanı (Perlin, Simplex, Wavelet, bunların bir kombinasyonu - sizin için ne olursa olsun) yapın. arazinin üst sınırı için düşük genlikli kısımlar, diğeri yüksek frekanslı, yüksek genlikli kısımlar ve düşük frekanslı, arazinin alt sınırı için yüksek genlikli. Alt sınırın üst sınırın üstünde olduğu durumlarda, herhangi bir arazi vokselini (veya oyununuzu araziyi temsil etmek için ne kullanırsa kullanmayın) dahil etmeyin. Sonuç kabaca şöyle ...


Ama bu 2D için değil mi?
Darestium

Ama oldukça :) böyle
Darestium

4
2D / 3D - aynı şey
Gavin Williams

Tamam, yarın uygulamaya çalışacağım ... Bana şans diliyorum;)
Darestium

@Darestium: Daha kolay görüntüleme için 2B bir örnektir. Aynı yöntem, birden fazla sayıda (cebirsel) boyut için de geçerlidir.
Martin Sojka

15

Bunun gibi bir şey yeterli olur mu?

resim açıklamasını buraya girin

Öyleyse, bu makaleye bakın . En alakalı kısımlardan alıntı:

Daha ilginç bir gürültü elde etmek için, birlikte simpleks gürültünün birden fazla oktavı eklenebilir. [...] Kabaca küresel bir yüzen kaya türü elde etmek istediğim için, gürültüyü merkezden uzaklığıyla çarpmam gerekiyor. [...] Ayrıca kayanın altta olduğundan daha düz olmasını istiyorum, bu nedenle ikinci bir çarpma faktörü y yönünde bir eğimdir. Bunları bir araya getirerek x ve za bitini sıkıştırırken gürültü için y'yi uzatarak yüzen bir kaya gibi bir şey elde ederiz. [...] Biraz ofset gürültü ile mağara kazmak da daha ilginç hale getirir.

  • Temel olarak, tek yönlü veya perlin gürültüsünden (veya birbirine eklenen birden fazla oktav gürültüsü ) oluşturulan bir veri kümesiyle başlayacaksınız .
  • Daha sonra onu daha küresel hale getirerek (gürültüyü merkezden uzaklığıyla çarparak) yüzen bir kara kütlesine daha yakın bir şey haline getirin .
  • Ve üste yakın düzleştirerek zemin oluşturun ( dikey bir gradyanla çarparak, yani üstte düşük değerlerle başlayıp aşağı doğru yükselerek).
  • Bu üçünü birleştirin ve X / Y / Z eksenleri boyunca gürültüyü ölçekleyerek şekli ayarlayın (makale Y eksenine gerilmeyi ve X ve Z eksenlerine sıkıştırmayı önermektedir ).
  • Mağaraları kazmak için ek bir gürültü geçişi kullanılabilir .

Evet, bence böyle bir şey meydan okurcasına istediğim şey. Şey, perlin gürültü ile çok az deneyimim var, bu yüzden üretebileceğim tek şey gerçekten temel dağlar ve "gürültü birlikte birden fazla oktav eklemek" hakkında herhangi bir fikir olmaz. Perlin gürültü üretimi için kod kullanıyorum Ben indi o stackoverflow.com/questions/4753055/... ve C # için taşıdık. Ill orijinal sonrası benim versiyonunu ekleyin ... misiniz bununla böyle bir kara parçası elde nasıl bana bir örnek vermeye istekli olmak kod?
Darestium

2
Bu yüzden makaleyi bağladım. Sonunda tüm adımların ve kaynak kodunun bir açıklaması vardır. Bunu incelemeye çalışmalısın.
David Gouveia

4
  1. Mevcut 3B ızgarayı kullanarak, ada üstlerinin olmasını istediğiniz yüksekliğe karar verin. Bu 2B düzlemde noktaları düzlemden saçarak ve ardından bu noktalara küpler yerleştirerek bir dizi ada oluşturun (buna XY düzlemi diyelim). Bunları kümeler halinde birbirine yaklaştırmak için uyumu kullanın. Herhangi bir deliği doldurun ve bir dizi ada tepeniz var.
  2. Bir kullan CA- adaları aşağı doğru büyütmek için benzer bir yöntem. (a) Başlangıç ​​noktalarınızı çizdiğiniz Z seviyesinde başlayarak, mevcut Z seviyesindeki her hücre için, XY düzlemindeki komşuların sayısı göz önüne alındığında, 0'dan 8'e (bir sonraki seviyeye) inme şansını belirleyin ( diyagonal komşular dahildir), örn. her komşu için% 10, en fazla% 80 şans atar. Bunu başlangıç ​​düzlemindeki her hücre için hesaplayın. (b) Sonra bu şansa karşı rasgele seçim yapın ve yüzde aralığındaysanız aşağı doğru genişletin. Durulayın, adım 2'yi tekrarlayın (bir sonraki seviyeye geçin, her voksel için komşuları belirleyin, o voksel için aşağı doğru uzatın), daha fazla uzama meydana gelmeyene kadar. Aşağı doğru uzantınız, komşu sayısı yaklaşımı nedeniyle bir koni oluşturmalıdır, çünkü adanın XY merkezine doğru olan voksellerin tipik olarak daha fazla komşusu olacaktır.

2. adım için sözde kod:

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

Adalarınız oluşturulduktan sonra, isteğe bağlı olarak farklı yüksekliklerde onları yukarı ve aşağı kaydırabilirsiniz.


Tamam, metodunda bir çatlak vardı ve araziyi içe doğru değil dışarıya doğru büyütüyor gibi görünüyor. Kodu
göndereceğim
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.