LogoIgnis

Sprites

Cutting a sheet into frames, and playing them.

Loading the scene…

A sprite is an image or a sequence of images in a game. It refers to a single visual entity on the screen.

In Ignis, SpriteNode draws Sprites. Sprite defines properties inherent to the art: the number and size of its frames, how fast it should play, and whether it loops. SpriteNode acts as a controller, selecting the appropriate frame on demand. The process is the same whether you want to render an image or play an animation: preload assets, create a Sprite, then pass it to a SpriteNode.

There are several kinds of Sprite, depending on what you need to draw.

  • SpriteImage represents one whole image.
  • SpriteSheet cuts an image into frames for animation.
  • SpriteGroup lays several Sprites together, end to end.

These Sprite implementations automatically work with the local asset bundle, allowing SpriteNode to reload images in live scenes as they change on disk.

Sprite itself is abstract, so it is straightforward to implement one for images stored in complex ways, such as when packed optimally by an art application or sprite packer.

Warning

SpriteSheet cannot hold animations that wrap across multiple rows or skip frames. A single animation must be specified contiguously on one row.

FlameLineage

The APIs for SpriteSheet and SpriteGroup are heavily inspired by their corresponding classes in Flame.

Examples#

A single image#

final fire = SpriteNode(sprite: SpriteImage('assets/images/bonfire.png'));
sprites.dart
Loading the scene…

Playing a sheet#

final fire = SpriteNode(
  sprite: SpriteSheet('assets/sheets/bonfire.png', BONFIRE_SIZE, fps: 16),
);
sprites.dart
Loading the scene…

Combining sprites#

final smoke = SpriteNode(
  sprite: SpriteSheet('assets/sheets/bonfire_smoke.png', BONFIRE_SIZE, fps: 10),
);

final flame = SpriteNode(
  sprite: SpriteSheet('assets/sheets/bonfire_flame.png', BONFIRE_SIZE, fps: 16),
);

final wood = SpriteNode(
  sprite: SpriteSheet('assets/sheets/bonfire_wood.png', BONFIRE_SIZE, fps: 6),
);
sprites.dart
Loading the scene…

Packing a sheet#

final slime = SpriteNode(
  sprite: SpriteSheet(
    'assets/sheets/slime.png',
    SLIME_SIZE,
    fps: 16,
    rows: [
      .new(frames: 14), // idle
      .new(frames: 30), // jump
      .new(frames: 25), // jump_forward
      .new(frames: 17), // spit
      .new(frames: 30), // spike
      .new(frames: 12), // injured1
      .new(frames: 13), // injured2
      .new(frames: 13), // injured3
      .new(frames: 45), // splat_wall
      .new(frames: 27), // recover
      .new(frames: 49), // death
    ],
  ),
);

final taps = TapInput(shape: .rectangle(DEMO_SIZE));
taps.onTap(() => slime.play(row: (slime.row + 1) % slime.sprite.rows));
sprites.dart
Loading the scene…
Try tapping!

Naming rows#

final slime = SpriteNode(
  sprite: SpriteSheet(
    'assets/sheets/slime.png',
    SLIME_SIZE,
    fps: 16,
    rows: [
      .new(key: 'idle', frames: 14),
      .new(key: 'jump', frames: 30),
    ],
  ),
);

final taps = TapInput(shape: .rectangle(DEMO_SIZE));
taps.onTap(() => slime.play(key: slime.row == 0 ? 'jump' : 'idle'));
sprites.dart
Loading the scene…
Try tapping!

Setting a rate per row#

final sheet = SpriteSheet(
  'assets/sheets/slime.png',
  SLIME_SIZE,
  fps: 16,
  rows: [
    .new(frames: 14, fps: 5), // idle
    .new(frames: 30), // jump
  ],
);

final idle = SpriteNode(sprite: sheet);
final jump = SpriteNode(sprite: sheet)..play(row: 1);
sprites.dart
Loading the scene…

Playing part of a row#

final slime = SpriteNode(
  sprite: SpriteSheet(
    'assets/sheets/slime.png',
    SLIME_SIZE,
    fps: 12,
    rows: [
      .new(start: 6, frames: 6), // idle
    ],
  ),
);
sprites.dart
Loading the scene…

Timing frames by hand#

final slime = SpriteNode(
  sprite: SpriteSheet(
    'assets/sheets/slime.png',
    SLIME_SIZE,
    fps: 0,
    rows: [
      .timed([0.8, 0.06, 0.06, 0.06, 0.06, 0.06]), // idle
    ],
  ),
);
sprites.dart
Loading the scene…

Scaling the rate#

final sheet = SpriteSheet('assets/sheets/bonfire.png', BONFIRE_SIZE, fps: 16);

final fire = SpriteNode(sprite: sheet);
final embers = SpriteNode(sprite: sheet, speed: 0.25);
sprites.dart
Loading the scene…

Switching sheets#

final creature = SpriteNode(
  sprite: SpriteGroup([
    SpriteImage('assets/images/bonfire.png', key: 'fire'),
    SpriteSheet.single('assets/sheets/slime_jump.png', SLIME_SIZE, fps: 16, key: 'slime'),
  ]),
);

final taps = TapInput(shape: .rectangle(DEMO_SIZE));
taps.onTap(() => creature.play(key: creature.row == 0 ? 'slime' : 'fire'));
sprites.dart
Loading the scene…
Try tapping!

Reporting progress#

final slime = SpriteNode(
  sprite: SpriteSheet('assets/sheets/slime_jump.png', SLIME_SIZE, fps: 16),
);

var count = 0;

slime.onFrame((frame) => log1('onFrame $frame'));
slime.onLoop(() => log2('onLoop ${count += 1}', .orange));
sprites.dart
Loading the scene…

Playing once#

final taps = TapInput(shape: .rectangle(DEMO_SIZE));
taps.onTapDown((event) {
  add(
    SpriteNode(
      sprite: SpriteSheet(
        'assets/sheets/explosion.png',
        EXPLOSION_SIZE,
        fps: 20,
        loop: false,
      ),
      cleanup: true,
      position: event.scene,
      anchor: .bottomCenter,
    ),
  );
});
sprites.dart
Loading the scene…
Try tapping!