ฉันจะสร้างฝูงบกลอยตัวสำหรับเครื่องยนต์ที่เหมือน Minecraft ได้อย่างไร?


19

ฉันกำลังสร้างเอนจินคล้าย Minecraft ใน XNA สิ่งที่ฉันต้องการทำคือการสร้างเกาะลอยคล้ายกับที่แสดงในวิดีโอนี้:

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

ฉันจะทำซ้ำสิ่งนี้โดยใช้เครื่องกำเนิดไฟฟ้าโลกได้อย่างไร ฉันต้องใช้อัลกอริธึมเสียงรบกวน Perlin บ้างหรือไม่ ฉันไม่รู้ว่ามันจะช่วยให้ฉันสร้างดินแดนเช่นนี้ได้อย่างไร

นี่คือรหัสสำหรับตัวกำเนิดสัญญาณรบกวน perlin ที่ฉันใช้:

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

แต่สิ่งที่ผู้เขียนรหัสใช้ดังต่อไปนี้เพื่อสร้างเสียงรบกวนและฉันไม่เข้าใจอย่างน้อยที่สุด

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

และนี่คือเว็บไซต์ที่ฉันใช้: http://lotsacode.wordpress.com/2010/02/24/perlin-noise-in-c/

และฉันพยายามใช้เสียงเพอร์ลินตามวิธีที่ระบุโดย Martin Sojka

ตกลงดังนั้นนี่คือสิ่งที่ฉันได้รับ:

ป้อนคำอธิบายรูปภาพที่นี่

คำตอบ:


21

สำหรับดินแดนฐานให้สร้างสองเขตข้อมูลเสียงต่อเนื่อง 2 มิติ (Perlin, Simplex, Wavelet เป็นการรวมกันของสิ่งใดก็ตามที่เหมาะกับคุณ) หนึ่งที่มีความถี่ต่ำเป็นส่วนใหญ่ ส่วนแอมพลิจูดต่ำสำหรับขีด จำกัด บนของที่ดินส่วนอีกอันมีทั้งความถี่สูงส่วนแอมพลิจูดสูงและความถี่ต่ำแอมพลิจูดสูงสำหรับขีด จำกัด ล่างของที่ดิน ในกรณีที่ขีด จำกัด ล่างสูงกว่าขีด จำกัด ที่สูงกว่าอย่ารวม voxels บนบก (หรือสิ่งที่เกมของคุณจะใช้เพื่อเป็นตัวแทนของภูมิประเทศ) ผลลัพธ์ที่ได้จะเป็นแบบนี้ ...


แต่นี่สำหรับ 2D ไม่ใช่เหรอ?
Darestium

แต่ฉันก็ชอบมันมาก :)
Darestium

4
2D / 3D - สิ่งเดียวกัน
Gavin Williams

ตกลงไม่ดีพยายามที่จะใช้มันพรุ่งนี้ ... หวังว่าฉันจะโชค;)
Darestium

@Darestium: มันเป็นตัวอย่าง 2D เพื่อให้มองเห็นได้ง่ายขึ้น วิธีการเดียวกันนี้ใช้ได้กับมิติ (พีชคณิต) ขนาดใดสูงกว่าหนึ่ง
Martin Sojka

15

สิ่งนี้จะเพียงพอหรือไม่

ป้อนคำอธิบายรูปภาพที่นี่

ถ้าเป็นเช่นนั้นตรวจสอบบทความนี้ การอ้างถึงส่วนที่เกี่ยวข้องมากที่สุด:

เพื่อให้ได้เสียงรบกวนที่น่าสนใจยิ่งขึ้นสามารถเพิ่มอ็อฟเทอร์เอ๊กซ์หลาย ๆ เสียงเข้าด้วยกัน [... ] เนื่องจากฉันต้องการเอาหินที่มีลักษณะเป็นทรงกลมหยาบ ๆ ฉันต้องคูณเสียงด้วยระยะทางจากจุดศูนย์กลาง [... ] ฉันยังต้องการให้หินราบเรียบอยู่ด้านบนมากกว่าด้านล่างดังนั้นปัจจัยการคูณที่สองคือการไล่ระดับสีในทิศทาง y รวมสิ่งเหล่านี้เข้าด้วยกันและยืด y เพื่อหาจุดรบกวนในขณะที่บีบอัด x และ za bit เราได้อะไรที่คล้ายหินลอย [... ] การขุดถ้ำด้วยเสียงรบกวนอีกตัวอย่างหนึ่งก็ทำให้มันน่าสนใจยิ่งขึ้น

  • ดังนั้นโดยทั่วไปคุณจะเริ่มต้นด้วยชุดข้อมูลที่สร้างขึ้นจากเริมหรือ Perlin เสียง (หรือมากกว่าเลอะเลือนหลายเพิ่มเสียงด้วยกัน )
  • จากนั้นจัดรูปร่างให้ใกล้เคียงกับพื้นดินที่ลอยอยู่โดยทำให้เป็นทรงกลมมากขึ้น(โดยการคูณเสียงรบกวนด้วยระยะทางจากศูนย์กลาง )
  • และสร้างพื้นดินโดยทำให้ราบเรียบใกล้กับด้านบน (โดยการคูณด้วยการไล่ระดับแนวตั้งนั่นคือเริ่มต้นด้วยค่าต่ำที่ด้านบนและสูงขึ้นไปทางด้านล่าง)
  • รวมสามสิ่งนี้เข้าด้วยกันและปรับรูปร่างโดยปรับสัญญาณรบกวนตามแกน X / Y / Z (บทความแนะนำให้ยืดแกน Y และบีบอัดบนแกน X และ Z )
  • ผ่านเพิ่มเติมของเสียงอาจจะใช้ในการขุดถ้ำ

