Godot Version
4.4.1
Question
So as part of a bigger project I’m building a 2D, grid-based building system. the kind of thing you’d see in like a city-builder or in a Tutorial like this yknow?
The problem is I’m using a Scene Collection for my tileset instead of a sprite atlas cause I’d figure it’d save me some time. Well imagine the egg on my face when I realized that Tilemaps only instantiate scenes on _ready()
, which means the built in .set_cell()
function just doesn’t work how’d you want it too. Don’t get me wrong if you go to check that grid tile it’ll say something’s there but when it comes to actually instantiating in the scene like you’d want it just doesn’t do anything.
So yeah if someone would mind helping me here with this that’d be greatly appreciated cause I’m kinda at my wit’s end here ngl.
I’m doing runtime generated random tilemaps in my game. What I do is have a phased level start where I put up a loading screen with a progress bar on it. I then scribble tiles into the tilemap, setting the progress bar based on what amount of the tilemap I’ve drawn, and then when it’s done I fade out the load.
The hierarchy looks something like:
Game (node)
LoadScreen (fullscreen texture rect)
Progress (progress bar)
Map (tilemaplayer)
...
The code looks something like:
enum LoadPhase = { initial, gen_map, fade_in, done }
var load_phase = LoadPhase.initial
func _process(delta: float) -> void:
match(load_phase):
LoadPhase.initial:
$Game/LoadScreen.visible = true
# init map generation, set load_phase to gen_map
LoadPhase.gen_map:
# do one (or a few?) 16x16 blocks of set_cell()
$Game/LoadScreen/Progress.value = ...
# if we're done, set up an alpha tween on $Game/LoadScreen
LoadPhase.fade_in:
#Wait for the tween to complete, then set phase to done and hide loadscreen
LoadPhase.done:
pass
I do the set_cell()
in 16x16 blocks because it seems to go faster that way. I think when you set_cell()
in one of the blocks it has to regenerate the entire block’s geometry and send that to the GPU, so if you just go by rows it takes substantially longer since it hits more blocks.
while that’s interesting that doesn’t help me at all.
Have you tried calling update_internals()
on the tilemap after set_cell()
?
yes and it doesn’t work. Like I said earlier the problem is that tilemaps only instantiate in the scenes on _ready()
. the actual tilemap is getting updated properly but it’s not instantiating in any scenes like what I want. like if you run get_used_cells()
it’ll show all the cells you added in like its supposed to. Hell it’ll even remove the scenes properly if you deleted or overwrote the tile. It’s just for some reason it can’t add in any new scenes.
here’s a rough version of my build_tile()
in case that helps.
@onready var builder : TileMapLayer
func build_tile(tile_pos : Vector2i) -> void:
# For TileSetScenesCollectionSource it should always be Vector2i(0, 0),
builder.set_cell(tile_pos, selectedTileID, Vector2i(0, 0))
I was hoping update_internals()
would force the scene instantiation. Something must be doing that if it’s called from _ready()
…
The docs for TileSetScenesCollectionSource
say:
Scenes are instantiated as children of the TileMap when it enters the tree. If you add/remove a scene tile in the TileMap that is already inside the tree, the TileMap will automatically instantiate/free the scene accordingly.
That second sentence strongly implies it should be instancing for you if you set_cell()
as well, so this might be an engine bug?
oh you know what it might be. Just to double check earlier you said you were using a tileset scene collection for something right? In that case A) does it work? and B) what version of Godot are you using for it? Im using Godot 4.4.1 like I said up top
I’m running 4.3 here (the current project was too far along to safely upgrade when 4.4 hit). I’m not currently using scene collections, since I’ve gone to a mixed 2D/3D setup (TileMapLayer ground, 3D scene on top with the tilemap at the far plane), but I’m pretty sure I had it working when I was.