Biomes handling

Godot Version

4.4.1

Question

I’ll try to explain, bear with me please.
I’m planning a 3d game with a huge open world that will be somewhat 2d. The game itself is 3d, the ground consists of continuous quads(billions of those), with x, y, z vertices. So it can have hills and stuff, but the player and objects will always move along with the ground, no jumping, flight etc.
The game will load the world by chunks of 100x100 9 tiles around the player. The ground itself can be interacted with(dig or drop soil to change certain vertex elevation, no holes)
That would probably be a mesh creation with height maps as a source for z vertex(or y? the elevation one)
But the world will also have biomes. Forests, grasslands, mountain etc. Each biome will spawn different things on runtime like foragables, creatures, when approached by a player and loaded. Each biome also has a different allowed movement speed for the player. So I need to check what biome player is currently in and what biome each “tile” belongs to, right? What are ways of doing it?
One more thing, the world will NOT be generating randomly as we go. It will be pregenerated and stored both elevations and biomes with permanent objects like trees and bouilders.
When I’m trying to google it’s always about biomes procedural generation, not about biomes handling.
How do I approach the matter? I create an additional tilemap for biomes along with terrain mesh? I don’t create terrain mesh at all, leaving ground to tiles? Will it look like a connected ground with lighting and reflections? Do I then check the position of the player to get the biome and go through each tile one by one to determine spawning stuff?
Or do I create some form of collision mesh for each biome and check player location like that? Is it better than constantly checking player pos for when the biome underneath them changes? And I still go through each tile one by one for spawning, right?
Also, how do I store it? Images? Loading and saving those as needed? Heightmaps only or biomes too? Or is ini file/resource better for that?
Or, maybe, there is some other way of doing all of that that I don’t know? I’m sorry if I’m asking something stupid, I’m still learning.

Store the biome information in an array defined in a custom resource class. You don’t need anything else.

What biome information exactly? Coords of each tile that belongs to it? Then I load them when the chunk is loaded and do what? Create tiles to then check for spawning and player position? The storage method itself is not the main issue here, it’s the approach of how to handle it and, coming from it, what to store exactly.

Well it’s your game, only you know what you need to store.

Regardless of whether the world is tile based or not you can always maintain a 2D array of certain “resolution” to store whatever you need per field. You can call this “biome map” and think of it as a bitmap, just that each “pixel” holds some data that is not color. If you with you can have a data structure for each square and populate it with all kinds of information. Such data structure can be implemented as another custom resource class.

When reading, calculate a square into which your real world point falls and use the stored data there.

Here’s an example:

class_name BiomeInfo extends Resource
	enum{GRASS, DESERT, SEA}
	var terrain_type = GRASS
	var precipitation := 1.0
	var wind := 1.0
	var foliage_density := 1.0
	# etc
class_name BiomeMap extends Resource
	var map: Array[BiomeInfo]
	var size: Vector2i
	var tile_size: Vector2
	func get_info(world_position: Vector2):
		#implement
	func set_info(tile: Vector2i, info: BiomeInfo):
		#implement

So it’s what I initially thought except for storage solution?
When the chunk is needed to load:

  1. 2d tilemap is created with tiles being assigned corresponding biome enum loaded from res
  2. Terrain mesh is created with same coords as tilemap beneath it but with a height coordinate loaded from resource as well, right? Then each quad is assigned corresponding shader of the biome of the tile underneath it.
  3. Permanent objects loaded from resource for that chunk and created on terrain mesh.
  4. Spawner goes through each tile in the 2d tile map checking if it’s not taken by permanent object and if rand spawn hits - spawns corresponding object on the terrain above it.
    What about player biome speed? Is player position constantly read in the process func and checked for what tile it’s above? Or is it some form of “entered” signal situation?

Reading your comment - so no tile map is needed really, just a resource that is read from according to needed coordinates? Yeah, that sounds much better xD
So it’s loaded from the file instead of creating tilemap step and then the rest is the same?

Yep, you don’t need to drag along any tilemaps or other nodes. Maintain a corresponding resource for each chunk, load when needed and retrieve the info.

The only “tricky” part is properly converting coordinates between world space and biome map space. This code can be implemented in the resource class itself. So the usage becomes very convenient. You just load chunk’s resource and ask it to give you proper biome info for a wanted world coordinate.

For the player, you can read every frame. I don’t think this will affect performance in any way. It’s just an array lookup with a bit of arithmetic.

If you end up with many entities needing to read it in realtime, and it shows up as a performance bottleneck, you can read only when entity enters a new position in the biome map. So yeah, some kind of “entered” thing.

You can also make the size of a single “tile” in the biome map as large or as small as you like. So there’s a room for eventual optimizations there as well.

Thanks! That helped a lot!

What do you mean by changing the size of a single tile in the biome map? The idea is that a single world tile will be 2m x 2m in godot units, and biome tiles will be the same size, so one chunk of 100x100 will be bigger than drawing distance of the player for objects besides terrain itself. The game will be with top down camera(isometric but adjustable).
All that with normal scale so player model will fit in 1 tile with room to spare. I am contemplating making tiles 1x1m and making player 0.5 scale of normal, but I’m afraid having non-usual scale will bite me in the ass later down the line with lighting or something else.

Right, if your system is generally tile based then it makes perfect sense that a biome “tile” corresponds to a map tile. However, with a system I described above you’re technically not constrained to it. You can easily have one biome tile covering 3x3 area of world tiles for example, if you want to optimize the total number of biome tiles… But yeah, best to start with 1:1 relation.