02 - Initial World & Region Generation

Starting with Word Generation?

Upon reflection, starting the project by diving into the rabbit hole that is procedural terrain generation was a mistake. I’ve realised now that I need to focus on gameplay first. However, I did have a few reasons for starting with world generation at the time.

Firstly, on the technical side, I wanted to ensure that the back-end data structures of the game were robust enough to scale to storing potentially thousands of characters and an unknown number of interactive objects. At the time, I felt that I needed to have a working back-end (a database of what objects were at which positions) before I could work on the front-end (gameplay). That turned out not to be the case, but I suppose I had to learn that lesson the hard way.

The next reason was that it’s important to me that the world always feels more important than the player’s current avatar. I don’t want the player to feel like they are the centre of the whole world, but rather that they are only one character in a larger ecosystem. The game will most likely feature permadeath, but when the player’s avatar dies, I don’t want the player to feel they had such an impact on the world that they need to start over just to re-experience core parts of the game. Instead, the non-player characters should fill any void left behind by the player’s dead avatar.

As I said earlier, I now see that procedural generation is not the priority at the moment, but it remains very much a core part of the game that I will return to later.

Non-Infinite Terrain

I’ve decided that the world shouldn’t be infinite (despite that seeming to be the far more common approach in procedurally generated worlds). My thinking for this is the following:

  • Firstly, my concern with an infinite world is that it makes any resource worthless over a long enough play time. If new terrain can always be generated, then there is always a chance of generating more of any given resource just by travelling to new areas.
  • Secondly, a lot of the emergent gameplay I hope to be able to implement will require a constant simulation of the large aspects of the world and all the characters within it. If the player can endlessly explore, the amount of data that needs to be fed to that simulation could also grow indefinitely. And all that sounds like a headache I’d rather avoid.
  • Lastly and most importantly, it’s going to be crucial to get the player to invest in the emergent narrative of their own unique world. I hope that the game will be able to create so many impactful emergent narratives that the player will become invested in the world and care about any changes that occur because they know those changes could impact the characters from those narratives. All of that is to say that I think that having a non-endless world will give the conflict of the world meaning as it’s not like characters can just run off to newly loaded chunks.

World Cell System

As you are probably thinking, the scope of this game is massive and it’s probably going to take far more time than I can even imagine. To try and make that time just a tiny bit shorter, I’ve decided to split the world up into individual regions (play spaces that can be loaded and unloaded one at a time), rather than try to tackle an endless and seamless chunk streaming system. This will mean that the player will have to go through a (hopefully very short) loading screen any time they travel from one region to another, like in RimWorld or Dwarf Fortress. Both these games have proven to be very fun and successful without the need for a continuous open world, which is very reassuring. My hope is that each region might end up feeling slightly like a room in a rogue-like, and I am also hoping that it adds charm of the retro, 2.5D, Pokémon-inspired art style I’ve chosen.

That essentially means that there are two data sets for me to work with, the overworld and the currently loaded region. The overworld is the world as a whole, and the region is the space where the gameplay takes place. The overworld will also be viewed by the player in the form of a map.

I mentioned earlier that one reason I focused on world generation early on was to make sure the Unity project structure and the underlying data structures were robust and scalable. After a lot of thinking, I’ve come up with what I think is a structure that works pretty well. I plan on talking about this in a lot more detail later, but essentially two scriptable objects in the engine that store all the data currently loaded. I’ve made them scriptable objects so they can easily be accessed by other Unity components.

image

World Generation Algorithms

If you are interested in finding out more about how the overworld generation works, I’d say just check out this page from my portfolio website. Although I’ve rewritten all the code from scratch, the basic concept and types of algorithms are still the same. But either way, if you are familiar with basic procedural generation techniques, I am sure you can probably guess how I’ve done it.

image

Initial Points of Interest

A world would be pretty boring without anything in it, which isn’t what I am going for. I want there to be an almost endless amount of emergent gameplay as possible, and that is going to require a lot of places for things to happen in. I am calling any part of the world that contains anything more than wilderness a point of interest; this will be the obvious things like cities and castles, but also other things like the remains of a long forgotten battle, a hidden tomb or even potentially temporary places like an NPC’s campsite.

For now, the locations of points of interest are generated with a near-even distribution between one another using a Poisson-disc sampling algorithm. But when I eventually return to procedural generation, I will take this a lot further. Currently, the plan is to use this code to generate a very dense grid of sample points. And then use an expanded version of my history generation algorithm to choose which sample points turn into actual points of interest. I think this will be an essential step in giving the game world some meaning for the player to use as a base for their own emergent narratives.

However, I haven’t even started the generation algorithms that will spawn the different types of POIs within the region scene. I will make sure to do that before going back to something abstract like history generation (that seems to be something I keep needing to remind myself).

image

Region Generation

Voxel Engine

image

A while back, I wrote a voxel engine inside Unity and thought it would be a perfect base for the terrain in this game. It’s not that I am having mining being a core part of the game or anything (although it might be something I explore one day). But, instead, the decision to use it was made because I wanted the terrain to have that 3D tile look is common in a lot of retro 2.5D games (example below).

image

Tile-based Terrain

As I just mentioned, I want to have my terrain look similar to the image above. I love the look of it, but it also works well with the grid-based back-end of the game. Here is a brief explanation of how the system works.

