30
\$\begingroup\$

I am programming a tile based game and I have some basic tiles (grass, dirt, etc..), but I can't figure out how to make good random map generation, because when I do some really random selection, if the tile should be grass/dirt, I get this:

In-game picture

I understand why this is happening, but what I want is to create some random continuous areas of grass or dirt. Something that would make more sense, like this:

Desired result

\$\endgroup\$
1
  • 1
    \$\begingroup\$ In addition to the proposed answers, you could write your own cellular automaton engine to generate such maps. Modifying the rules of the automaton you could generate very different behaviours, leading in very different types of maps. For example, gievn a 2d automaton similar to Life, flooding rules could lead to "ocean and isles" (Like your picture above), and a rule like 1267/17 could lead into beautiful laberynths. \$\endgroup\$
    – Manu343726
    Commented Jun 22, 2014 at 17:16

4 Answers 4

25
\$\begingroup\$

What you could do is randomly generate a Voronoi map like this:

  1. Picking random center points (see the black dots) and randomly decide if they are grass or dirt.
  2. Then for over all tiles, check if it's closest to a center point of dirt or a grass.
  3. Done!

If what you did previously is "flip a coin" for each tile (noise), generating a Voronoi diagram will provide a much better result.

You could improve on this by dividing the center points into islands with an algorithm that:

  1. Picks a small group of centers points and designates them as leaders.
  2. Iteratively adds a random adjacent undecided center point each turn.
  3. Done!

enter image description here

\$\endgroup\$
11
  • 1
    \$\begingroup\$ Thanks, but it seems like a very difficult solution. \$\endgroup\$
    – Vilda
    Commented Jun 21, 2014 at 10:21
  • 2
    \$\begingroup\$ This is not a very difficult solution. First randomly pick the center points. Then decide if they are grass or dirt. Now loop over the array and decide if each tile is closest to a dirt point or a grass point. Perhaps tell me which part challenging? \$\endgroup\$
    – AturSams
    Commented Jun 21, 2014 at 10:33
  • \$\begingroup\$ Measuring the distance. Anyway, I will try and let you know. \$\endgroup\$
    – Vilda
    Commented Jun 21, 2014 at 10:55
  • \$\begingroup\$ Ok, so I have succesfully generated some points. (dropbox.com/s/dlx9uxz8kkid3kc/random_tile_map2.png) and so my whole class for generating looks like this: dropbox.com/s/pxhn902xqrhtli4/gen.java. But it still doesn't work. \$\endgroup\$
    – Vilda
    Commented Jun 21, 2014 at 11:07
  • 4
    \$\begingroup\$ @ViliX your links are broken \$\endgroup\$ Commented Jul 3, 2014 at 13:13
25
\$\begingroup\$

You could use perlin noise, which is normaly used for heightmap generation. Perlin noise in games

Then you could use the heights as an adviser, how high the chance of grass/dirt occuring in one region of the map is.

Example (Perlin noise values from 0-256): If the value is over 200 the chance that grass is placed is 80% (dirt 20%). If the value is between 100 and 200 the chance that grass is placed is 50% (dirt also 50%). If the value is under 100 the chance that grass is placed is 20% (dirt 80%).

\$\endgroup\$
6
  • \$\begingroup\$ Ok, let's say that I have an array[][] of float (0-1 of probability) and I can use it to spawn tiles with the probability. But how do I fill this probability array? The array is (let's say) 400x200, how to fill it with probability values? \$\endgroup\$
    – Vilda
    Commented Jun 21, 2014 at 10:17
  • \$\begingroup\$ He is suggesting to fill it with perlin noise (instead of white noise like coin flips). \$\endgroup\$
    – AturSams
    Commented Jun 21, 2014 at 10:24
  • \$\begingroup\$ Yes, but how can I achieve that? \$\endgroup\$
    – Vilda
    Commented Jun 21, 2014 at 10:25
  • \$\begingroup\$ The link I posted could clear this question. First generate a noise and then smooth this noise.The result will be a 2 dimensional array you can use for further generation of the tilemap.link \$\endgroup\$
    – Klitz
    Commented Jun 21, 2014 at 10:26
  • \$\begingroup\$ It is a really good answer but it will rarely get results similar to the ones you presented. \$\endgroup\$
    – AturSams
    Commented Jun 21, 2014 at 10:35
