| Engine | Godot 4.2 or newer |
|---|---|
| Language | GDScript |
| Node | CharacterBody2D with Sprite2D + CollisionShape2D |
| Time to run | ~3 minutes |
| Level | Beginner |
| Output | One file (~50 lines) |
| Dependencies | None. No plugins. |
The prompt
Open a fresh chat at claude.ai/new, paste, and send.
$ Build me a Godot 4.2+ top-down player controller as a single GDScript. # shape - One script, file player.gd, attached to a CharacterBody2D. - Assumes a Sprite2D child and a CollisionShape2D child. - No plugins, no autoloads. # movement - 8-direction movement read with Input.get_vector("move_left","move_right","move_up","move_down"). - Normalized so diagonals are not faster. - Acceleration toward the target velocity and friction back to zero when no input, both @export floats. - Speed @export float, default 160. Acceleration default 1200. Friction default 1000. - Apply movement with move_and_slide(). # sprite - Flip the Sprite2D horizontally based on the last non-zero horizontal input. - Do not flip when the player is only moving vertically. # constraints - Use the built-in InputMap action names above. If they do not exist, add a one-line comment telling me to create them in Project Settings > Input Map. - No magic numbers. Everything tunable is @export. - Godot 4 API only (velocity is a property, move_and_slide takes no arguments). # return format - One file. No prose before or after the code. - Brief comments only on the accel/friction lerp, since that is the non-obvious bit.
What this gets you
A controller that already feels right: the character eases up to speed and coasts to a stop instead of teleporting on and off at full velocity. That accel/friction pair is the difference between "programmer movement" and something you would actually ship.
It is deliberately one file on a CharacterBody2D so you can read the whole thing in one screen and bolt anything else onto it later, an animation tree, a dash, a state machine.
Why it matters
Normalized diagonals
The single most common top-down bug: hold up and right and the player scoots off 40% faster than they move straight. Input.get_vector returns a normalized vector, so this is handled, but only if you actually use it instead of reading four booleans by hand.
Acceleration and friction
Instant velocity feels robotic. Lerping velocity toward a target (accel) and back toward zero (friction) gives movement weight in about four lines. Exposed as @export so you can dial "floaty" vs "tight" without touching code.
Godot 4, not Godot 3
Half the tutorials online are Godot 3, where move_and_slide took the velocity as an argument. In Godot 4 velocity is a property and move_and_slide takes nothing. This prompt pins the version so Claude does not mix them.
What good output looks like
- velocity is a property, not a local. You should see
velocity = ...then a baremove_and_slide(). If move_and_slide has arguments, Claude gave you Godot 3 code, say "Godot 4 only" and regenerate. - Input read once into a vector. Look for a single
Input.get_vector(...)call, not fourInput.is_action_pressedchecks stitched together. - Friction only applies with no input. The friction branch should be inside an
if direction == Vector2.ZEROcheck, otherwise the player can never reach full speed. - move_toward or lerp for accel.
velocity = velocity.move_toward(target, accel * delta)is the clean idiom. delta belongs here even though move_and_slide is delta-aware, because the accel step is your own.
How to wire it up
- Create a CharacterBody2D node. Add a Sprite2D and a CollisionShape2D as children.
- Save the output as
player.gdand attach it to the CharacterBody2D. - Open Project Settings › Input Map and add four actions:
move_left,move_right,move_up,move_down. Bind WASD and/or the arrow keys. - Run the scene. The character should move in 8 directions and ease to a stop.
Common gotchas
The player does not move at all
Almost always the Input Map actions do not exist yet. Godot does not create them for you. Add move_left/right/up/down in Project Settings › Input Map.
Diagonal movement is faster
You are probably reading input as a raw Vector2 without normalizing, or building it from four booleans. Use Input.get_vector, which normalizes for you.
The sprite flips while walking straight up
The flip should only update when horizontal input is non-zero. Guard it with if direction.x != 0: so vertical-only movement leaves the facing alone.
Where to go next
- Animations. Add an AnimationPlayer and swap idle/walk based on whether velocity is near zero.
- Dash. A timed velocity override on a key press. Reuse the @export pattern for dash speed and duration.
- State machine. Once you have idle / walk / dash / attack, port to the FSM structure so states stop fighting each other.
Use it however you want
The prompt is CC0, paste it, modify it, ship a game with it. The output Claude gives you is yours. A link back to pixeldex.dev in your project's README helps the next solo dev find this stuff.