Skip to content

Animated Sprites

You can use the region property of a quad to render a subregion of a texture.

This is useful for rendering animated sprites or tilemaps.

ts
import { Toodle } from "@blooper.gg/toodle";

const canvas = document.querySelector("canvas")!;

const toodle = await Toodle.attach(canvas, { filter: "nearest" });

await toodle.assets.registerBundle("goombird", {
  textures: {
    goombird: new URL("/img/goombird.png", "https://toodle.gg"),
  },
  autoLoad: true,
});
await toodle.assets.loadFont(
  "ComicNeue",
  new URL("https://toodle.gg/fonts/ComicNeue-Regular-msdf.json"),
);

const spriteSheet = toodle.Quad("goombird", {
  position: { x: 0, y: -48 },
});

const animatedQuad = toodle.Quad("goombird", {
  idealSize: {
    width: 48,
    height: 48,
  },
  region: {
    x: 0,
    y: 0,
    width: 48,
    height: 48,
  },
  scale: 5,
});

const spritesheetSize = toodle.assets.getSize("goombird");

toodle.camera.zoom = 1.5;

const frameCount = spritesheetSize.width / 48;
let frame = 0;

let acc = performance.now();
function paint() {
  toodle.startFrame();

  // run the animation at 12fps
  if (performance.now() - acc > (12 * 1000) / 60) {
    acc = performance.now();
    frame++;
    frame %= frameCount;
  }

  toodle.draw(spriteSheet);
  toodle.draw(animatedQuad);
  animatedQuad.region.x = frame * 48;

  // draw a debug rect of the current region
  toodle.draw(
    toodle.shapes
      .Rect({
        idealSize: {
          width: 48,
          height: 48,
        },
        color: {
          r: 1,
          g: 0,
          b: 0,
          a: 0.2,
        },
      })
      .setBounds({
        left: -spritesheetSize.width / 2 + animatedQuad.region.x,
        y: spriteSheet.bounds.y,
      }),
  );

  // draw debug text
  toodle.draw(
    toodle.Text(
      "ComicNeue",
      [
        `Frame: ${frame}`,
        `Region: ${JSON.stringify(animatedQuad.region)}`,
      ].join("\n"),
      {
        position: { x: 0, y: 50 },
        fontSize: 16,
        color: { r: 0, g: 0, b: 0, a: 1 },
      },
    ),
  );
  toodle.endFrame();
  requestAnimationFrame(paint);
}
paint();