Godot Draw Order Basics: Walk Behind Trees While the HUD Stays in Front

Created: 2026-09-22Last updated: 2026-10-10

Sort out the three tiers of draw order (CanvasLayer, z_index, Y Sort) so a character can walk behind a tree on a top-down map.

On a top-down map, you want the character in front of the trunk when it's below a tree (toward the front of the screen), and hidden behind the trunk when it goes around above it. In Godot, you build this with Y Sort, which reorders things by their Y coordinate.

Try to fix it with z_index, the number that decides front and back, and the character either never hides at all or always hides. That's because z_index isn't a number to swap every time you walk; it's a number that moves the whole tier something is drawn on.

This article sorts out how far CanvasLayer, z_index, and Y Sort each reach, and covers how to let the character walk behind a tree.

With z_index 0 on both, on the left the figure stands in front of the trunk, and on the right it has gone behind the trunk with its torso hidden by it

What You'll Learn

  • Draw order is split into three tiers, and numbers can't reach across tiers
  • Which node to turn Y Sort Enabled on for
  • Y Sort compares node origins, and how to line them up with the picture's feet
  • How a child's z_index is added on top of its parent's

Tested with: Godot 4.7

Sponsored

The three tiers that decide draw order

Godot's 2D decides front and back in these three tiers.

  1. Which CanvasLayer it's on
  2. The effective z_index within the same canvas (the single board 2D pictures are drawn onto). Larger is in front
  3. The order within the same effective z. Tree order, or Y Sort order
Draw order is decided in three tiers: CanvasLayer, effective z_index, and order within the same z. If the upper tier differs, the lower tier's numbers can't reach

Against something on a different upper tier, the lower tier's numbers can't compete.

"Effective z" is the z actually used for that node. As you'll see later, a child's z_index is added to its parent's, so it can end up a different value from the number shown in the Inspector.

In other engines: in Unity's 2D, z_index, which fixes front and back, is close to Sorting Layer and Order in Layer; Y Sort is close to the sorting you get with Transparency Sort Mode set to Custom Axis at (0, 1, 0); and sorting a character's parts as one set against the outside is close to Sorting Group.

Front and back between CanvasLayers

CanvasLayer is a node that adds one more board to draw pictures on. The one with the larger layer number comes to the front as a whole, and the contents of separate boards don't mix.

On the same board, a character with z_index 100 comes in front of the heart, but split the boards and it stops at the CanvasLayer boundary

A HUD (displays overlaid on the screen, like health or score) and the pause screen go under a CanvasLayer to fix them "always in front" using this property. In exchange, there's no way for the world side to cut in front of or behind them. A character with Z Index at 100 that still stays behind the hearts is this happening.

To cut in, move that node into a CanvasLayer with a larger layer than the HUD too. A CanvasLayer is fixed to the screen by default. If you want to place it as a world object that flows across the screen as the camera moves, turn on Follow Viewport too. Giving loading screens a large value like 100 is covered in Dynamic Loading Basics.

From here on, it's about things within the same canvas.

Sponsored

Tree order within the same layer

In a scene where you haven't touched a single Z Index, everyone is in the same layer (the same effective z). Front and back within it are decided by the order in the scene tree: nodes lower in the tree are drawn later and come out in front.

Putting the floor first and lining up what stands on it after is enough. Stacking three TileMapLayers works the same way: to put grass on top of the floor, put the grass layer lower in the tree (TileMapLayer Basics).

The dead end comes when you line up the character and the tree as siblings. Tree order doesn't move, so whether the character walks north or south, front and back don't change.

Set Player's Z Index to 1, and whether the figure is above or below, it no longer leaves the front of the trunk

Set the character's Z Index to 1, and it does come in front of the tree. In exchange, it never hides at all. Set the tree to 2, and now it always hides. What z_index can decide is only "which one is on top." Just putting in a value doesn't make them swap automatically to match where you walk.

Don't use z_index for things you want to swap.

How to set Y Sort and the position it compares

Y Sort changes the order within the same layer from tree order to Y-coordinate order. The larger the Y, meaning the lower on screen, the more in front. In a top-down game, this Y is what expresses "in front = closer to the camera."

Where you turn it on is where people stumble. Y Sort Enabled is a switch that sorts that node's contents in Y order. Turning it on for the character itself and the tree itself only sorts what's inside each, so their front-back order doesn't change.

Turning on Y Sort for Tree and Player doesn't change front and back; turning it on for Actors, the parent holding both, sends the figure behind the trunk

What to turn it on for is the parent that holds the character and the tree as siblings.

Level (Node2D)          Y Sort Enabled = off
├─ Ground
└─ Actors (Node2D)      Y Sort Enabled = on   ← only here
   ├─ Tree
   └─ Player

The YSort node from Godot 3 no longer exists in Godot 4. Y Sort Enabled plays the same role.

What's compared isn't the picture's outline or how much things overlap, but the Y of the node's origin. The cross that appears when you select a node in the editor is that point.

Sprite2D has Centered on by default, so its origin is in the middle of the picture. As-is, the switch also happens in the middle of the picture. What readers expect is the feet and the base of the trunk, so if this is off, it looks like "it isn't working."

