Godot Version
4.2.1, will probably upgrade to whatever newest as the project goes
Question
TL;DR is the title, but I’ll explain everything from the beginning.
As a coding exercise, I’m remaking a 2005 Chinese Flash game Tower of Sorcerer 1.4, which was based on a PC98 Japanese Title Tower of Sorcerer.
(Playthrough Video as the reference)
I did it mainly for the auto-progressing combat system, and I’m struggling but making progress consistently.
However, the overworld side of the game isn’t going well.
I got something working, but it’s not really a comfortable base to build upon, so I want to hear what people has to say about my current approach.
The goal, is to have a 11 * 11 playable area, which will have about 50 floors, swapping when player hit a trigger or teleport.
My first approach was simple. Just stack a lot of invisible 11 * 11 TileMap, and show() the current floor on demand.
It worked…for the version. I didn’t dig into it, but I think Godot “Fixed” the bug where invisible TileMap won’t have collision.
Now hiding or even DISABLE PROCESSING won’t stop walls from another floor to block player’s way.
I don’t know why should invisible stuff have collision in the first place, but okay.
Guess I’ll find another way.
So what I did after that, is I just store all the data in an array, queue_free() all the 11 *11 TileMap, and use the Array to paint the tiles onto an one and only TileMap node.
It looks like this:
extends Node2D
class_name LevelManager
signal level_changed(ascend: bool)
## Will cause issue when floor value is negetive
var levels_tilemap_data: Array
var level_names: Array
var tilemap: TileMap = preload("res://stage/levels/level_default.tscn").instantiate()
@export var current_level: int = 0:
set(new_level):
var has_level: bool = false
for level: String in level_names:
if level == str(new_level):
has_level = true
break
if has_level:
_clear_level(current_level)
_set_level(new_level)
level_changed.emit(new_level > current_level)
current_level = new_level
func _ready() -> void:
for child: Node in get_children():
if child is TileMap:
levels_tilemap_data.append(_get_level_tilemap_data(child))
level_names.append(child.name)
child.queue_free()
add_child(tilemap)
_set_level(current_level)
func _get_level_tilemap_data(level: TileMap) -> Array[Array]:
var level_data: Array[Array]
for layer: int in level.get_layers_count():
var used_cells_coords: Array[Vector2i] = level.get_used_cells(layer)
for cell_coords: Vector2i in used_cells_coords:
var source_id := level.get_cell_source_id(layer, cell_coords)
var atlas_coords := level.get_cell_atlas_coords(layer, cell_coords)
var alter_id := level.get_cell_alternative_tile(layer, cell_coords)
level_data.append([layer, cell_coords, source_id, atlas_coords, alter_id])
return level_data
func _set_level(level: int) -> void:
for tile_info: Array in levels_tilemap_data[level_names.find(str(level))]:
tilemap.set_cell(tile_info[0], tile_info[1], tile_info[2], tile_info[3], tile_info[4])
func _clear_level(level: int) -> void:
for tile_info: Array in levels_tilemap_data[level_names.find(str(level))]:
tilemap.set_cell(tile_info[0], tile_info[1], -1, Vector2i(-1, -1))
And…it works. For now at least, there will be issue with special stages later, but that’s not hard to work around.
Using another array to point to other array is probably a bad idea as well, but I’ll put that aside for now, for there’s more important stuff is not functioning:
How do I save the state of the TileMap?
As it stands, when I leave the level and come back, everything respawns, which is not how the game works.
It’s pretty easy to archive the whole game into an array and load back from it, but modifying it is a nightmare.
Even if I succeed, is it really a good idea to mess around a giant array that makes the whole game functional?
I can probably dig around and find a way around it, but I have a feeling I’ve already made some gigantic stupid mistakes and shouldn’t build upon it further.
A potential solution is to just make a giant TileMap, say 110 * 55, then just shift the TileMap around to create the illusion of level switching. Never worry about save and load since it’ll never be unload.
But…I planned to open source this project once it’s done, also I want it to run on the web. I want to make it as simple and elegant as possible.
I just feel like I’m missing something really fundamental here.
What’s your thought? Thanks for reading.

