Position ignored when adding child (CanvasLayer interaction issue?)

Godot Version

4.4.1

Question

I feel like I’m losing my mind, because when I search for my issue it seems like everyone is trying to do the exact opposite.

Let’s say I’ve got a base scene set up like this:

The idea is to have a basic floorplan with hallways that have points where rooms have a chance to spawn (the RoomTrigger objects, one is visible in the viewport in the image above). The triggers have an array of packed scenes that are themselves rooms, with similar CanvasLayers for the floor and wall geometry. All the CanvasLayers (both in the root “level” scene with the hallway layout and the room scenes) are set to follow the viewport, which is a camera tied to the player at present.

The problem:
I’m trying to add the room with something like this in the RoomTrigger script:

var room = room_list.pick_random() #this is the exported packed scene array, ofc
var room_root = room.instantiate()
add_child(room_root)

This added the child room…but at the root level scene’s (0,0), not at 0,0 relative to the RoomTrigger node itself. So I tried about a million different variations of this:

var room = room_list.pick_random() #this is the exported packed scene array, ofc
var room_root = room.instantiate()
room_root.position = position
room_root.global_position = global_poition
print(room_root.global_position)
print(global_position)
add_child(room_root)

etc. etc.etc., to the same result. Then I figured maybe it was some weirdness from trying to instantiate it, set the position, and add it to the tree all on the same frame so I added a

await create_tween().tween_interval(1).finished

before the add_child(room_root), but all that accomplished was making it take a second before spawning the room at (0,0) anyway.

So, please, help me out here - I’ve been neck deep in 3D projects for months and figured it would be fun to teach myself enough to mess with a 2D tile-based goofy project to blow off steam, but now it’s 2:30 in the morning and I feel like a maniac digging through the docs and inspector for some kind of inheritance flag I missed.

CanvasLayer nodes have their positions relative to the screen, not to their parent or the scene tree. So even if you change the local position, the children of the CanvasLayer will still be positioned relative to the screen, not world space. This is exactly why CanvasLayer is used for UI. Why not just use a regular Node2D?

1 Like

Oh. :woman_facepalming: I was using CanvasLayers to ensure that since I’m instantiating child scenes dynamically, the floors and walls get forced into their respective Z-levels regardless of their position on the scene tree. Is there a better way to go about that?

You can actually set the Z-index directly on TileMapLayer nodes, or even on individual TileSet atlas sources, to ensure they render correctly in their respective visual layers. There’s no need to use CanvasLayer for this.
Just group your TileMapLayers under a regular Node2D to keep things organized, or instantiate them directly if you’re building the structure dynamically. This way, floors, walls, and any other layered geometry will always draw in the correct order, and you’ll avoid the screen-space behavior of CanvasLayer entirely.
Also, it is worth noting that all 2D nodes already inherit from the CanvasItem class, which provides properties like z_index for controlling draw order. These nodes still render in world space and follow the camera, making them suitable for tile-based level construction.

1 Like

Thanks a ton!!

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.