Isometric procedurally generating world

Godot Version

4.5.1

Question

tried to generate a 16x16 later also 32x32 an mayby 64x64 tile large map procedurally from a isometric sprite using a 2D sprite. For that, I used the following code. But somehow the hills look very weird. I’ve already played around with some variables in the codes. But it didn’t get any better. Later, I still want to look for small lakes, and I’m slowly getting very desperate. Could someone give me a tip?

extends Node2D

@export var tile_size: Vector2 = Vector2(32, 32)
@export var world_width: int = 16
@export var world_height: int = 16
@export var max_height_blocks: int = 3
@export var noise_resource: Resource

var grass_texture: Texture2D

func _ready():
	grass_texture = preload("res://assets/tiles/grass_block.png")
	generate_world(world_width, world_height)

func generate_world(width: int, height: int):
	var heights = []

	for x in width:
		heights.append([])
		for y in height:
			heights[x].append(0)

	for x in width:
		for y in height:
			var left_height = 0
			var top_height = 0 

			if x > 0:
				left_height = heights[x - 1][y]
			if y < 0:
				top_height = heights[x][y - 1]

			var new_height

			if left_height == top_height and left_height < 3:
				if randi() % 100 < 10: 
					new_height = left_height + 1
				else:
					new_height = left_height
			else:
				if left_height != top_height and left_height < 3 and top_height < 3:
					new_height = max(left_height, top_height)
				else:
					if left_height == 3 or top_height == 3:
						new_height = 3

			new_height == clamp(new_height, 0, 3)
			heights[x][y] = new_height

			var screen_x = (x - y) * 16
			var screen_y = (x + y) * 8 - new_height * 16

			create_tile(Vector2(screen_x, screen_y))


func create_tile(pos: Vector2):
	var tile = Sprite2D.new()
	tile.texture = grass_texture
	tile.position = pos
	add_child(tile)

Hi,

It’s very hard to help as “procedurally generating a world”, or “the hills look weird” can mean a lot of different things. The best thing you could do is provide some examples of games that generate world similar to what you’re trying to achieve, that way, we could try to give you some hints or algorithms to try out.

Here are a few notes though:


Offsetting the tiles on the y axis works fine for height, but if the elevated tiles are exactly the same as the non elevated ones, you’ll have trouble with perspective, kind of an optical illusion.
Here, it seems there’s two elevation levels, but in reality it’s just a “corridor”:


A thing you could try is to change the color of the elevated tiles. Even a slight tint change could already help a lot to understand the layering of your world.


I see you declared a noise texture, probably to use as a height map. This is a great way of generating organic worlds procedurally, as you can just rely on a pre existing Godot feature to generate the random data, and you simply convert that data to your tiles (converting a noise texture data to a tilemap is theorically fairly easy).

Using noise come with some issues, for instance the difficulty of having some control over the randomness, not being able to iterate properly for more complex worlds, but it can be a good place to start.


Generating worlds like this is usually done by adding multiple steps of generation. For instance, you could start by generating the water and dirt tiles using a dedicated noise. Then, you generate the height/hills only on the dirt tiles (as water tiles will always be on ground level). Then, you need to lower the dirt tiles that are neighbours to water tile, to have a smooth transition between water and dirt. Then, you add some snow on the tiles that are elevated by at least 3 levels of height (considering you have many height levels).

There is no algorithm that will generate a world that’s a perfect fit for your game. You need to experiment with different algorithms, different techniques, and find out what combination of steps and computations works the best for you.