Generate the same trees in the same seed, if there is space, procedural generation

Godot Version

4.4.beta1

Question

I’m working on generating random trees in my game and I want them to always appear in the same locations when using the same seed. I’ve used multithreading to optimize performance, and I’ve split the world into chunks. I’m running into an issue where I need to make sure that trees are only generated if there’s enough space, without being affected by the random order of threads.

I tried to do a check that looks at the area from -1,-1 to 1,1 around a potential tree location to see if there’s already a tree at that location. But it didn’t work due to the random order of threads, causing the selection of the tree that was close to others to be random.

Here is my script:

extends Level

@onready var tilemap: TileMapLayer = $"tilemap - floor - 0"

var number_of_chunks := 3
var cells_in_chunk := 20
var semaphore := Semaphore.new()

@export var noise : Texture
var rng := RandomNumberGenerator.new()

func _ready() -> void:
	super()
	for x in number_of_chunks:
		for y in number_of_chunks:
			var chunk_coords := Vector2i(x,y)
			load_chunk(chunk_coords)

func load_chunk(chunk_coords : Vector2):
	var thread_numb := 0
	for x in cells_in_chunk:
		for y in cells_in_chunk:
			var coords := Vector2(x,y)
			var thread := Thread.new()
			var final_coords := coords + (chunk_coords * cells_in_chunk)
			rng.seed = noise.noise.get_noise_2d(final_coords.x,final_coords.y) * 5000
			thread.start(set_cell.bind(final_coords,thread,rng.randi_range(0,100)))
			thread_numb += 1
	for i in thread_numb:
		semaphore.post(4)
		if !is_inside_tree():
			return
		await get_tree().process_frame

func set_cell(final_coords : Vector2, _thread : Thread,tree_chance : int):
	semaphore.wait()
	var atlas_coords := Vector2(0,0)
	if tree_chance > 50 and tree_chance <= 60:
		#spawn tree
		atlas_coords = Vector2(4,0)
	tilemap.set_cell.call_deferred(final_coords,1,atlas_coords)

Hi.
I wonder how important it is to have “true” randomness in the first place. I mean you could precalculate a tree distribution map with a poisson disk algorythm. Then based on the seedyou could rotate this map (per chunk) and remove a bunch of trees. This would be really fast and ramdom enough for the human eye … i think.

2 Likes

Yeah, I agree - use a distribution that can’t overlap. With your current constraint that would probably be too grid-like, but what I did for my forest generator was to use a regular grid with each point offset, then remove some of them based on noise or seeded random chance. Looked fine from a first person perspective.