First, I created the tile models in Blender (a 3D art software). As you can I see, I’ve created the same model for each possible rotation, as at the moment I have no way to handle tile rotation, but again that is something I might add one day.

I also UV unwrapped the models on a 16-by-16 grid to match the retro pixel art style I am going for. Some of the UVs are not perfectly seamless, which bothers me way more than it should. But neither are the textures in the reference image above, so it will do.

Then lastly, I painted the colour of each vertex (not visible in the image) depending on what type of texture that part of the model would use. For example, any vertex that is related to an upward-pointing face of a model is green, and the sides are red. This is later used to assign which texture goes where. The major downside of this system is that it means each tile model has more vertices than is necessary, but right now I am not sure of another way to calculate the textures of each tile (due to underlying requirements of the voxel engine).

image

Then in the game engine, I created an editor tool that takes the mesh objects from that Blender file and uses it writes a C# class that contains all the data for each tile type. This is done to improve run-time performance. Below is an image of the tool and the code it creates. The C# class is essentially a static dictionary that can be used to get the mesh data for any of the possible tile types. In the future (if I stick with the vertex colour approach), I will improve this code so that it converts the vertex colour data into face data. Because, at the moment, it is done at runtime every time a tile is placed, even though it will always be the same for each tile type, far from efficient. However, I don’t plan to use vertex colours long term, so I’m okay with it for now.

image
image

At run time, when building the chunk mesh, a check is run to determine if the block should be a voxel or a tile.

image

Above is the current result. At the moment, I am just using free art from Kenney’s site but will change it all at some point to be something more reminiscent of the retro style I am going for.

If the block has neighbors in a series of certain places, then it just adds a regular voxel to the mesh. However, if it is a voxel, it will only draw the faces of the cube that are visible.

Otherwise, if the block is a tile, another function runs to determine which type and then draws that tile. I’ve decided not to include a tile type for every possible combination, instead, it will only be used for the top block in the chunk. Any underground blocks will always be voxels; this decision reduces the total possible number of tile types from 256 down to less than 30 (Or at least I am pretty sure it does).

One other major improvement I am planning is removing the Enums from the system; currently, each tile type in the dictionary uses an Enum as the key value. While I am still making sure the correct tiles always spawn in the correct place, it’s useful to have a readable value in the dictionary. However, performance could be improved by replacing the Enums with a Bit Mask; so 00000001 might represent the first tile type, and 00000010 might represent the second.

Terrain & Ocean Generation

I also thought it was very important to make sure that the terrain within the region matched the type of terrain on the overworld map. Again, this shouldn’t have been a priority at this time, but that is clearly one of the development’s core themes at this point. But I made this system that adds oceans to the edge of the region terrain when the neighboring world cell position is an ocean biome.

image

This system was made before I added the tile generation, so it is a bit broken at the moment, but I thought I’d mention it anyway. You can also see in some of the terrain tiles that the tile set is incomplete at the moment; it’s taking absolutely ages to find and fix all the edge cases that can come up.

I think this screenshot is also a good place to point out another thing I am thinking about; I am not sure how to handle the edge of the regions. I really don’t like how the play space abruptly ends; in the future, I might add some ‘border’ chunks, which would be terrain that can’t be accessed. But that then opens up another problem of how you make it clear where the play space ends and the border begins. Anyway, that’s a problem for another day.

My Problem with Pre-determined Biomes

When it came to climate and terrain generation, I never really considered anything other than going with pre-determined biomes. By that I mean having a finite list of biome types in the game and each biome type having its own set of attributes and parameters that can be fed to generation algorithms as needed (e.g. terrain shape and types of vegetation). But since developing the biomes currently in the game, I’ve gone off the idea for two reasons:

The first is user experience-focused. The problem is that once the player has been to a biome, they’ve seen it. They can never experience that biome for the first time again, and every time they find another copy of that biome, there is no real sense of discovery, or at least it is likely extremely diminished. They might never see that biome generated in the exact same way twice, but the differences between each variation are likely to be minor at best.

The other reason I’ve gone off this approach is slightly more practical. On the development side, there needs to be some kind of set of ‘biome data profiles’ (a data set that holds all the different parameters for each of the different biome types). Currently, I only have a total of nine biomes in the game and managing these data sets has proven to be a pain. This is mainly because any time I make a change to any of the generation algorithms, it then requires me to go through call of the current eleven biome types and retune the parameters, which takes ages.

I’ve realised now that working on any of the procedurally generated parts of the game at the moment is not the priority; right now, I need to focus on the gameplay. But when I do go back to working on procedural generation, I am thinking a more dynamic system would be ideal. The basic idea would be to still use the same parameters that I’ve already simulated (e.g altitude, temperature & rainfall) but in a more granular way. For example, the altitude would probably dictate the general shape of the terrain, the level of rainfall might effect the density of plants, and the temperature would determine the type of plants. Then the type and density of plants could determine the type of wildlife. And so on and so on… My theory is that this might result in more varied climates that not only better resemble the complexities of our own planet but also keep that feeling the player’s feeling of discovery alive for longer.

I am getting excited just thinking about it but need to remind myself to remain focused on gameplay for now. In the next development update, I will explain the steps in developing the systems that will enable the systematic gameplay I aim to achieve.