Walk up behind a tree and disappear behind it, then come back down in front. One checkbox gives a flat top-down world a sense of depth.
Loading the game engine. This takes a moment the first time.
script at all.
Walk UP behind a tree and you go behind it. Walk back DOWN and you are in front of it again. Nothing in this file does that. The level's root node has "Y Sort Enabled" ticked, so every frame Godot draws whatever is lower on the screen in front of whatever is higher up. That is how a top-down world gets a sense of depth.
Notice what is NOT here: this player does not set z_index. A z_index is a fixed layer number, so it would pin the player in front of every tree forever and Y sorting would never get a say.
Each tree's origin sits at the BOTTOM of its trunk, which is what makes the sorting look right. Y sorting compares those origin points, so putting a tree's origin at its base means "how far down is this tree standing?"
■ UI node · ■ 2D node · % unique name · .tscn instanced scene
extends CharacterBody2D
## How fast the player moves. "@export" puts "speed" at the top of the
## Inspector panel, so you can change it there instead of in this file.
@export var speed: float = 200.0
# "_physics_process()" runs about 60 times a second. Godot calls it for you.
func _physics_process(_delta: float) -> void:
# "Input.get_vector(...)" checks all four keys at once and turns them
# into one arrow pointing where you want to go. The names in quotes are
# Godot's built-in names for the arrow keys.
var direction: Vector2 = Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
# "direction" is only WHICH WAY (its length is always 1).
# "direction * speed" stretches it, so "velocity" is also HOW FAST.
velocity = direction * speed
# Nothing moves until "move_and_slide()" runs.
move_and_slide()
# The warrior picture only faces right, so walking left mirrors it and
# walking right puts it back.
if direction.x < 0.0:
$Sprite2D.flip_h = true
if direction.x > 0.0:
$Sprite2D.flip_h = false