> A High-Performance 2D Voxel Game Engine Framework made in Unity
Disclaimer: I did not create the art assets used in this game. Most art assets were created as temporary placeholders by a friend, and the character + animations were created by this awesome person: https://rvros.itch.io/animated-pixel-hero
All of the code, gameplay design, gameplay systems, and algorithms were created by me. Total codebase is around 255k LOC.
MageForge is a solo project created in an attempt to make a Terraria/Starbound-like game. The initial goal of the project was to create a game that explored one of the unexplored voxel-based game genres: Magic.
The plan was for the game to have Terraria-like gameplay with building, crafting, mining, exploration, biome diversity, enemy diversity, and boss fights. The elements that make MageForge unique is that it will feature a RPG system complete with different classes, skills, abilities, stat points, and class-based progression. The goal is to have multiple boss fights per world, each one being defeated unlocking a new resource that generates in the world. The final boss fight unlocks a randomly generated portal in the world which allows the player to move to the next "stage" in their adventure. This will grant the player access to a brand new world, bosses, items, and resources. The player is able to freely travel between worlds.
In its current state, MageForge is a High-Performance 2D Voxel Framework for the Unity Game Engine. I started this project with the intent of designing numerous systems that would be used to ultimately built up to the game that I envisioned.
I eventually decided to forego the decision of turning the project into a fully-fleshed out game because I am a programmer at heart, and not an artist.
The result is an ongoing personal project for me, and right now it is signficantly different from what my plans are for this project. Eventually, I may reconsider my decision, and I will take the codebase to create a fully functional game out of it, but I don't have the artist talent (either from myself or other people) to make this game become a reality.
Take the direct competitors to MageForge, Terraria and Starbound.
Terraria is built using C# and Microsoft's MonoGame/XNA Framework. The entire game is built on top of these frameworks, and they are responsible for handling the barebones of the game such as rendering, and providing rudamentary apis which can built ontop of one another to achieve complex systems such as 2D Physics and Collision Detection.
Starbound on the other hand is built using a proprietary engine, and it is built using C++ and SDL 1.2.
Both of the game have a similar gameplay style, and both are arguably very fun to play for those that enjoy the game. I myself own and have played both of them. Often, I wondered how exactly the games were created and whether it would be possible for me to recreate something like what either of these games have achieved. So I started creating my own version of these games, and I did it partially to learn Unity since I've always wanted to get into more professional game development as a hobby.
I quickly realized that these games are very well made, and a lot of work had to go into them for them to not only be playable, but enjoyable. As a result, I went through around 1 year of development working with different techniques and system implementations to achieve the same type of gameplay as both of these games had. The result?
To give the reader some context for the gifs above, this game is running on (at the time of writing) 5-8 year old hardware. The game is running on a Intel i5-4690k (no OC), a GTX 1070 w/ 8gb GDDR5, and 16gb DDR3 Ram clocked @ 1866MHz.
In constrast, these gifs demonstrate the game running on relatively modern hardware (for 2021-2022 standards). This game is running on a Ryzen 5800x (with slight under-volting), a GTX 1070 w/ 8gb GDDR5, and 32gb DDR4 Ram clocked @ 4000MHz (DOCP).
Terraria maintains between 100-200 fps on the same (i5 processor) hardware, and Starbound maintains around 120 fps.
The world in the above image is 1024x1024 tiles = 1,048,576 tiles total, 32 pixels per tile, and the performance stays fairly consistent regardless of where you are located in the world (above ground and underground has little impact on performance).
Now, does the performance scale? What would happen if we were to increase the world size? The performance remains constant to what you see above.
This means that a world which is 1024x1024 total blocks will have the exact same performance as a world that has (8192 x 8192) tiles or even (16,384 x 16,384) tiles. This is because the framework uses a very clever chunk system to only load tiles that are absolutely necessary, and only keeps the data that is needed in memory. Of course, there are some tradeoffs when it comes to large world sizes, namely that the data needs to be stored somewhere (on disk), and that data needs to get periodically fetched. Putting the game on an SSD definitely helps with this and decreases occasional lag spikes.
Does the performance stay constant when the player breaks or builds blocks? Well, yes and no. Things start to get fuzzy when you factor in other systems because of the various techniques I utilize. Let me explain:
Here is a GIF of the performance when building or breaking blocks (The performance is actually highly then what is shown here because I am recording my screen which uses system resources):
The performance without recording stays at a more consistent 700+ FPS while breaking blocks. This is because the technique used to calculate lighting is GPU based (compute shaders), and so is video encoding. Both of the applications need to compete for system resources which degrades performances.
The hardest performance hit comes in the form of breaking blocks as quickly as possible. See below:
Again, with recording software, the performance takes a hit. Here is a screenshot of the performance after the video encoding process completes:
We can see that the lowest trough of the performance graph is 352 fps, which seems to indicate that the performance is unstable. This occurs because data needs to be transferred to and from the GPU to recalculate the fog of war. This introduces physical latency from the PCIe bus. The need to perform this task multiple times a second, with the addition of other applications attempting to utilize the GPU in parallel, results in GPU thrashing and degrades performance. However, one crucial piece of information is left out of this analysis: This is the absolute worst case scenerio the player or any future game developers will encounter.
From a gameplay system perspective, the performance displayed above is actually quite good overall. First, destroying 1 tile at a time is super inefficient, and chances are that the player will want to destroy multiple blocks at the same time. The performance hit of multi-block destruction is constant, since it isn't necessary to recompute the fog of war based on single tile changes. All of the blocks (say in a 3x3 grid) could be destroyed and only 1 call would need to be made to the compute shader, in the same way a single block needs a single call to the compute shader to update the fog of war.
Second, in any type of voxel-based game, there is always at least some delay between breaking a block/tile (i.e., the time it takes to mine a block). Therefore, there is no need to recompute the fog of war multiple times per second, even if only a single block is getting destroyed at a time. By adding a slight delay, the performance hit from any required fog-of-war recomputation is amortized over the delay time.
Finally, it's also worth mentioning that loading chunks have very little impact on performance. Simply falling down from the top to the bottom of the map has no performance impact. This is because the fog of war system is completely independent from the chunking system.
If this system is considered from the aforementioned gameplay perspective, the current performance is more then enough to integrate additional new systems.
Additionaly, once Unity DOTS is released, the game can be easily upgraded to the ECS component system, as well as the C# Jobs System. This will increase performance even further since multiple systems I've built can take advantage of ECS (such as chunk loading), and multithreading (such as liquid simulation, and the traditional cpu-based fog-of-war recomputation algorithm).
The definition of a gameplay system can vary depending on whether you define it as core engine tech, or whether that system itself is used for gameplay (such as inventory).
Here are a list of the current gameplay systems I have implemented:
In-depth articles on some of these topics are coming soon!
Fully functioning liquid system with multi-liquid support. Integrated pressure system based on articles written by:
http://www.jgallant.com/2d-liquid-simulator-with-cellular-automaton-in-unity/
https://w-shadow.com/blog/2009/09/01/simple-fluid-simulation/
Fog-of-War system with built in smooth lighting (GPU-based), and traditional lighting (CPU-based, multithreaded, not shown here).
Every operation performed (placing and breaking blocks) leads to an idempotent state on the occulsion map. The fog-of-war system is built completely from scratch using render textures, compute shaders, and light/dark maps.
More System Demonstrations Coming Soon!