Prepare your pixel art animation sheet
A predictable grid saves hours. For this tutorial, use a sheet that is 256 by 256 pixels, arranged as 8 columns and 4 rows. That gives 32 by 32 pixel frames. Put idle on row 1, run on row 2, and jump on row 3. Leave row 4 empty for a future attack or spare frames.
Before you import, check three things:
- Every frame uses the same cell size with no extra padding between cells
- The character feet sit on the same baseline in each row so the sprite does not bounce when it plays
- The file is PNG with either full transparency or a single solid background, not a mix
If you generated the sheet outside Godot, the Image Studio and templates workflow is a useful way to keep canvas size and cell alignment consistent across exports.
Drag the PNG into your project, for example into res://art/player_sheet.png. Select the AnimatedSprite2D that will use it later and set texture_filter to Nearest under CanvasItem in the Inspector. That keeps pixel edges sharp when the camera zooms or the window scales.
Slice the sheet into a SpriteFrames resource
You will build one SpriteFrames resource that holds all animations for the character. Work in the main editor, not in code, so you can see each frame as you select it.
- Create a
CharacterBody2Dnode, then add a childAnimatedSprite2Dand a childCollisionShape2D. - Select the
AnimatedSprite2D, findSpriteFramesin the Inspector, and choose New SpriteFrames. - Click the SpriteFrames editor panel at the bottom of the editor to open it.
- Rename the default animation from
defaulttoidle. - Click Add frames from sprite sheet. It looks like a small grid icon in the panel toolbar.
- In the picker, load
player_sheet.png. Set Horizontal to 8 and Vertical to 4. You should see the grid lines land exactly on cell borders. If they do not, stop and fix the sheet size before you continue. - Drag to select the 6 frames on row 1, then click Add. Repeat after creating
runandjumpanimations with Add Animation.
Name and fill idle, run, and jump
Keep the naming simple and lowercase: idle, run, jump. Add 4 to 8 frames per animation for pixel art. You can reorder frames by dragging them in the filmstrip. Delete any blank or duplicate frame now, because a single bad frame is very visible at 10 frames per second.
Stick to GDScript for the playback script below unless your team already uses C#. The tradeoffs are explained in GDScript vs C# for AI assisted projects.
Set loop mode and animation speed
Select each animation in the SpriteFrames panel. Two settings control most of the feel.
| Animation | Loop | Speed (FPS) | Why |
|---|---|---|---|
idle | On | 6 | Slow breathing loop that can repeat forever |
run | On | 10 | Snappy cycle that matches footstep timing |
jump | Off | 8 | Plays once so landing can switch to idle or run |
Loop is the looping arrow button next to the animation name. Speed is the FPS field to the right of the frames. Pixel art usually looks best between 6 and 12 FPS. Higher values blur the stepped motion that makes pixel art appealing.
Use AnimationPlayer only if you need to animate position or scale at the same time. For pure sprite swapping, AnimatedSprite2D plus SpriteFrames is simpler and easier to test.
Test playback in a sample scene
Build a small room so you can see movement and gravity. Your scene tree should look like this:
- Player as
CharacterBody2Dwith the script belowAnimatedSprite2DCollisionShape2Dwith a capsule shape
- Floor as
StaticBody2Dwith a large rectangle shape
Attach this script to the Player node. It handles left and right movement, jumping, and animation switching with no extra setup.
extends CharacterBody2D
@onready var sprite: AnimatedSprite2D = $AnimatedSprite2D
const SPEED := 120.0
const JUMP_VELOCITY := -220.0
const GRAVITY := 800.0
func _physics_process(delta: float) -> void:
if not is_on_floor():
velocity.y += GRAVITY * delta
elif Input.is_action_just_pressed("ui_accept"):
velocity.y = JUMP_VELOCITY
var direction := Input.get_axis("ui_left", "ui_right")
velocity.x = direction * SPEED
move_and_slide()
if not is_on_floor():
sprite.play("jump")
elif direction != 0.0:
sprite.play("run")
sprite.flip_h = direction < 0.0
else:
sprite.play("idle")Press F6 to play the current scene. You should see idle loop when still, run loop when moving, and jump play once in the air. If the feet slide, raise run FPS by 1 or 2. If the sprite looks blurry, check texture_filter is still Nearest.
Tip: test with the window at both 1x and 2x scale. Pixel crawl often hides at one zoom level and appears at another.
If you work with Godot MCP Studio, you can ask the agent to open the sample scene, list the animations in the SpriteFrames resource, play the scene, and report which animation name is active while the character moves. That gives you a quick second check that idle, run, and jump all trigger.
When all three play correctly, save the SpriteFrames as res://art/player_spriteframes.tres so you can reuse it for enemies or alternate skins. You now have a sliced pixel art animation sheet running as timed Godot 2D animation.