A tool I developed that samples Perlin noise and integrates it into Unity's terrain system to procedurally generate a landscape. I made all the code, but the props placed on the map were not made by me, I downloaded them from the asset store.
The final result is an archipelago with a bunch of different biomes, which inadvertently makes it sort of look like a board of the game Catan.
Engine: Unity 2021 LTS.
Production Period: Created for a class during my time in RMIT, made in around a month.
Notable Features:
Sampling Perlin noise to generate height data for Unity's terrain system.
Create a colour map based on height and applying it to the terrain's material through ray marching.
Placing props on the landscape based on the mesh's height, also through ray marching.
The goal I had in mind when programming this tool was to create a system that could be integrated into a hypothetical game with a procedural focus. Hence, it needed to be easy to set up, but also flexible enough to give me options to generate fairly different results rapidly.
For ease of use, I made it so you can control everything from a single script - "TerrainGeneration1" - housed in the topmost parent object, "TerrainUpdated". This script generates the terrain data. It is also responsible for triggering the colour map generator script (which is inside of a child object) after the mesh is created. In the same way, it also triggers the tree generator to place the props on the map. This can be seen in the above image
In order to make debugging easier, I exposed all of its internal functions to the editor via the context menu of the scripts, so I could easily test each aspect of the tool individually, or together, as seen below:
Another aspect I designed with the intent of making debugging simpler was the adoptions of GUI gizmos. As seen below, I made a green box that visually represents the area where the tree placer takes effect:
I'll go through each main variable and what it does.
Terrain Resolution - The size of the terrain is determined in powers of 2, the number of times it is powered is set by this variable
Grid Number - The number of islands generated is also in powers of 2. In this case, 2 x 2 = 4 islands generated
Terrain Mat - A reference to a terrain material, which has no info at first, and gets populated after generation is finished
Unlike the material, no reference to the terrain component is needed because it lives in the same object as the script itself. Likewise, the terrain data comes attached to it, so no need to reference it either
Biomes is a struct array where you can define the details pertaining to the terrain mesh being generated. It is in an array format so that you can specify multiple terrain types to be used at the same time. For each item in the array, you can define the following variables:
Biome Name - Purely for ease of use, it has no function. It's a label so it is easier for the user to remember what terrain type each item of the biomes array is creating
Spawn Chance - In order to pick which terrain biome from the array should be used in each island, this variable gives the system a higher or lower chance of using this specific array item's information, where higher values are more likely to be used, making smaller ones rarer
Biome Depth - Ranging from 0 and 1, it determines how high the mesh of this biome is allowed to become, where 1 is the maximum height, and 0 is completely flat
Biome Scale - Determines how much area of the Perlin noise is being used. Higher values make the height variance more pronounced, lower values make for more even heights
Once all the above variables are defined, the script then calculates the height data of each island one at a time in a grid format, using the modifiers to change the Perlin noise equation for each. It then applies a falloff to ensure each island has a flat area between each of them, where the sea can separate them
All this information gets stored in the terrain data asset that lives inside the terrain component. I made it so the script that generates terrain is attached to the same object that has the terrain itself, that way it saves me from having to reference it to a remote object. This then concludes the mesh generation aspect of this tool
Colour Per Height is also a struct array. The name is self explanatory enough. Colour of the terrain is determined by height, which creates a pattern as if it was a layered cake. You can add any number of colours by adding items to the array
Colour Depth - Ranging from 0 to 1, where 0 is sea level and 1 is the peak of the height map. This value is compared to the next item's colour depth to determine up to what height this specific colour can go to (in case no value is above the last value determined, it turns everything above it black)
Biome Colour - The colour itself that will be applied at this height range
Once the information has been set, the colour is applied to the terrain through a system I developed by taking inspiration on the concept of ray marching. As seen in the above image, the Colour Map Generator object is hanging above the terrain. Notice that it is 199 units above ground, but its ray height is 200, meaning that it is guaranteed to always hit the ground.
Once the terrain mesh has been concluded, this object is told to initiate the process of generating the colour map, which it does by moving incrementally from the top left corner of the terrain down to the bottom right, row by row, column by column (it knows the extremities of the terrain mesh by receiving it from the terrain generator script). Each time it moves, it shoots a ray downwards which determines the location where it has made contact with the terrain as a vector 3. Depending on the depth of said vector 3, it checks against the Colours Per Height array to see in what colour range it falls under. Once found, it stores this particular colour as a pixel of a texture 2d, which upon being finished, gets applied to the terrain's material, in the above case, named ProceduralLayer
Optionally, I also made a boolean that allows you to store the resulting texture as a png in your local files, in case you want to keep it for later. This concludes the colour generation aspect
Finally, the system that places the trees and other props. It actually works through the same logic as the colour map generator, through ray marching. An object above the terrain moves in rows and columns one by one, shooting a ray down, and upon its point of contact, it spawns a prop depending on the specified information. Each prop is attached as a child of this game object in the end. The following are the main variables:
Start - The X and Y coordinates (not including depth) where the tree generator starts moving from
DebugSpawnCount - The total number of props that will be spawned
Size - The total area that will be used to spawn the props
Max Height - The tallest point where any prop can be spawned
Valid Layers - You can specify in what layers you want a prop to spawn or not. This is how I made it so nothing spawns in the sea, by making sure that the sea object is in a layer not included here
Size Variance - Once a prop is spawned, this factor determines its variance in scale, both up and down
Extras Factor - I wanted there to be more small objects on the ground than trees, which is why I made this variable, so that for every tree that spawns, this number of props also spawns, ensuring there is always more of them than trees
Trees Per Height is, as you can imagine by now, also a struct array. It works similarly to the colour array in the sense that it utilizes the height of the terrain to determine what will happen, except here it spawns an object instead of making a colour map:
Tree Depth - The height needed to spawn this specific prop type
Spawn Chance - How likely this particular prop is to actually spawn, which can be used to make certain props more or less prevalent than others
Biome Trees and Extras - Here you can specify the prefabs for the props you want to spawn in the terrain for this particular height. Whenever it successfully chooses a spot to spawn something, it picks one from the list at random
This concludes the section about spawnning props