9
\$\begingroup\$

enter image description here

http://gamedevelopment.tutsplus.com/tutorials/generate-random-cave-levels-using-cellular-automata--gamedev-9664

here is my version of the cellular automata method start by filling the grid with random then run these cullular automata rules on it a couple times

  • If a living cell has less than two living neighbours, it dies.
  • If a living cell has two or three living neighbours, it stays alive.
  • If a living cell has more than three living neighbours, it dies.
  • If a dead cell has exactly three living neighbours, it becomes alive.

and it ends up looking like a cave

the index can be converted to the x&y position and back with this code

public int TileIndex(int x, int y)
{
    return y * Generator.Instance.Width + x;
}
public Vector2 TilePosition(int index)
{
    float y = index / Generator.Instance.Width;
    float x = index - Generator.Instance.Width * y;
    return new Vector2(x, y);
}

i just return a list of bools because i use this list for many things: caves, trees, flowers, grass, fog, water you can even combine multiple lists in different ways here i first remove all smaller caves then union two random lists

enter image description here

private int GetAdjacentCount(List<bool> list, Vector2 p)
{
    int count = 0;
    for (int y = -1; y <= 1; y++)
    {
        for (int x = -1; x <= 1; x++)
        {
            if (!((x == 0) && (y == 0)))
            {
                Vector2 point = new Vector2(p.x + x, p.y + y);
                if (PathFinder.Instance.InsideMap(point))
                {
                    int index = PathFinder.Instance.TileIndex(point);
                    if (list[index])
                    {
                        count++;
                    }
                }
                else
                {
                    count++;
                }
            }
        }
    }
    return count;
}
private List<bool> GetCellularList(int steps, float chance, int birth, int death)
{
    int count = _width * _height;
    List<bool> list = Enumerable.Repeat(false, count).ToList();
    for (int y = 0; y < _height; y++)
    {
        for (int x = 0; x < _width; x++)
        {
            Vector2 p = new Vector2(x, y);
            int index = PathFinder.Instance.TileIndex(p);
            list[index] = Utility.RandomPercent(chance);
        }
    }
    for (int i = 0; i < steps; i++)
    {
        var temp = Enumerable.Repeat(false, count).ToList();
        for (int y = 0; y < _height; y++)
        {
            for (int x = 0; x < _width; x++)
            {
                Vector2 p = new Vector2(x, y);
                int index = PathFinder.Instance.TileIndex(p);
                if (index == -1) Debug.Log(index);
                int adjacent = GetAdjacentCount(list, p);
                bool set = list[index];
                if (set)
                {
                    if (adjacent < death)
                        set = false;
                }
                else
                {
                    if (adjacent > birth)
                        set = true;
                }
                temp[index] = set;
            }
        }
        list = temp;
    }
    if ((steps > 0) && Utility.RandomBool())
        RemoveSmall(list);
    return list;
}
\$\endgroup\$
1
  • 2
    \$\begingroup\$ Please explain what your code does, rather than posting a sample of what it does, a link, and the code itself. Your answer should be self-contained, so that even if the links break, it's still usable. \$\endgroup\$
    – anon
    Commented Jan 15, 2015 at 2:01
7
\$\begingroup\$

Select a point on the map. Place desired tile type with a base value such as 40. Keep track of where you placed your newly desired tile. Add the starting point to a list.

For each point in this list, you visit all neighbours. While you have enough power left (started at 40) add a desired tile and add it to the list to be visited. Give the new tile less power, determined by you. Easiest = random lowering. After you visited the tile from the list, remove it. Start over again by visiting any unvisited but created tiles.

\$\endgroup\$
1
  • \$\begingroup\$ What an accessible and fun-sounding implementation, thank you! \$\endgroup\$ Commented May 8, 2020 at 16:25

You must log in to answer this question.

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