Set_cell() doesn't set tiles

Godot Version

4.3

Question

I’m trying to make a save system with JSON and trying to get the tilemaps to save and load has been quite a struggle. This is my current code for it:

func save_tile_map_data() -> void:
	current_save.tilemaps.tiles = var_to_str(Global.grid_helper_component.dirt_tilemap_layer.get_used_cells())

func load_tile_map_data() -> void:
	var tiles = str_to_var(current_save.tilemaps.tiles)
	for tile in tiles:
		Global.grid_helper_component.dirt_tilemap_layer.set_cell(tile)
func load_game() -> void:
	var file : FileAccess = FileAccess.open(SAVE_PATH + "save.sav", FileAccess.READ)
	var json := JSON.new()
	json.parse(file.get_line())
	var save_dictionary : Dictionary = json.data as Dictionary
	current_save = save_dictionary
	Global.game_controller.load_world_scene(current_save.scene_path)
	load_date_time()
	load_player_position()
	load_tile_map_data()

My Issue is that despite the code running perfectly fine without throwing any errors, it does not load any of the tiles that it saves.

Does the save data look right? If you load the data and print it to the log, does the data look right?

func save_tile_map_data() -> void:
	current_save.tilemaps.tiles = var_to_str(Global.grid_helper_component.dirt_tilemap_layer.get_used_cells())
	print("saved tiles:", current_save.tilemaps.tiles)

func load_tile_map_data() -> void:
	var tiles = str_to_var(current_save.tilemaps.tiles)
	print("loaded tiles:", tiles)
	for tile in tiles:
		Global.grid_helper_component.dirt_tilemap_layer.set_cell(tile)

Yeah, looks fine to me.

You are only saving the coordinates of the cells, not the tiles each coordinate is using.

You will need to save the TileMapLayer.tile_map_data and load that

I tried that but I didn’t quite figure out how to make it work. But now I tried to set it the same way I place the tiles I want to save and load in the first place, but that doesn’t work either and I don’t know why

func save_tile_map_data() -> void:
	current_save.tilemaps.tiles = var_to_str(Global.grid_helper_component.dirt_tilemap_layer.get_used_cells())

func load_tile_map_data() -> void:
	var tilemap_layer: TileMapLayer = Global.grid_helper_component.dirt_tilemap_layer
	var tiles = str_to_var(current_save.tilemaps.tiles)
	tilemap_layer.set_cells_terrain_connect(tiles, 1, 0, true)
	BetterTerrain.update_terrain_cells(tilemap_layer,tiles)

.get_used_cells() gives you an array of cells that have been used. So, if you have a tilemap that looks like:

. . . . . . . .
. x x x . . . .
. x x x . . . .
. . . . . . . .

You’ll get something like:

[(1, 1), (2, 1), (3, 1), (1, 2), (2, 2), (3, 2)]

That tells you which cells in the tilemap are used. You still need to make note of what tile is in each. For that, you want to call .get_cell_atlas_id() or possibly even .get_cell_tile_data() on each cell in that list.

1 Like

Good news: I get it now
Bad news: this somehow still doesn’t work (it also doesn’t sit under another layer or spawns out of view, just absolutely nothing)

func save_tile_map_data() -> void:
	var tilemap = Global.grid_helper_component.dirt_tilemap_layer
	for cell in tilemap.get_used_cells():
		var tile_info = {
		"tile_position": var_to_str(cell),
		"source_id": var_to_str(tilemap.get_cell_source_id(cell)),
		"atlas_coords": var_to_str(tilemap.get_cell_atlas_coords(cell)),
		"alternative_tile": var_to_str(tilemap.get_cell_alternative_tile(cell))
		}
		current_save.tilemaps.append(tile_info)

func load_tile_map_data() -> void:
	var tilemap = Global.grid_helper_component.dirt_tilemap_layer
	for tile_info in current_save.tilemaps:
		var tile_position = str_to_var(tile_info["tile_position"])
		var source_id = str_to_var(tile_info["source_id"])
		var atlas_coords = str_to_var(tile_info["atlas_coords"])
		var alternative_tile = str_to_var(tile_info["alternative_tile"])
		tilemap.set_cell(tile_position, source_id, atlas_coords, alternative_tile)

What exactly did not work with TileMapLayer.tile_map_data?

It seems to work just fine:

extends Node


@onready var src_tilemap: TileMapLayer = $SrcTilemap
@onready var dest_tilemap: TileMapLayer = $DestTilemap


func _process(delta: float) -> void:
	if Input.is_action_just_pressed("ui_accept"):
		var data = src_tilemap.tile_map_data
		dest_tilemap.tile_map_data = data

1 Like

same issue as with set_cell() it just doesn’t do anything, even tho the issue runs with errors

func save_tile_map_data() -> void:
	var tilemap = Global.grid_helper_component.dirt_tilemap_layer
	current_save.tile_data = var_to_str(tilemap.tile_map_data)

func load_tile_map_data() -> void:
	var tilemap = Global.grid_helper_component.dirt_tilemap_layer
	tilemap.tile_map_data = str_to_var(current_save.tile_data)

edit: I got it to work! I changed the code a little more and then realized it was being overwriten by another load function. Thank for the help everyone, I learned a lot!

func save_tile_map_data() -> void:
	var level_name = Global.game_controller.current_world.name
	var tilemap = Global.grid_helper_component.dirt_tilemap_layer
	var file = FileAccess.open(SAVE_PATH + level_name + "_" + "tile_data.sav", FileAccess.WRITE)
	var array = tilemap.get_tile_map_data_as_array()
	file.store_buffer(array)

func load_tile_map_data() -> void:
	var level_name = Global.game_controller.current_world.name
	var file = FileAccess.get_file_as_bytes(SAVE_PATH + level_name + "_" + "tile_data.sav")
	var tilemap = Global.grid_helper_component.dirt_tilemap_layer
	tilemap.set_tile_map_data_from_array(file)