Well, I took the plunge and uploaded a build to collect feedback
Well, I would've loved to start a devlog from the beginning, but it's never too late to start.
I've long had the idea of making a completely procedural game, inspired by great titles like Minecraft and No Man's Sky.
I've done many interesting experiments previously, testing different combinations with Marching Cubes, Wave Function Collapse, Noise Sampling, Volumetric Noise Generation, and some other home-made algorithms that I haven't come up with a fancy name for yet 😆.
Then I decided to start with the basics: a horizontal grid and some Perlin noise with customizable lacunarity and octaves. I went straight to the point and avoided spawning millions of hexagons on the memory, starting instead with creating a grid of chunks.
It was very fun to pick up pen and paper and do some geometry math to think about the shaders and the mesh generation.
After creating a good library of hexagon-based mathematics (replicated in HLSL because some of this would definitely go to the GPU), the rest was relatively mechanical. However, performance problems quickly arose, mainly (obviously) during the generation of a large number of chunks.
So after a few hours of profiling and refactoring the code to be better prepared for modifications, I opted to move the chunk generation to Jobs. Although I quickly realized that the first problem would be that the number of triangles and vertices in each chunk could be VERY different, making it difficult to use fixed-size structures, and that appending would take a toll on performance, a small idea came to mind.
✨Separate the generation into two jobs✨. The first job targeted all possible cells in each chunk (by this point, I was already planning to have depth, so each chunk had three dimensions, which meant a lot of cells to go through). So the first job was extremely simple: for each cell, generate an integer that would serve as a mask for the sides that would be generated for that cell (and zero meanings no cell required).
After this, I could create much smaller input buffers for the next job, and also, by reading a mask, I could calculate the number of vertices and triangles required for that piece of mesh.
And of course, this allowed me to create fixed-size NativeArrays for each submesh structure.
Performance was ensured.
And it was, after a bit of a struggle with the Burst compiler that doesn’t like nested lists, BlobArray came to the rescue.
The geometric shape was already working perfectly.
But everything was the same color, and assigning a material to each chunk manually looked horrible (I did it), I assure you 😅. So I set out to write a shader. As I had committed to not using assets, I didn’t want to create a texture, so I used some deterministic math to draw hexagon patterns and paint them in different colors within a 4-color palette, and also paint the sides in a grid of little squares.
It was fun to try different combinations until I hit a hexagon 12 times smaller than the mesh hexagon to have a seamless grid parallel to the general grid.
At that moment, there was a lot of math flying around to combine the sub-hexagons with those of the neighbor (since clipped parts looked ugly).
Until at one point, I simply thought of sampling with the world coordinate instead of the UV (on the top face), and as if by magic, it worked perfectly at first try.
Then I spent an entire day polishing details, creating useful little systems to sample multiple color palettes and be able to create them from a scriptable object capable of creating a texture to load into the material.
This way, I managed to make the entire terrain share the same material.
I knew performance would thank me in the future...
Taking advantage, of course, of the super simplistic art with few colors being sufficient, otherwise, I would have had to design a much more complex texture swap system.
Lastly, I leveraged the world's shape by generating a collision system that avoided using convex mesh colliders.
It simply consists of a pool of hexagon-shaped colliders that "surround" each physical entity (for now, only the player) in the solid cells.
I didn't notice a significant impact on performance with this because there was only one player, but I ran several simulations with spheres bouncing around, and there I found a significant performance difference in favor of the hexagon pool over Unity's mesh collider.
For now, that's it. I'm already thinking about what I'll implement next weekend.
It's been a very long entry, I know, so thank you very much from the bottom of my heart for reading this far 🥰
Of course, any feedback or ideas you have are welcome!
See you in the next entry 👋
Files
Get Sixagon
Sixagon
A minimalist game where you relax, explore, and unleash your creativity
Status | Released |
Author | Marquitos |
Genre | Adventure |
Tags | 3D, First-Person, Pixel Art, Procedural Generation, Retro, Unity |
Leave a comment
Log in with itch.io to leave a comment.