0

EDIT: I ended up using the FastNoise library found here: https://github.com/Auburns/FastNoise It has everything under the sun which someone might need to generate many different kinds of noise. As the title might suggest its also quite fast!

I'm creating a 2D infinitely, procedural generated world. I load and unload chunks from disc as the player moves. I'm using a cellular automata function to define how local tiles within each chunk are designed but I need noise (Perlin in this case) to define what biome type each chunk will be as new ones are created. I understand how I would translate decimals between 0 and 1 to represent this, my only issue is that the tutorial I followed on creating Perlin noise requires that you pass it a predefined 2d array and returns a noise array of the same size. Because my world grows dynamically I'm a bit confused on how I would use a fixed sized array to designate new chunk types.

Other answers I've seen don't cover exactly how to handle the infinite part of noise generation. My best guess is that I need to somehow generate or expand the noise with each newly created chunk although how I do this stumps me.

Here is some code I translated to C# from here: http://devmag.org.za/2009/04/25/perlin-noise/

admittedly some of the math here I don't fully understand yet, especially the bitwise function!

public class PerlinNoiseGenerator
    {
        public int OctaveCount { get; set; }
        public float Persistence { get; set; }


        public PerlinNoiseGenerator(int octaveCount, float persistence)
        {
            this.OctaveCount = octaveCount;
            this.Persistence = persistence;
        }

        public float[,] GenerateWhiteNoise(int width, int height)
        {
            float[,] noiseFieldToReturn = new float[width, height];
            for (int i = 0; i < width; i++)
            {
                for (int j = 0; j < height; j++)
                {
                    noiseFieldToReturn[i, j] = (float)Game1.Utility.RGenerator.NextDouble() % 1;
                }
            }
            return noiseFieldToReturn;
        }

        public float[,] SmoothNoiseField(float[,] whiteNoise, int octave)
        {
            int width = whiteNoise.GetLength(0);
            int height = whiteNoise.GetLength(1);
            float[,] smoothField = new float[width, height];

            int samplePeriod = 1 << octave;

            float sampleFrequency = 1.0f / samplePeriod;

            for(int i =0; i < width; i++)
            {
                int samplei0 = (i / samplePeriod) * samplePeriod;
                int samplei1 = (samplei0 + samplePeriod) % width;

                float horizontalBlend = (i - samplei0) * sampleFrequency;
                for(int j =0; j < height; j++)
                {
                    int samplej0 = (j/samplePeriod) * samplePeriod;
                    int samplej1 = (samplej0 + samplePeriod) % height;
                    float verticalBlend = (j - samplej0) * sampleFrequency;

                    float top = LinearInterpolate(whiteNoise[samplei0, samplej0],
                        whiteNoise[samplei1, samplej0], horizontalBlend);

                    float bottom = LinearInterpolate(whiteNoise[samplei0, samplej1],
                        whiteNoise[samplei1, samplej1], horizontalBlend);

                    smoothField[i, j] = LinearInterpolate(top, bottom, verticalBlend);
                }
            }

            return smoothField;
        }

        public float[,] GeneratePerlinNoise(float[,] baseNoise, int octaveCount)
        {
            int width = baseNoise.GetLength(0);
            int height = baseNoise.GetLength(1);

            float[][,] smoothNoise = new float[octaveCount][,];

            float persistance = .5f;

            for(int i =0; i < octaveCount;i++)
            {
                smoothNoise[i] = SmoothNoiseField(baseNoise, i);
            }
            float[,] perlinNoise = new float[width, height];
            float amplitude = 1f;
            float totalAmplitude = 0.0f;

            for(int octave = octaveCount - 1; octave > 0; octave-- )
            {
                amplitude *= persistance;
                totalAmplitude += amplitude;

                for(int i =0; i < width;i++)
                {
                    for(int j =0; j < height; j++)
                    {
                        perlinNoise[i, j] += smoothNoise[octave][i, j] * amplitude;
                    }
                }
            }

            for(int i =0; i < width; i++)
            {
                for(int j =0; j < height; j++)
                {
                    perlinNoise[i, j] /= totalAmplitude;
                }
            }
            return perlinNoise;
        }

        public float LinearInterpolate(float a, float b, float alpha)
        {
            return a * (1 - alpha) + alpha * b;
        }
    } 

This code should compile and produces a FIXED size array of noise

  • You are probably looking for something like: https://forum.unity.com/threads/perlin-noise-chunk-loading-chunks-not-looking-right.541105/ This all boils down to using `perlinNoise[i, j] and width, height`. I do not have time to help you more than this. – Christophe Roussy Oct 07 '19 at 09:23

1 Answers1

1

The main thing you want to make sure is that the starting random noise is pseudo random, so you always have a "fixed random value" for a coordinate.

It might be that you'd have to rewrite your random noise generator, using the coordinates as input. I imagine your maps have a random seed number, so you could use this post as a starting point, adding 1 factor: A pseudo-random number generator based on 2 inputs

For a bit of inspiration for your map making, I wrote this article a while back: https://steemit.com/map/@beeheap/create-a-fantasy-grid-map-in-excel

Added after your comment: the only function you'd need to change is the GenerateWhiteNoise one. I don't speak C#, but this is the general idea:

GenerateWhiteNoise(int x_start_coord, int y_start_coord, int random_seed) {
    int default_x_width = 100;
    int default_y_heigth = 50;
    float[,] noiseFieldToReturn = new float[width, height];

    for (in x = x_start_coord; i < default_x_width + x_start_coord; x++)
    {
        for (in y = y_start_coord; i < default_y_width + y_start_coord; y++)
        {
            noiseFieldToReturn[i, j] = (float)pseudo_rnd_value(x, y, random_seed);
        }
    }
    return noiseFieldToReturn;
}

That should give you the pseudo random values you need to build your map tiles, the only thing you need is the coordinate of the player (x and y).

Koen Rijnsent
  • 230
  • 1
  • 13
  • Thanks for the reply. Your Excel map is very cool. I'm not exactly sure how to interpret the question you linked, however. I get that I'd need to use coordinates as input, but I'm not sure how I would do that without somehow referencing a set 2D array (like you have in yours). – propellerhat23 Oct 16 '19 at 02:34
  • Updated my answer with some mockup code, does that help? – Koen Rijnsent Oct 17 '19 at 11:27