Loading

Logo

Procedural Content Generation

Programming and System Design · 5 minutes Read

Creating a dynamic PG world that adapts based on economy, AI, and weather + seasonal changes.

To further enhance the immersion of the world, create an Endless Globe around it.

When approaching procedurally generated worlds within Unity, there are two simple techniques that I've come across. The first being a simple Grid, assigning various prefabs such as grass, rocks, and trees, and instantiating them randomly. The second is slightly more complex, creating a randomised terrain based on Perlin Noise, before then painting certain textures on top. In this post, I'll cover how to do both, with the complexity and design falling completely on what you, the developer, can expand upon.

Part 1: Instantiated Grid

To implement a simple PCG into Unity we create some prefabs with various variables and just place them around. A simple way we can add complexity to this design is assigning each item at least two or three different objects, and upon instantiating leaving one active at random. This can further be expanded by assigning seasons and passage of time, potentially even having the trees change colour as the years go by. To start, create a simple "for" loop, running through the width and height of the grid, and adding a terrain prefab in each spot. In order to add variety, create a random number generator to determine what prefab to create in each coordinate, as well as some checks to allow water or certain collisions to not occur.

for (int y = 0; y < gridHeight; y++)
{
	for (int x = 0; x < gridWidth; x++)
	{
		Transform prefab = Instantiate(plainPrefab) as Transform;
		Vector2 gridPos = new Vector2(x, y);
		prefab.position = CalcWorldPos(gridPos);
		prefab.parent = this.transform;
	}
}

Ta-da! It really is that simple, the complexity and design depending completely on how it's utilised and the creativity behind it. If you would like to experience a project I made using this methodology with some very basic AI and environmental changes, check out the page linked below.

Test on Itch.io

Part 2: Procedural Terrain

Terrains within Unity have always been a slight peeve of mine especially as someone who enjoys creating their custom physics. Having ground that is wobbly in some places and wobblier in others is practically begging for players to abuse wall clipping and gravity exploits. This is a problem that I still haven't fully figured out how to fix, but by limiting the maximum depth of the terrain as well as the size, you can make the world seem large whilst containing fewer polygons, preventing the terrain from getting in the way of the core mechanics, whilst remaining realistic. This is where Perlin Noise comes into play, creating smoother transitions and scales seamlessly, and it seems to work quite well. Below is an image of the terrain in practice, with some texture and height overlap meshing together.

To start, create a simple Terrain in Unity and inside the settings set the Terrain size, as well as Mesh Resolution and any other detail you prefer. Within the Texture settings, start creating some terrain textures that you're planning on using, making sure to order them in a way that makes sense for when you call them in code.

Inside the script call the Terrain component as well as assigning a public Depth, and Scale variable. The Depth consists of how high the terrain goes. The Scale is how focused the Perlin Noise will be. These values should be experimented with to get a consistent visual. Next, in order to generate the terrain, assign the TerrainData of the Terrain and the heightmap, calculating where on the map each variance should be, before applying the Perlin Noise to that specific scale. The second image below may seem slightly convoluted but is in fact simply defining where on the terrain each variance should be based on the x and y positions. In the centre, the concentration will be the strongest due to the multiplier variable, yet as it slowly expands that number will slowly decrease, allowing for a smooth transition. This, of course, doesn't have to only be a circle, with the concentration dependant on whatever you decide.

TerrainData GenerateTerrain(TerrainData terrainData)
{
	terrainData.heightMapResolution = size + 1;
	terrainData.size = new Vector3(size, depth, size);
	terrainData.SetHeights(0, 0, GenerateHeights());
	return terrainData;
}

Painting the Terrain works exactly the same, defining where on the script you want to apply the splat weights, and creating a multiplier to strengthen its effect on the centre, slowly fading it out. Both of these methodologies could even be joined together, creating a random terrain and then instantiating resources such as trees and rocks directly on top, allowing a completely random world to be created within Unity.