Insatiate objects on specific tiles of a tilemap

Godot Version



I’ve been trying to wrap my head around automatic object-placement on specific tiles in a tile map using gdscript. I’ve read the documentation and watched all the tutorials I can find, but to no avail. I’m not using layers, different sources, terrain sets or any such thing. Only a basic tilemap from a basic tileset of five tiles.

I’ve generated a large tilemap using noise and automatically placed different colored tiles to function as the game world. The tilemap consists of five base tiles with atlas coordinates ranging from (0, 0) to (4, 0).

What I’m trying to do is instanciate a scene, say a rock, on every tile that matches a specific atlas coordinate, say (1,0).

I’m guessing I need to use something like var tiles = tilemap.get_used_cells_by_id(0, 0, Vector2(1,0)) in order to create an array of cells specific to the tile I want to place objects on. However I am drawing blanks here because never am I getting cell coordinates that match actual yellow cells in the tilemap. It seems purely random at this point.

As an added bonus, I would love to be able to place several instances of each object/scene randomly within the bounds of my tilemap cells. This is because my tilemap is designed to represent sectors of the world and are rather large (each tile is 512x512 pixels). If I could have code split each tile into a smaller grid for random object placement within, that would be awesome.

Thankful for any help on the topic.

You will get the cell id which is the position in the tilemap. For example in a 2x2 tilemap they would be from top-left to bottom-right: (0, 0), (1, 0), (0, 1), (1, 1)

Then, you’ll need to multiply that value by the TileSet.tile_size to get the visual position of that cell in the tilemap. In the example, if the tile size is (16, 16) it would be: (0, 0), (16, 0), (0, 16), (16, 16)

After that you’ll need to add the position of the tilemap node itself to get the final position of the cell if you are instantiating the scenes outside the tilemap.

This assumes that the tilemap scale and rotation are not modified.

Thank you. I was already close to the answer and your reply made me realize my mistake. The following code now correctly spawns my object in the center of each yellow tile (or cell):

func asteroid_spawner():
	var tiles = tilemap.get_used_cells_by_id(0, 0, Vector2i(1, 0))
	var half_tile_size = tilemap.tile_set.tile_size / 2  # Calculate half tile size
	for cell in tiles:
		var world_position = cell * tilemap.tile_set.tile_size
		var asteroid_instance = asteroid_scene.instantiate()
		asteroid_instance.position = world_position + half_tile_size

In this case I am in fact spawning a single asteroid in the dead center of each yellow cell. I would now like to spawn a random number of asteriods in each cell but make sure that they won’t overlap. I’ve been trying to divide each cell into subcells using code and then iterate through an array of subcells in order to place each new instance, but the task is too difficult for me to grasp at this point. Any pointers? I would like to be able to use an export variable for the number of subdivisions so that I can tweak it in the inspector later.

Thank you for all your help!