I don't understand TileMapLayer setcell()

Godot Version

4.3

Question

Update:
Edited to format code snippets.
Also, while writing out my question I actually solved my issue, kind of, by just hard coding the source_id and alternate_tile. I would still appreciate anyone taking the time to glance at this to help me understand how this works and how I can possibly get these values dynamically.

I’m making a game that will use this level editor after I get the bugs worked out of it and the game will have multiple tile sets and atlas’ so generating the source_id, atlas_coords, and alternate_tile by code would be nice.

End of Update

Hello everyone, first post here. I have several questions but I’ll try to keep it as brief as possible (I fail). I have read everything I can find, including the docs, and I still can’t understand this method.

I’m following a tutorial to build an in-game level editor.

The thing is, this tutorial is in Godot 3.x and I’m trying to update it for 4.3 to use modern methods etc. In previous versions of Godot all you needed to set a cell were the x,y coords and a tile ID iirc. Now we also need source_id, atlas_coords, and alternative_tile. I think I have a clunky brute force method for getting the atlas coords but I’m not even sure I understand what source_id or alternative_tile are asking for.

Like, the atlas_coords are a vector2i but the alternative_tile is an int. I would have expected the alternative_tile to also be a vector2i that points to a different tile as an alternate. So, I think I don’t understand what it actually is. I just have it set to 0 so that the code works but I have no idea why this works or what it means. Can anyone explain?

I don’t know what the source_id is either. Is this the atlas id? I’m assuming it is.

I know I can get both of these values by looking at the map, selecting a specific tile, and then hard coding the values but I want to get them dynamically so that a level can be built in the game by the player.

As far as I can tell, the only methods that can get these values would require a tile to already be placed in the map. I’m trying to get these values so that a tile can be chosen by the player and then placed in an empty map.

I would like to be able to get these values by an id like I have set up for the item selector.

bandicam 2024-09-23 11-09-26-302

In particular, I don’t really like this array of atlas coords right below this paragraph. It feels like a brute force method. Is there a better way to do this? As I mentioned above, the game will have multiple tile sets so it might not always have 9 coordinate values like this.

# array of vectors that correspond to tile atlas locations
var tile_atlas = [Vector2i(0, 0), Vector2i(1, 0), Vector2i(2, 0), 
Vector2i(0, 1), Vector2i(1, 1), Vector2i(2, 1), 
Vector2i(0, 2), Vector2i(1, 2), Vector2i(2, 2)]

Set Cell Docs:

My scripts:
item_texture.gd:

extends TextureRect

@export var this_scene: PackedScene
@export var tile:bool = false
@export var tile_id = 0

@onready var object_cursor = get_node("/root/main/Editor_Object")
@onready var cursor_sprite = object_cursor.get_node("Sprite2D")



func _ready():
	connect("gui_input", Callable(self, "_item_clicked"))
	pass # Replace with function body.


func _item_clicked(event):
	if(event is InputEvent):
		if(!tile):
			if(event.is_action_pressed("mb_left")):
				object_cursor.current_item = this_scene
				cursor_sprite.texture = texture
				Global.place_tile = false
		else:
			if(event.is_action_pressed("mb_left")):
				Global.place_tile = true
				Global.current_tile = tile_id
				cursor_sprite.texture = self.texture
		
	pass

Editor_Object.gd:

extends Node2D

var can_place = true
var is_panning = false

@export var cam_pan_speed = 10
@export var cam_zoom_speed = 0.2

@onready var level = get_node("/root/main/Level")
@onready var cam_container = get_node("/root/main/cam_container")
@onready var cam = cam_container.get_node("Camera2D")
@onready var tile_map_layer:TileMapLayer = level.get_node("Tiles")

var current_item

# array of vectors that correspond to tile atlas locations
var tile_atlas = [Vector2i(0, 0), Vector2i(1, 0), Vector2i(2, 0), 
Vector2i(0, 1), Vector2i(1, 1), Vector2i(2, 1), 
Vector2i(0, 2), Vector2i(1, 2), Vector2i(2, 2)]

# Called when the node enters the scene tree for the first time.
func _ready():
	cam.make_current()
	pass # Replace with function body.


# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta):
	global_position = get_global_mouse_position()
	
	if(!Global.place_tile):
		if(current_item != null and can_place and Input.is_action_just_pressed("mb_left")):
			var new_item = current_item.instantiate()
			level.add_child(new_item)
			new_item.global_position = get_global_mouse_position()
	else:
		if(can_place):
			if(Input.is_action_pressed("mb_left")):
				place_tile()
			if(Input.is_action_pressed("mb_right")):
				remove_tile()
	
	move_editor()
	is_panning = Input.is_action_pressed("mb_middle")
	pass

#TileMap Methods
func place_tile():
	var clicked_cell = tile_map_layer.local_to_map(tile_map_layer.get_local_mouse_position())
	var atlas_coords = tile_atlas[Global.current_tile] 
	tile_map_layer.set_cell(
		clicked_cell, 
		0, 
		atlas_coords, 
		0)

func remove_tile():
	var clicked_cell = tile_map_layer.local_to_map(tile_map_layer.get_local_mouse_position())
	var atlas_coords = tile_atlas[Global.current_tile]
	tile_map_layer.set_cell(
		clicked_cell, 
		0, 
		atlas_coords, 
		-1)

#General Editor Methods
func move_editor():
	if(Input.is_action_pressed("w")):
		cam_container.global_position.y -= cam_pan_speed
	if(Input.is_action_pressed("s")):
		cam_container.global_position.y += cam_pan_speed
	if(Input.is_action_pressed("a")):
		cam_container.global_position.x -= cam_pan_speed
	if(Input.is_action_pressed("d")):
		cam_container.global_position.x += cam_pan_speed
	pass

func _unhandled_input(event):
	if(event is InputEventMouseButton and event.is_pressed()):
		if(event.button_index == MOUSE_BUTTON_WHEEL_UP):
			cam.zoom += Vector2(cam_zoom_speed, cam_zoom_speed)
		if(event.button_index == MOUSE_BUTTON_WHEEL_DOWN):
			cam.zoom -= Vector2(cam_zoom_speed, cam_zoom_speed)
	if(event is InputEventMouseMotion and is_panning):
		cam_container.global_position -= event.relative / cam.zoom

This is how I use it https://www.youtube.com/watch?v=7_fwNiZL0xY&t=59s

Hello,
Source_id is the index of the texture in tileset.
Atlas_coord is the position of the tile in the source texture
Alternate_tilr is the index of the alternative tile. You can create an alternative tile by right clicking it. It create a tile with different parameters but same texture.