Now let’s give the falling circle a face. This is Falling and jumping again, but drawing your own sprite instead of a circfill. The physics is identical — the one new thing to get right is where the sprite sits, because spr measures from a different point than a circle does.
First, in the Sprites tab, draw a little character in slot 0 (see the Drawing sprites lesson). Then use the code below.
1. The corner catch
circfill(x, y, …) draws around its center, but spr(n, x, y) draws from the sprite’s top-left corner. A sprite is 8 pixels tall, so its feet are 8 pixels below y, at y + 8. If you forget that, the character lands buried up to its neck — the ground meets its head instead of its feet.
This little + 8 is an offset: the gap between the point you store (the top-left corner) and the point you actually care about (the feet). Almost every sprite you position will need one somewhere.
2. Land the feet
Pick a GROUND line where the feet should rest. Because the feet are at y + 8, you compare that to the ground, and when it lands you set y = GROUND - 8 — putting the feet on the line and the sprite 8 pixels above it:
let y = 40;
let dy = 0;
const GRAVITY = 0.3;
const GROUND = 104; // where the FEET rest
function update() {
dy = dy + GRAVITY;
y = y + dy;
if (y + 8 > GROUND) { // feet, not head
y = GROUND - 8;
dy = 0;
}
}
function draw() {
cls(1);
line(0, GROUND, 128, GROUND, 5);
spr(0, 60, y);
}3. Walk, face, and jump
Add left/right movement and a jump. spr(n, x, y, flipX) mirrors the sprite, so passing facingLeft makes your character turn to face the way it walks (from Drawing sprites). The jump is the same negative dy, gated on standing on the ground:
if (key('ArrowLeft')) {
x = x - 1.5;
facingLeft = true;
}
if (key('ArrowRight')) {
x = x + 1.5;
facingLeft = false;
}
if (keyp(' ') && y === GROUND - 8) {
dy = JUMP; // only jump when the feet are on the ground
}Send the full program to the editor and you’ve got a little character that runs, faces the right way, and jumps — all yours because it’s your art in slot 0.
Try it: draw a second frame in slot 1 and swap between them while walking — let s = flr(time() * 8) % 2 then spr(s, x, y, facingLeft) — for a two-frame walk cycle. Change GROUND and watch the feet still land exactly on the line.
What you learned
spr(n, x, y)draws from the top-left corner, not the center likecircfill.- A sprite’s feet are at
y + 8— compare that to the ground, and land withy = GROUND - 8. - That
+ 8is an offset: the gap between the stored corner and the point you care about. spr(n, x, y, facingLeft)flips the sprite so it faces the way it walks.