A Guide to Procedurally Generated 2D Maps

A Guide to Procedural Generation

While hand-crafted 2D maps for games are amazing, they can take a lot of time to make. Plus, once your game is published, the only way to add more maps is to update the game – which in itself is a tedious process. This is where procedural generation comes in, as it allows our code to generate maps for us and takes out much of the grunt work!

In this tutorial, we’ll be creating a 2D map that is procedurally generated with many different types of terrain(forest, snow, desert, etc). We’ll accomplish this by using procedural noise maps and generating one for the height, moisture, and heat. Combining these together will give us a specific biome for each tile, and result in a pretty nifty map generation handled entirely by our backend!

Example of a procedurally generated map in Unity

Project Files

In this tutorial, we’ll be using an edited public domain sprite sheet for the different biome tiles (from OpenGameArt.org).

  • Download the edited sprite sheet here
  • Download the complete Unity project here

Don't miss out! Offer ends in
  • Access all 200+ courses
  • New courses added monthly
  • Cancel anytime
  • Certificates of completion

Creating the Project

To begin with our procedural map generation, let’s create a new Unity project with the 2D template selected. In our new project, we want to create 4 new folders.

  • Biomes – where we’ll store the scriptable objects representing each biome
  • Prefabs – where we’ll store the tile prefab and player prefab
  • Scripts – where we’ll be storing our C# scripts
  • Sprites – where we’ll be storing our sprite sheet

Unity Assets folders set up for map generation

Inside of the Sprites folder, drag in the sprite sheet.

Spritesheet we'll be using for procedurally generating our map

Selecting the sprite sheet, let’s change some settings over in the Inspector.

  • Set the Texture Type to Sprite (2D and UI)
  • Set the Sprite Mode to Multiple
  • Set the Pixels Per Unit to 32
  • Set the Filter Mode to Point (no filter)
  • Click Apply to apply the changes

Unity Inspector with Spiresheet Import Settings

Now we need to divide our sprite sheet into individual sprites. In the Inspector, click on the Sprite Editor button to open up the Sprite Editor window.

  • Click on the Slice dropdown
  • Set the Type to Grid By Cell Size
  • Set the X and Y Pixel Size to 32
  • Click Slice
  • After that’s done, click on the Apply button and exit the Sprite Editor

Unity SpriteEditor for 2D map sprite sheet

In the Project browser, you should now be able to open up the sprite sheet to see all of the individual sprites.

Sprites in Unity Assets after being sliced

What is Noise?

How are we going to procedurally generate our map? With noise. You give the algorithm an X and Y value and it will return to you a value between 0 and 1. Below is an example of Perlin Noise – this is what Unity uses.

Example of a noise map

Noise is used for a large number of things and pretty much every game with procedurally generated terrain uses it. Now, this doesn’t look very natural as the transition between 0 and 1 is very uniform across the noise map. To combat this, we can stack multiple noise maps on top of each other to get more varied and custom outputs.

These are known as waves and have a few properties that we can modify.

  • Seed – this is the amount we are offsetting the noise so that we’re not sampling the same area for everything
  • Frequency – this is the scale of the noise map we’ll be sampling, a higher frequency will result in a more bumpy and noisy output, while a lower frequency will result in something more like the image above
  • Amplitude – this defines the size or intensity of the output

Here’s a visual look at frequency:

Graph demonstrating high frequency waves vs. low frequency

Here’s a visual look at amplitude:

Graph showing High Amplitude waves vs. low amplitude

Here’s what a noise map would look like with just 1 wave. It looks quite uniform and not that natural.

Noise map example with only 1 wave used

Here’s a noise map with 2 waves. It looks a lot more natural as the transitions between 0 and 1 are less consistent.

Noise map with 2 waves used

Noise Generator

Let’s now create a new C# script and call it NoiseGenerator. This script is only going to have one function – generate noise. Before we do that though, at the bottom of the script (outside of the NoiseGenerator class) let’s create a new class so that we can have waves.

Now back in the main NoiseGenerator class, let’s create the Generate function.

Here’s what each of the parameters do:

  • width – width of the noise map
  • height – height of the noise map
  • scale – overall scale so we can zoom in or out if needed
  • waves – array of different waves to generate the noise map
  • offset – horizontal and vertical offset if needed

The function also returns a float[,] – a 2D float array. Think of this as a spreadsheet with columns and rows. Each element will be a number between 0 and 1.

So inside of this function, we want to create a 2D float array for our noise map and then loop through each of those elements with 2 for loops.

Inside of the second for loop, let’s first get the sample position for the X and Y values.

Then we can look through each wave and sample the noise – taking into consideration the frequency and amplitude.

And that’s it! Here’s a look at the final function.

Biome Preset

