DevBlog_01: Terrain Generation
Me (game developer) and a friend of mine (artist) are creating a single-player, Tower Defense, Exploration game in Unreal Engine 5.
Tower Defense with a twist: The player will not be dragging and dropping defenses on a map from the game interface, like in traditional TD games. The player will be a character gathering resources, building defenses and defending along with the defenses he builds (think of: Orcs Must Die X Minecraft).
Exploration: Every time the player replays a level, the layout of the level will be different. The goal is to make the player explore every time the level starts and not allow him to memorize locations and strategies. Every level play through must be unique.
Discover the process behind creating the terrain in our game:
As there will be a building mechanic in the game, we wanted to make the terrain grid-based and not hide the grid from the player.
Each level’s flooring is created by arranging various types of ‘cell chunks’ adjacent to one another. Think of the type of chunks as types of landforms: hills, valleys, plains, etc.
Chunk code design:
ChunkManager: Responsible for generating the world.
Chunk: Base class from which the different Blueprint Chunk assets inherit and are constructed.
Cell: Holds information about the cell, shows/hides and animates the mesh.
Chunk creation:
The UE5 editor is not a place, where you can easily design and create these landforms (it’s also not intended to be), that’s why each chunk is designed and constructed in Autodesk Maya.
Attempt 1:
I thought that simply importing the Chunk.fbx into UE5 and then just attaching the custom Cell class component to the meshes in the newly created blueprint, will be enough to achieve the latter code design.
But apparently, in UE5 you can not bulk/mass add components in a Blueprint asset, as seen in the video below:
Attempt 2:
I’ve also tried to do the same through code: adding the Cell class component to the Chunk blueprint hierarchy from the constructor function. However, when the constructor runs, the data of the blueprint is not yet available:
#include "ImportedChunk.h"
// Pseudocode to run inside the constructor function
void AImportedChunk::GetChildrenOfImportedChunk()
{
if (RootComponent)
{
TArray<USceneComponent*> ChildComponents;
RootComponent->GetChildComponents(ChildComponents); // Feed the ChildComponents array.
// Iterated through ChildComponents array and add the custom Cell class.
for (USceneComponent* ChildComponent : ChildComponents)
{
// ChildComponents is empty and we never get inside this loop.
}
}
}
Solution:
At the end, I imported only the cell.fbx and used it to construct the whole Chunk blueprint from code.
In order to achieve the chunk landforms designed in Maya, I only needed to know where and how to place each cell in the chunk. For that we created a python script for Maya, to export the chunk landform data (the location, rotation and mesh type of each cell) into a JSON file:
💡: What I have found surprising, is that you can not have .txt files inside an UE5 project.
Therefore, if I want to have a JSON file with config data, I had to convert that file into so called “Data Table” asset, in order to use it in code:
Now each chunk landform blueprint asset should have its own JSON config data, so it knows how to construct itself.
💡: Something that I didn’t like about Unreal, is that you can’t reference assets (in my case the JSON config data) in the blueprint asset and use these references in the constructor:
Therefore, I had to use ObjectFinder class in the constructor, so I can get the JSON data that I want to use. The following function will have to be improved in the future, but for now it does the job :)
// I know it is prone to human error to get assets by their name,
// but this was the only approach I could find working for now.
ConstructorHelpers::FObjectFinder<UDataTable> AChunk::GetGridConstructJsonPath() //TODO: Find a better way to reference the different Chunks Data.
{
static ConstructorHelpers::FObjectFinder<UDataTable> DataTableFinderHill(TEXT("/Script/Engine.DataTable'/Game/LyudmilContent/ChunksJSON/chunk_data_HillChunk.chunk_data_HillChunk'"));
static ConstructorHelpers::FObjectFinder<UDataTable> DataTableFinderFlat(TEXT("/Script/Engine.DataTable'/Game/LyudmilContent/ChunksJSON/chunk_data_FlatChunk.chunk_data_FlatChunk'"));
TArray<FString> Substrings;
GetName().ParseIntoArray(Substrings, TEXT("_"), true);
FString blueprintName;
for (int i = 0; i < Substrings.Num(); i++)
{
if (Substrings[i] == "BP")
{
blueprintName = Substrings[i + 1];
break;
}
}
if (blueprintName == "HillChunk")
{
return DataTableFinderHill;
}
else if (blueprintName == "FlatChunk")
{
return DataTableFinderFlat;
}
return DataTableFinderFlat;
}
After figuring out all these intricacies related to Unreal, we now have a nice, flexible system to creating different types of chunk landforms, just by using a JSON file. (Would be interesting to try and ask ChatGPT to produce some JSON data and see what landforms it will produce)
Cells Animation:
As main part of the game is Exploration, I wanted to make the experience of it fun, ‘mysterious’ and enjoyable:
- The player never knows whats out there in the world, unless he decides to go and check.
- The world is generated and unveiled in front of the player’s eyes (or I should say: underneath his feet):
This animation functionality was achieved with the following interaction between the Chunk and the Cell class:
DevBlog_01 of my exploration/tower defense game ends here. We have a lot of stuff planned for the future and day by day we are implementing them.
The cell mesh is about to change and next thing in the planning is to implement interactables in the world: rocks to collect, trees to cut, fences and towers to build🌄:
Thanks for reading and reaching the end. Here is some cake for u 🍰.