ใช่ฉันคิดว่าสิ่งนี้จะท้าทายสิ่งที่ฉันต้องการ เป็นสิ่งที่ฉันมีประสบการณ์น้อยกับเสียง perlin ดังนั้นสิ่งเดียวที่ฉันสามารถสร้างได้คือภูเขาพื้นฐานจริง ๆ และจะไม่มีความคิดใด ๆ เกี่ยวกับวิธีการเพิ่ม "หลายเสียงอ็อกเทฟของ togther) สำหรับการสร้างเสียง perlin ฉันใช้รหัส ที่ผมได้ออกstackoverflow.com/questions/4753055/...และรังเพลิงมัน C #. ป่วยเพิ่มรุ่นของฉันในการโพสต์ต้นฉบับ ... คุณจะต้องมีความตั้งใจที่จะให้ฉันตัวอย่างของวิธีการที่ผมจะประสบความสำเร็จเช่นผืนแผ่นดินที่มีว่า รหัส?
Darestium

2
นั่นเป็นเหตุผลที่ฉันเชื่อมโยงบทความ มันมีคำอธิบายของทุกขั้นตอนและรหัสที่มาในตอนท้าย คุณควรลองศึกษาดู
David Gouveia

4
  1. ใช้กริด 3 มิติที่มีอยู่ของคุณตัดสินใจเลือกความสูงที่คุณต้องการให้เกาะติด สร้างกลุ่มเกาะในระนาบ 2D นั้น (เรียกว่าระนาบ XY) โดยการกระจายจุดผ่านระนาบแล้ววางลูกบาศก์ที่จุดเหล่านั้น ใช้การรวมตัวกันเพื่อดึงพวกมันเข้าหากันเป็นกลุ่ม กรอกข้อมูลลงในหลุมใด ๆ และคุณมีชุดเกาะยอด
  2. ใช้CA- วิธีการที่คล้ายกันในการเติบโตของเกาะต่างๆ (a) เริ่มต้นที่ระดับ Z ที่คุณพล็อตจุดเริ่มต้นของคุณสำหรับแต่ละเซลล์ในระดับ Z ปัจจุบันนั้นกำหนดโอกาสที่จะขยายไปสู่ระดับล่างถัดไปที่กำหนดจำนวนเพื่อนบ้านในระนาบ XY จาก 0 ถึง 8 ( รวมเส้นทแยงมุมเพื่อนบ้าน), เช่นกำหนดโอกาส 10% สำหรับแต่ละเพื่อนบ้าน, สูงสุด 80% โอกาส คำนวณค่านี้สำหรับแต่ละเซลล์ในระนาบเริ่มต้น (b) จากนั้นสุ่มเลือกโอกาสนี้และขยายลงหากคุณอยู่ในช่วงเปอร์เซ็นต์ ล้างทำซ้ำขั้นตอนที่ 2 (ไปที่ระดับถัดไปกำหนดเพื่อนบ้านสำหรับแต่ละ voxel ขยายลงไปสำหรับ voxel นั้น) จนกระทั่งไม่มีนามสกุลเพิ่มเติมเกิดขึ้น ส่วนขยายลงของคุณควรก่อตัวเป็นรูปกรวยเนื่องจากจำนวนเพื่อนบ้านใกล้เคียงเพราะว็อกแซลที่เข้าหาจุดศูนย์กลาง XY ของเกาะจะมีเพื่อนบ้านมากขึ้น

Pseudocode สำหรับขั้นตอนที่ 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

เมื่อหมู่เกาะของคุณสร้างเสร็จคุณสามารถเลือกที่จะเลื่อนขึ้นและลงในอวกาศเพื่อให้ได้ระดับความสูงที่แตกต่างกัน


ตกลงฉันมีรอยแตกที่วิธีการของคุณและดูเหมือนว่าจะเติบโตภูมิประเทศออกไปด้านนอกแทนที่จะเข้าด้านใน ฉันจะโพสต์รหัส ...
Darestium
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.