In order to define our biomes, we’re going to use scriptable objects. These are basically files we can create which hold properties. Let’s create a new C# script called BiomePreset.

  1. First, we need to be using the UnityEditor namespace.
  2. Then we need to add the CreateAssetMenu attribute.

Inside of the class, we can now define our properties.

  • We have the sprites in an array for variation.

Underneath our properties, we’re going to create two functions. GetTileSprite returns a random sprite from the tiles array.

MatchCondition checks to see if this biome matches the given height, moisture, and heat values. A biome can be viable if the given values for that tile are greater than or equal to the biome’s minimum values.

Back in the editor, let’s go over to our Biomes folder and create some biomes. Right-click and select Create > New Biome Preset. Give it a name. Go ahead and create 7 biome assets. Desert, Forest, Grassland, Jungle, Mountains, Ocean, and Tundra.

Biomes set up in Unity assets for 2D map generation

Then for each biome, we want to select it, go over to the Inspector, and fill in the properties.

  • Desert
    • Min Height = 0.2
    • Min Moisture = 0
    • Min Heat = 0.5
  • Forest
    • Min Height = 0.2
    • Min Moisture = 0.4
    • Min Heat = 0.4
  • Grassland
    • Min Height = 0.2
    • Min Moisture = 0.5
    • Min Heat = 0.3
  • Jungle
    • Min Height = 0.3
    • Min Moisture = 0.5
    • Min Heat = 0.62
  • Mountains
    • Min Height = 0.5
    • Min Moisture = 0
    • Min Heat = 0
  • Ocean
    • Min Height = 0
    • Min Moisture = 0
    • Min Heat = 0
  • Tundra
    • Min Height = 0.2
    • Min Moisture = 0
    • Min Heat = 0

Also, make sure to fill in the tiles array with each biome’s respective sprites from the sprite sheet.

Creating the Map

Now we can create a new C# script called Map and attach it to a new empty GameObject called _Map. This script is in charge of generating our 2D map. First, let’s start with some properties.

An array to store all of our biomes and the prefab for each tile.

The dimensions of the map, scale and offset.

We’re going to be generating 3 maps. A height, moisture and heat map. These each need their own waves and output array.

Now we can create our main function called GenerateMap. This will get a noise map for the height, moisture and heat, then spawn in the tiles and give them each a biome.

We can then call this function in the Start function.

You’ll see we’re calling a function called GetBiome. This is in charge of deciding which biome to select based on the given values for height, moisture and heat. Let’s create it now.

Before we fill in the function though, there’s something we need to create. It’s a new class called BiomeTempData. Because this is how we’re going to calculate which biome to choose:

  1. First, we’ll loop through each biome and see if the height, moisture and heat are equal to or greater than the biome’s minimum values.
  2. If so, we’ll add that biome to a list (because multiple biomes may meet those conditions and we need to choose one).
  3. Then we’ll loop through that list and for each biome calculate its difference value. This is basically the difference between the height and its min height, the moisture and its min moisture and the heat and its min heat added together to give us a number.
  4. We want to find the biome with the lowest difference value as that means it’s the closest to the target conditions.

Here’s what the BiomeTempData class looks like:

Now inside of our GetBiome function, let’s first get a list of all the biomes which match the given height, moisture and heat.

Once we have the list of possible biomes, we need to find the one with the lowest difference value.

Back in the Unity editor now, we can fill in the properties. First, drag in all the biome assets into the biomes array.

List of Biomes added in Unity Inspector for Procedural Map Generation

  • Set the Width to 100
  • Set the Height to 100
  • Set the Scale to 1

Then, create two elements inside of the height waves array and make them like so:

Settings for height waves for procedurally generated maps in Unity

For the moisture waves I only have 1, although you can have as many as you wish.

Moisture Waves settings in Unity for map generation

Then finally, the heat waves:

Heat Waves settings in Unity for procedurally generated map project

Tile Prefab

Let’s now create the tile prefab. In the Hierarchy, right click and create a new 2D Object > Sprite. Call it TilePrefab. Drag it into the Prefabs folder to save it as a prefab.

TilePrefab created in Unity Assets

Select the _Map object and drag the tile prefab into the corresponding property field.

TilePrefab added to Unity Inspector

Press play and you should see the generated map!

Procedurally generated map in Unity

Conclusion

Congratulations! We just created a procedurally generated 2D map inside of Unity!

As you’ve seen, with procedural generation, we can easily add a lot of variety to our games. This has a ton of implications on the replayability value of them, as well as how much development time will be needed to set them up.

Of course, you can extend map generation far beyond this. Perhaps you want to expand your biomes more and make unique ones for an alien planet. Or maybe you want to tweak the settings and create more of an ocean world. The sky is essentially the limit here, but with randomized maps, you’ll have a whole new world to explore each time the game is played.

We hope you’ve gotten a lot out of this tutorial, and we wish you the best of luck with your future games!