Put a single image on screen in Godot 4, read the arrow keys, and slide it in any direction without letting it leave the window.
This is the one-sprite slice: one Sprite2D node, one short script, and top-down movement in eight directions, with no physics body, no gravity, no jump, no level, no pickups, and no export. When your picture glides around the window and stops at every edge, you are finished.
Godot’s editor changes between 4.x releases. When I checked this session, the current Godot 4 manual listed Sprite2D as the node for drawing a 2D texture, and its Input class reference listed Input.get_vector(). If a menu or button on your screen does not match the steps below, search the current Godot manual for the feature name instead of guessing.
Why a Sprite2D and not a body
Most Godot player tutorials start with CharacterBody2D, because a body can collide with walls and floors. You do not have any walls yet. Adding a physics body now means adding a collision shape you do not need and learning rules you are not using.
A Sprite2D can already sit somewhere on screen and change its position. That is all movement is at this stage. When you later need the sprite to bump into things, you can make it a child of a CharacterBody2D and move the code. The input part stays almost the same.
Step 1: Create a project
Open Godot and create a new project. Give it a name like OneSprite and pick an empty local folder.
The project manager asks for a renderer. Any of the options can draw one 2D image. If you are unsure, keep the default, or check the current manual’s page on renderers for what each one supports.
When the editor opens, you should see a FileSystem dock with an icon.svg file in it. Godot puts that image in new projects. You will use it as your sprite, so you do not need any art.
Step 2: Make the scene
In the Scene dock, choose to create a root node of a custom or other type, then search for Sprite2D and add it. Rename the node to Mover by double-clicking it.
With Mover selected, look at the Inspector. Find the Texture property. Drag icon.svg from the FileSystem dock onto that slot. The Godot logo should appear in the 2D viewport.
Save the scene as mover.tscn. Saving early means the run button knows what to launch.
Drag the sprite somewhere near the middle of the outlined rectangle in the viewport. That rectangle is the game window. Anything outside it will not be visible when the game runs.
Step 3: Check the input actions
Godot ships with built-in input actions for interface navigation, including ui_left, ui_right, ui_up, and ui_down, mapped to the arrow keys. You could use them, but they belong to menus. A clearer habit is to make your own.
Open Project Settings and find the Input Map tab. Add four actions:
move_leftmove_rightmove_upmove_down
For each one, click the add-event button next to it and press the key you want. Arrow keys work. So do W, A, S, and D. You can assign both to the same action.
Close the window. If the Input Map is somewhere else in your version, the manual’s page on input actions shows where it lives now.
Step 4: Attach the script
Select Mover and attach a new script. Keep the default language, GDScript. If a template is offered, choose an empty one so you type everything yourself.
Replace the contents with this:
extends Sprite2D
@export var speed := 300.0
func _process(delta: float) -> void:
var direction := Input.get_vector("move_left", "move_right", "move_up", "move_down")
position += direction * speed * delta
var screen := get_viewport_rect().size
position = position.clamp(Vector2.ZERO, screen)
Save the script.
What each line does
extends Sprite2D means this script is a Sprite2D with extra behavior. It has a position property already, because Sprite2D inherits from Node2D.
@export var speed makes the speed show up in the Inspector. You can change it there without opening the script. The unit is pixels per second.
_process(delta) runs once every frame. delta is how many seconds passed since the last frame. Multiplying by it keeps the speed the same whether your computer draws fast or slow.
Input.get_vector() reads all four actions at once and returns a direction. Holding right gives you a vector pointing right. Holding right and down gives you a diagonal. The current class reference says the result is limited to a length of one, so diagonals are not faster than straight lines. Older tutorials do that step by hand with normalized().
position += direction * speed * delta moves the sprite. When no key is held, the direction is zero, so the sprite stays put.
get_viewport_rect().size gives you the size of the game window in pixels. clamp() keeps each coordinate between zero and that size. The sprite cannot leave the screen.
In Godot 2D, Y grows downward. That is why move_up is passed in the negative Y slot.
Step 5: Run the scene
Use the editor’s Run Current Scene button, or its keyboard shortcut. The manual lists the current shortcut for your platform.
A window opens with the logo in it. Hold an arrow key. The logo slides. Let go. It stops. Hold two keys. It moves diagonally at the same speed. Push it into a corner. It stops at the edge.
When it does not move
Work through these in order:
- Nothing happens at all. Click inside the game window so it has keyboard focus.
- An error mentions an action name. The text in
get_vector()must match the Input Map exactly, including underscores and lowercase letters. - It moves the wrong way on one axis. Two actions are swapped in the call. The order is left, right, up, down.
- It runs off the screen. The clamp line is missing, or it sits above the movement line instead of below it.
- It moves in jumps instead of gliding. You may have written
Input.is_action_just_pressed, which fires once per press.get_vector()reads held keys.
Make it yours
Change three things, one at a time, and run after each:
- Set
speedto a much smaller number in the Inspector, then a much larger one. Pick the value that feels right for your idea. - Swap
icon.svgfor your own small image. Drop the file into the FileSystem dock, then drag it onto the Texture slot. - Notice that the sprite can still poke halfway off the edge. That happens because its position is its center. Try shrinking the clamp range by half the texture size on each side. The Sprite2D reference in the manual shows how to read the texture’s size.
What this page leaves out
There is no collision, so the sprite passes through anything you add to the scene. There is no animation, no camera, no level, and no way to win. Each of those is its own small step.
When you want walls, the next move is a CharacterBody2D with a collision shape and move_and_slide(). Read the current Godot manual’s page on CharacterBody2D before you switch, because the movement line changes even though get_vector() stays.
For now, you have one picture that does what you tell it. Save the project.