Hey, there, it’s nice to see you again!
This week we’ve started transforming our game to 3D. This means that we had to re-think some of our systems.
We’ll use some technical terms and programmer’s thoughts here, and we’ll dedicate extra-posts (different than Research Logs) to these topics in the near future!
The main part, that requires rewriting, is our map system. Currently, our Terrain and Entities were stored separately from each other. This turned out to cause quite some troubles, as references were everywhere and nowhere. Therefore, we’re going to merge these two systems when going 3D and have to change parts of them anyway.
While thinking about how to implement it, we came up with some great new gameplay ideas, however, they required a voxel map system, instead of the commonly used height-map in RTS games. Our system already used a Tilemap-approach, which is basically a voxel map, but in 2D. We thought to ourselves, “We’ll just add a 3rd axis and we’re done”. Our map was simply going to be a 2D Array of 3D Arrays. In other words, an array of chunks, which contain their local data. Each array of local data would be a struct which contains its terrain and the entities at that position. Pretty simple, right?
We wrote a quick and dirty console application to test our hypothesis. It creates a (theoretical) map with the size of 1024×1024 with a height of 256 tiles.
…Oh, we were wrong!
What you see above is a screenshot of the Visual Studio Diagnostic Tools. While we were waiting for the console application to print out “Finished”, we couldn’t but notice that the computer was getting cumbersome. Our map was quickly growing in size. As you can see, by the time we took a screenshot it already had 10 GB and it was still growing!
Why was it so big? Well, we were creating a new object for every single position on the map. Our map size resulted in a total of 268,435,456 voxels The arrays alone would already take up about 2.2 GB! In addition to all the objects created, we would have gone up to approximately 20 GB total.
We came up with a fairly simple optimization, also used in other games, like Minecraft. Instead of storing the tile, where it is, we only store its id. This id is then used for a lookup. Additionally, we store entities in its chunk instead of the full position.
Our Action-System is a good example of what happens when you plan a system from scratch without looking back to whether it still makes sense later. In detail, we differentiate between two distinct types, Team Actions and Entity Actions. A Team Action is the interface between our Unity-Client and our Unity-free Core and tells it what the Player wants to do. Entity actions, on the other hand, just describe what an Entity (Cell, Microbe, Titan) is up to do, like Attacking!
The player sends a Swarm of Microbes to an Opponent Cell thru the UI, the Client registers a TeamAction in the Core, which spawns a Swarm with the Attack-Action (scripted in Lua).
As we plan to go multi-player in the future, we need to make sure that values actually mean the same things on all devices, regardless of the processor and operating system. Therefore, we use C#’s double type wrapped in our own struct, rounding it to three digits every time it’s set!
As there are 15 digits of precision, we ended up using 13, including three decimal places, just to be on the safe side.
This helps “constraining” any value of becoming too inaccurate, without using the decimal type, which is like 10-15 times slower according to own testing.
Wrapping Things Up
We’re currently working hard re-developing our integral Map system, and once this is finished (in a few programming days), the next step is to actually display it!
But, that’s a story for after Christmas!
We wish you all Merry Christmas and a Happy New Year, and we’ll be back in the office in