With Centered as-is, the origin is at the center of the picture; set Offset to (0, -64) and the origin comes to the picture's bottom edge

The fix is to put the node's position at the feet and shift the picture up with Offset. For a 128×128 image, setting Offset to (0, -64) puts the picture's bottom edge at the node's position. Centered can stay on.

One more thing: don't turn it on inside the character's scene. Turn it on on the character side, and the body, shadow, and weapon start being sorted as separate participants. If you have the weapon held a little higher, its Y is smaller than the body's, so only the weapon goes behind the body.

Sponsored

z_index and addition between parent and child

z_index is a number that moves the layer itself. Only when you put it on a child is there a bit more to think about.

Z As Relative is on by default. In that case, a child's effective z is the parent's effective z plus its own z_index. If the parent is 10 and the child is 1, the child is 11. Turn it off, and the parent's value isn't added, so its own z_index becomes its value within that canvas. Either way, it doesn't cross CanvasLayers.

When the parent's Y Sort is off and the effective z is the same, a child is drawn after its parent. Setting the child to 0 doesn't put it behind the parent. There are two ways to put it behind: enter -1 in Z Index, or turn on Show Behind Parent (a checkbox that draws it before the parent while keeping z as-is). These two give different results. -1 lowers the effective z itself, so it changes not just the order relative to the parent but also the relationship with things outside.

Enter -1 in the shadow's Z Index, and its effective z becomes -1, sinking behind the ground at effective z 0

This is where shadows trip people up. Put the shadow as a child of the character and enter -1 to put it behind the body, and it does go behind the body. But its effective z becomes -1, so with the ground still at 0, it sinks behind the ground. What you thought you fixed inside the character turns into a contest with the ground outside it. Leave the shadow at 0 and place it above the body in the tree, and it comes out behind the body and in front of the ground.

NodeParentZ IndexEffective zWhere it shows
GroundLevel00In front of the shadow (behind everything else)
TreeActors00Competes with Player by Y
PlayerActors00Competes with Tree by Y
ShadowPlayer-1-1Disappears under the ground
WeaponPlayer11Always in front of the tree

The bottom two rows of the table (shadow and weapon) are where people trip up.

As-is, the body and weapon are compared with the tree as one set; enter 1 in Weapon's Z Index, and only the weapon leaves the group

When a part's effective z becomes a different value from the body's, that part leaves its parent's group and starts competing directly with things outside. Set the weapon to 1, and even when the character goes behind the tree, only the weapon stays in front of the trunk.

To decide only the order among parts, leave every Z Index at 0 and arrange them by their order in the tree. Put shadow, body, and weapon in that order, and the shadow is under the body and the weapon on top of it. Then the whole set swaps with the tree together, based on the character's feet.

Hands-On: Walk Around to the Front and Back of a Tree

A forest in a top-down ARPG, fruit trees and villagers in a farming sim, hiding behind pillars in a top-view shooter—they all have the same shape. Here you'll place just one tree and one character, and swap front and back without touching a single Z Index.

① What to place

Create one new scene. The only art is the icon.svg included in every project from the start, so there are no assets to prepare.

