4
\$\begingroup\$

I'm currently writing a world generator for a Tilemap-Engine in C++. But there is a little problem concerning the Water to Land ratio. My DiamondSquare-class returns a field of random float-values between 0 and 1, and my condition to let my tilemap-class generate a Land-Tile on the given spot, is that the returned value is bigger than 0.5. The problem is, that the amount of values bigger or smaller than 0.5 fluctuates with every call of my float-field-generation function, so that i often only have a few little Islands/a few little lakes on my map. How could i manipulate my code, so that a 50/50 distibution is more likeley to happen?

include "DiamondSquare.h"
include <Windows.h>
pragma comment (lib, "winmm.lib")
floatfield /*typedef floatfield is std::vector<std::vector<float>>*/ DiamondSquare::generate(int n, float randomness) {

// Initializing stuff
srand(timeGetTime());
fieldSize = pow(2,n)+1;
randField.resize(fieldSize);
for (int i=0; i<fieldSize; i++)
{
    randField[i].resize(fieldSize);
    for (int j=0; j<fieldSize; j++)
    {
        randField[i][j] = 0;
    }
}
stepDist = pow(2, n-1);
m_randomness = randomness;

// Setting corner point values
randField[0][0] = (float)(rand()%1001)/1000;
randField[fieldSize-1][0] = (float)(rand()%1001)/1000;
randField[0][fieldSize-1] = (float)(rand()%1001)/1000;
randField[fieldSize-1][fieldSize-1] = (float)(rand()%1001)/1000;

// Core Algorithm
for (int i=0; i<n; i++)
{
    for (int k=0; k<pow(2,i); k++)
    {           
        for (int l=0; l<pow(2,i); l++)
        {
            // Getting position of current tile
            int xPos = k*2*stepDist+stepDist;
            int yPos = l*2*stepDist+stepDist;
            // Getting Average of sorrounding tiles
            randField[xPos][yPos] = (randField[xPos-stepDist][yPos-stepDist]+randField[xPos+stepDist][yPos-stepDist]+
                                      randField[xPos+stepDist][yPos+stepDist]+randField[xPos-stepDist][yPos+stepDist])/4;
            // Random displacement
            if (rand()%2 == 1)
                randField[xPos][yPos] += (float)(rand()%1001)/1000/pow(randomness,i+1);
            else randField[xPos][yPos] -= (float)(rand()%1001)/1000/pow(randomness,i+1);

            // Doing the same for the Square-Step tiles left of and above     the current tile
            squareAvg(xPos-stepDist, yPos, i);
            squareAvg(xPos, yPos-stepDist, i);

            // If diamond-step-tile is part of the right side row
            if (k == pow(2,i)-1) 
                squareAvg(xPos+stepDist, yPos, i);
            // If diamond-step tile is part of the bottom row
            if (l == pow(2,i)-1)
                squareAvg(xPos, yPos+stepDist, i);
        }
    }
    stepDist /= 2; // Self explanatory
}
return randField; // Return random field.
[![Often, insted of maps like this, there is stuff like 10% water or 10% land][1]][1]}

void DiamondSquare::squareAvg(int x, int y, int i) {
// Testing if square-Tile lies at on of the sides of the map, if so, adding         0 (i tried implementing wrapping, but this option generated better looking     landscapes)
randField[x][y] = (x!=0) ? randField[x-stepDist][y] : 0;
randField[x][y] += (x!=fieldSize-1) ? randField[x+stepDist][y] : 0;
randField[x][y] += (y!=0) ? randField[x][y-stepDist] : 0;
randField[x][y] += (y!=fieldSize-1) ? randField[x][y+stepDist] : 0;
// If 4 values were added /= 4, otherwise /=3.
if (x!=0 && x!=fieldSize-1 && y!=0 && y!=fieldSize-1)
    randField[x][y] /= 4;
else randField[x][y] /= 3;
// Displacement
if (rand()%2 == 1)
    randField[x][y] += (float)(rand()%1001)/1000/pow(m_randomness,i+1);
else randField[x][y] -= (float)(rand()%1001)/1000/pow(m_randomness,i+1);
}

Is it maybe even possible to preciseley control n<0.5 to n>0.5 ratio ? That would be ideal. I just don't know how and where to manipulate the code. That's about it.

\$\endgroup\$
5
  • \$\begingroup\$ rand() isn't really right for this. Look at Perlin noise or simplex noise. \$\endgroup\$
    – 3Dave
    Oct 27, 2016 at 17:59
  • \$\begingroup\$ it's generates a x/1000 value with an x in between 0 and 1000, How is that not the kind of random number that should be used as a random displacement value? And i know that perlin noise would be an alternative to the diamond square algorithm but i personally dont like the kind of patterns it creates. \$\endgroup\$ Oct 27, 2016 at 18:04
  • \$\begingroup\$ Instead of adjusting the generation, it might be better to shift the water level up and down after running diamond-square. If more height samples are greater than 0.5, move the water level higher. If more are less than .5, move the water level down. This only drawback I see is mountains heights might be out of the range you want. \$\endgroup\$
    – tyjkenn
    Oct 27, 2016 at 19:26
  • \$\begingroup\$ Have you tried seeding your terrain? For example, before doing the algorithm, select, say, 10 random points in the array and set half to be the min value and half to be the max. Then when generating the terrain, don't recalculate those points, but use them for the next cycle. Does that make sense? (BTW I've never tried this - it's just an idea off the top of my head.) \$\endgroup\$ Jan 9, 2017 at 3:23
  • \$\begingroup\$ @DavidLively Completely random numbers are right for diamond-square. Perlin noise is a different technique. \$\endgroup\$
    – user253751
    Nov 16, 2017 at 3:33

3 Answers 3

1
\$\begingroup\$

While creating the height values, calculatevthe average of them, this can be done in two ways: either save them in an array, get their sum and divide it with the count or simply have an average and every time you get a new height value add value / count to the average.

The average should be the water level, this is the closest you can get to 50-50%

\$\endgroup\$
0
\$\begingroup\$

When I want to adjust the proportions of different terrain types that occurs in a fractal I use this function:

    List<float> getCutOffLevels(float[,] map, float[] levels) 
    {
        float[] mapValues = new float[map.Length];
        System.Buffer.BlockCopy(map, 0, mapValues, 0, sizeof(float)*map.Length);
        Array.Sort(mapValues);
        float levelSum = 0;
        foreach(float a in levels){
            levelSum+=a;
        }
        List<float> cutOffLevels = new List<float>();
        float t = 0;
        for (int i=0;i< levels.Length-1;i++) 
        {
            cutOffLevels.Add(mapValues[(int)Math.Floor(mapValues.Length * t / levelSum)]);
            t += levels[i];
        }
        return cutOffLevels;
    }

The function takes the fractal as input in addition to an array containing the proportions of the different terrain types. Say, for example you want two regions at an 2:1 ratio, you input an array with values 2 and 1. The output is a list containing the values of the of the fractal at the border between regions. The first region has values greater than than the first item in the list, the second region greater than second value, and so forth.

\$\endgroup\$
-1
\$\begingroup\$

You can subtract .5 and then divide your outcome by something to decrease the spread and then add .5

\$\endgroup\$
1
  • \$\begingroup\$ How does that help? That will just make the mountains lower and the lakes less deep, won't it? \$\endgroup\$ Jan 9, 2017 at 3:20

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .