Posted September 20, 2025 by Yunasawa
🔶 Better maintainable approach
For biome calculation, instead of the classic approach of using a giant switch-case to call the right static method for each biome, I wanted something more maintainable and efficient.
The downside of the old approach is obvious: every time I add or modify a biome, I would have to go back and update the switch-case. That’s messy and hard to scale.
To solve this, I used a [BiomeProducer(int biomeID)]
attribute. Each biome has its own static class decorated with this attribute and implements a voxel-processing method with a consistent function signature. Using Reflection, I can automatically collect all these methods and bake them into a NativeHashMap
.
The neat part is that Burst compiler supports FunctionPointer<T>
, which means I can turn those static methods (grabbed via Reflection delegates) into actual function pointers that Burst can compile and call directly. This makes the biome producer system both powerful and extensible: adding a new biome is as simple as writing a new class with the attribute—no need to touch the core code.
For example:
[BurstCompile, BiomeProducer(BiomeType.A)]
public static class ABiomeProducer
{
[BurstCompile]
public unsafe static ushort GetVoxelValue(int3* p, int seed)
{
return 0;
}
}
As you can see, the producer functions can only use primitive types or pointers as parameters, to stay compatible with Burst.
🔶 Proceducer implementation problem
While working on biome producers, I realized that constantly writing methods with the exact required signature was a bit of a hassle. Using a abstract class instead of attribute could solve part of the problem, but it introduced another issue: sometimes I would forget to add the [BurstCompile] attribute.
To fully solve this, I decided to leverage Roslyn analyzers. I created a ruleset that:
On top of that, I implemented a fix provider using source generation to automatically add or correct the code when these errors occur.
Now, all I have to do is declare a [BiomeProducer] attribute, and with a single click, all the necessary methods and attributes are generated automatically. This ensures my code always follows the standard, while also saving a huge amount of time when extending the biome system.