Put a Node2D at the root as Level, and attach level.gd.

  • Add a ColorRect under Level as Ground. Position (0, 0), Size (1152, 648), Color dark gray, Mouse Filter Ignore (doesn't receive clicks)
  • Add a Node2D under Level as Actors. Turn on Y Sort Enabled under Ordering in the Inspector
  • Add a Sprite2D under Actors as Tree. Position (400, 300), icon.svg in Texture, Scale (0.6, 1.4), Offset (0, -64)
  • Add a CharacterBody2D under Actors as Player. Position (400, 360), Motion Mode Floating, Y Sort Enabled left off
  • Under Player, a CollisionShape2D (CircleShape2D, radius 16)
  • Under Player, three Sprite2Ds. From the top: Shadow (icon.svg, Scale (0.35, 0.12), Modulate (the color multiplied into the picture) black), Body (icon.svg, Scale (0.4, 0.4), Offset (0, -64)), and Weapon (icon.svg, Scale (0.15, 0.15), position (24, -36))
  • Add a CanvasLayer under Level as Hud. Leave Layer at 1. Under it, add a Label as InfoLabel at position (16, 16)
  • For Tree, Player, and InfoLabel, right-click and turn on "Access as Unique Name"

Ground's Size is the same numbers as the screen. If you've changed the screen size in the project settings, match that. Ground is above Actors in the tree, so it's always drawn at the very back without touching Z Index.

Tree's Offset is (0, -64), so the picture's bottom edge is at (400, 300). This point is the base of the trunk, the height where front and back switch. Player works the same way, with its origin at the feet. It's top-down, so set Motion Mode to Floating (The Difference Between Motion Modes).

② Put it together

# level.gd
extends Node2D

const SPEED := 160.0

@onready var player: CharacterBody2D = %Player
@onready var tree_node: Sprite2D = %Tree
@onready var info: Label = %InfoLabel

func _physics_process(_delta: float) -> void:
	var direction := Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
	player.velocity = direction * SPEED
	player.move_and_slide()
	info.text = "Player Y %.1f / Tree Y %.1f" % [   # %.1f shows one decimal place
		player.global_position.y, tree_node.global_position.y
	]

All it does is move and show the two Y values on screen. There isn't a single line deciding front and back. The Y values shown on the label are what Y Sort actually compares.

Writing like %Player gets a node by its unique name rather than by its path in the tree. Even if you rearrange the tree later, these three lines don't need rewriting (Getting Nodes).

③ Run it and check

At Y 320 the figure is in front of the trunk, and at 280 it's hidden behind it. Enter 1 in the weapon's Z Index, and only the weapon stays on top of the trunk

Run with F6 (runs the scene you have open) and walk up and down with the arrow keys. Check three things.

At the starting position of 360, the body and trunk are apart. Walk up, and they start to overlap from around 351; at 320, the top 60% of the body overlaps the trunk. Even so, as long as Player Y is larger than Tree Y's 300.0, the body is in front. Walk further up to 280, and the body has gone behind the trunk and can't be seen. Z Index is 0 on both, and the switch is the single frame crossing 300. It doesn't gradually show through; it swaps all at once.

Next, try turning off Actors' Y Sort Enabled. Wherever you move in Y, the body no longer leaves the front of the trunk. Turn it back on, and with the body hidden by the trunk around 280, set Weapon's Z Index to 1, and only the weapon stays on top of the trunk. You can see that when a part's effective z becomes a different value from the body's, only that part leaves the set.

Finally, set Weapon back to 0, walk down to 360, and turn on Player's Y Sort Enabled. The weapon goes behind the body, and the parts start being sorted separately. Set Player's Z Index to 100 and walk up over the label at the top left, and the label stays in front. Put each thing you tried back as you go.

Two points.

  • For things you want to swap, match their effective z and leave it to Y Sort: that's why this article's hands-on doesn't use a single Z Index. The moment its effective z differs from the other's, swapping by Y stops
  • The parent makes the range to sort, and a node with Y Sort off moves as one set: turn on Actors, and what's inside it becomes subject to sorting. Leave Player off, and the body, shadow, and weapon are treated as one person's set. Give a part an effective z different from the body's, and only that part leaves the set

If you want to change the order of the shadow and body, swap Shadow and Body in the tree instead of using Z Index. It stays within the same layer, so the relationship with the ground doesn't change.

Troubleshooting

SymptomCauseFix
After grouping trees under Trees, the character only comes in front of or behind the whole forestThe container Trees is off, so the forest is sorted as one by Trees' origin YTurn on Y Sort Enabled on Trees too. The same goes when trees are drawn in a TileMapLayer
You turned on Y Sort Enabled on both the tree and the character, but they don't swapY Sort Enabled is a switch that sorts that node's contents. It's only sorting what's inside each of the tree and the characterTurn it on for the parent (Actors) that holds the tree and the character as siblings
The height where front and back switch is the middle of the picture, not the base of the trunkSprite2D's Centered is on, so the node's origin is at the center of the picturePut the node's position at the feet and shift the picture up with Offset ((0, -64) for 128×128)
After moving things under a plain Node to organize them, Y Sort stopped workingA plain Node isn't a CanvasItem (the common parent of Node2D, Sprite2D, and Control), so the chain of position, z addition, and Y Sort breaks thereUse a Node2D as the container

Bonus: Good to Know First

  • When placing trees with tiles: if you drew tall things in a TileMapLayer, make that layer a child of Actors and turn on Y Sort Enabled on the layer side too. Leave it off, and the whole forest is compared as one sheet, and the character comes out in front of or behind the whole forest. Fix tiles whose base height is off with Y Sort Origin under the TileSet's Rendering
  • Don't make two CanvasLayers with the same number: even with the same layer, the contents of two boards don't mix, and which is in front is decided by tree order. Just changing where they're placed swaps them, so give different numbers to things whose front and back you want to be clear
  • Darkening the screen is per board too: CanvasModulate, which darkens the world, works per CanvasLayer, so splitting boards splits the range of the darkening too (2D Lighting Basics)
Sponsored

Summary

  • Draw order has three tiers: CanvasLayer → effective z_index → order within the same layer. Against something on a different upper tier, the lower tier's numbers can't compete
  • Within the same layer, it's tree order by default: nodes lower in the tree are drawn in front
  • Put Y Sort on the parent: Y Sort Enabled is a switch that sorts that node's contents in Y order. Turn it on for the parent that holds the things you want sorted
  • What's compared is the Y of the node's origin: Sprite2D's origin is the center of the picture, so shift the picture up with Offset to line it up with the feet
  • Don't use z_index for things you want to swap: a child's z_index is added to its parent's, and when a part's effective z differs from the body's, only that part leaves the set

Start by picking one node in your own scene that currently has a z_index. If you put that number in to decide "always this one," it's right as it is. If you put it in because you want them to swap as they move up and down, that's Y Sort's job. What in your game do you want the character to be able to walk behind?

Read nextNext in the learning path
Rendering

Godot 2D Lighting Basics: Adding Depth with PointLight2D and Shadows

Read this note
Godot Notes in this section104