I want to loop through the world and find any "Grass" tiles on my Tilemap. I then want to perform an action on each valid tile, like instancing a new scene.

What I'm trying to accomplish is creating any trees, rocks, etc in the world at runtime. This let's me build the background map, and generate a new environment each playthrough.

I'm new to Godot, recently migrating from BYOND. Over there, this would be pretty straightforward. Just tell the client that for every grass in the world, place a tree if a probability threshold is met:

World
	GenerateTrees()
		for(var/turf/grass/g in world)
		    	if(prob(10))
		    		var/obj/tree/t = new(obj/tree)
					t.loc=locate(g.x, g.y, g.z)

I have no clue how I'd go about doing something like this with GDscript, though... can anyone help me out?

Out of my head i think the following function should work fine. Just add this function and spawn_tree(tile_position: Vector2) to your Tilemap and set GRASS_TILE_INDEX accordingly.

func generate_trees() -> void:
 		for tile_position in get_used_cells():
 			if get_cellv(tile_position) == GRASS_TILE_INDEX:
 				spawn_tree(tile_position)

Here's what I've got... (This script is attached to my TileMap)

extends Node2D

onready var tree_scene = preload("res://testtree.tscn")

func spawn_tree(tile_position: Vector2):
	tree_scene.instance()
	
func generate_trees() -> void:
	for tile_position in get_used_cells():
		if get_cellv(tile_position) == tilemap.tile_set.find_tile_by_name("Grass"):
			spawn_tree(tile_position)

It's returning an error saying "The method get_used_cells() isn't declared in the current class"

I wasn't 100% on what you meant by adding the spawn_tree func, but I hope I got it there, and the GRASS_TILE_INDEX detail was fuzzy. The language I'm used to is a variation of C++, but it's got so much stuff built-in that I feel like coming over here to a "real language" might be a bit more of a learning curve than I foresaw =)

It's returning an error saying "The method get_used_cells() isn't declared in the current class"

Try changing extends Node2D to extends Tilemap, as that should fix the issue,

The issue is happening because the get_used_cells is a function defined in Tilemap, but the script believes it is working with a Node2D class rather than the Tilemap class. Tilemap class extends the Node2D class, which is why the script runs up to line 9, but you cannot access functions defined in the Tilemap class from GDScript without telling the script that it extends the Tilemap class.

Well... that's kind of a duh moment on my part. lol

So, here's what I have now:


extends TileMap

onready var tree_scene = preload("res://testtree.tscn")

func spawn_tree(tile_position: Vector2):
    tree_scene.instance(tile_position)

func generate_trees() -> void:
	for tile_position in get_used_cells():
		if get_cellv(tile_position) == tile_set.find_tile_by_name("Grass"):
			spawn_tree(tile_position)

func _ready():
	generate_trees()

It's no longer displaying errors, but it still isn't instancing the trees. I feel like I'm probably making another mistake that's obvious to a more experienced godot dev and I'll laugh at how easy it is to fix...

You have to add the tree to the scene. "instance" just creates it in memory but doesn't add it to the screen.

func spawn_tree(tile_position: Vector2):
    var tree = tree_scene.instance()
    tree.position = tile_position #may need to convert this to screen coordinates
    add_child(tree)

I've tried various ways to change the spawn position, but no matter what I try, I still can't get the trees to spawn. I thought maybe I wasn't referencing my grass correctly, but when I just tried to instance a tree onto every tile_position in the for argument, it didn't work then either. I'm so confused right now lol.

Here's an update on exactly what I've got in the script attached to my Tilemap:

extends TileMap

onready var tree_scene = preload("res://testtree.tscn")

    
    func spawn_tree(tile_position: Vector2):
    	var tree = tree_scene.instance()
    	tree.position = tile_position
    	add_child(tree)
    
    func generate_trees() -> void:
    	for tile_position in get_used_cells():
    		if get_cellv(tile_position) == tile_set.find_tile_by_name("Grass"):
    			spawn_tree(tile_position)
    
    func _ready():
    	generate_trees()

I'm thinking maybe the 'for' argument isn't doing something right, but I can't be sure.

The tile position is an integer index into the tile map, not a pixel coordinate position. For example, you may get a unit (1, 2) as a tile position. If your tiles were 200 pixels wide, then this would be position (200, 400) in pixels. I don't know the size of your tiles, but that is the idea.

Also, make sure you are adding the tree to the proper node, you may want to add it to a sibling node on top of the tilemap. Not sure how that works if you are adding loose objects, I haven't worked with tilemaps all that much.

3 years later