Sprite Components¶
Sprites are 2D images (or regions of images) that represent the visual appearance of game objects.
They are the most common way to display characters, items, backgrounds, and other visuals in 2D
games. Flame provides several sprite-based components that make it easy to load images, play
animations, and switch between visual states, all while benefiting from the transform properties
inherited from PositionComponent.
SpriteComponent¶
The most commonly used implementation of PositionComponent is SpriteComponent, and it can be
created with a Sprite:
import 'package:flame/components/component.dart';
class MyGame extends FlameGame {
late final SpriteComponent player;
@override
Future<void> onLoad() async {
final sprite = await Sprite.load('player.png');
final size = Vector2.all(128.0);
final player = SpriteComponent(size: size, sprite: sprite);
// Vector2(0.0, 0.0) by default, can also be set in the constructor
player.position = Vector2(10, 20);
// 0 by default, can also be set in the constructor
player.angle = 0;
// Adds the component
add(player);
}
}
SpriteAnimationComponent¶
This class is used to represent a Component that has sprites that run in a single cyclic animation.
This will create a simple three frame animation using 3 different images:
@override
Future<void> onLoad() async {
final sprites = [0, 1, 2]
.map((i) => Sprite.load('player_$i.png'));
final animation = SpriteAnimation.spriteList(
await Future.wait(sprites),
stepTime: 0.01,
);
this.player = SpriteAnimationComponent(
animation: animation,
size: Vector2.all(64.0),
);
}
If you have a sprite sheet, you can use the sequenced constructor from the SpriteAnimationData
class (check more details on Images > Animation):
@override
Future<void> onLoad() async {
final size = Vector2.all(64.0);
final data = SpriteAnimationData.sequenced(
textureSize: size,
amount: 2,
stepTime: 0.1,
);
this.player = SpriteAnimationComponent.fromFrameData(
await images.load('player.png'),
data,
);
}
All animation components internally maintain a SpriteAnimationTicker which ticks the
SpriteAnimation. This allows multiple components to share the same animation object.
Example:
final sprites = [/*Your sprite list here*/];
final animation = SpriteAnimation.spriteList(sprites, stepTime: 0.01);
final animationTicker = SpriteAnimationTicker(animation);
// or alternatively, you can ask the animation object to create one for you.
final animationTicker = animation.createTicker(); // creates a new ticker
animationTicker.update(dt);
To listen when the animation is done (when it reaches the last frame and is not looping) you can
use animationTicker.completed.
Example:
await animationTicker.completed;
doSomething();
// or alternatively
animationTicker.completed.whenComplete(doSomething);
Additionally, SpriteAnimationTicker also has the following optional event callbacks: onStart,
onFrame, and onComplete. To listen to these events, you can do the following:
final animationTicker = SpriteAnimationTicker(animation)
..onStart = () {
// Do something on start.
};
final animationTicker = SpriteAnimationTicker(animation)
..onComplete = () {
// Do something on completion.
};
final animationTicker = SpriteAnimationTicker(animation)
..onFrame = (index) {
if (index == 1) {
// Do something for the second frame.
}
};
To reset the animation to the first frame when the component is removed, you can set
resetOnRemove to true:
SpriteAnimationComponent(
animation: animation,
size: Vector2.all(64.0),
resetOnRemove: true,
);
SpriteAnimationGroupComponent¶
SpriteAnimationGroupComponent is a simple wrapper around SpriteAnimationComponent which enables
your component to hold several animations and change the current playing animation at runtime. Since
this component is just a wrapper, the event listeners can be implemented as described in
SpriteAnimationComponent.
Its use is very similar to the SpriteAnimationComponent but instead of being initialized with a
single animation, this component receives a Map of a generic type T as key and a
SpriteAnimation as value, and the current animation.
Example:
enum RobotState {
idle,
running,
}
final running = await loadSpriteAnimation(/* omitted */);
final idle = await loadSpriteAnimation(/* omitted */);
final robot = SpriteAnimationGroupComponent<RobotState>(
animations: {
RobotState.running: running,
RobotState.idle: idle,
},
current: RobotState.idle,
);
// Changes current animation to "running"
robot.current = RobotState.running;
As this component works with multiple SpriteAnimations, naturally it needs an equal number of
animation tickers to make all those animations tick. Use animationsTickers getter to access a map
containing tickers for each animation state. This can be useful if you want to register callbacks
for onStart, onComplete and onFrame.
Example:
enum RobotState { idle, running, jump }
final running = await loadSpriteAnimation(/* omitted */);
final idle = await loadSpriteAnimation(/* omitted */);
final robot = SpriteAnimationGroupComponent<RobotState>(
animations: {
RobotState.running: running,
RobotState.idle: idle,
},
current: RobotState.idle,
);
robot.animationTickers?[RobotState.running]?.onStart = () {
// Do something on start of running animation.
};
robot.animationTickers?[RobotState.jump]?.onStart = () {
// Do something on start of jump animation.
};
robot.animationTickers?[RobotState.jump]?.onComplete = () {
// Do something on complete of jump animation.
};
robot.animationTickers?[RobotState.idle]?.onFrame = (currentIndex) {
// Do something based on current frame index of idle animation.
};
SpriteGroupComponent¶
SpriteGroupComponent is pretty similar to its animation counterpart, but especially for sprites.
Example:
class PlayerComponent extends SpriteGroupComponent<ButtonState>
with HasGameReference<SpriteGroupExample>, TapCallbacks {
@override
Future<void> onLoad() async {
final pressedSprite = await game.loadSprite(/* omitted */);
final unpressedSprite = await game.loadSprite(/* omitted */);
sprites = {
ButtonState.pressed: pressedSprite,
ButtonState.unpressed: unpressedSprite,
};
current = ButtonState.unpressed;
}
// tap methods handler omitted...
}
IconComponent¶
IconComponent renders a Flutter IconData (such as Icons.star) as a Flame component. The icon
is rasterized to an image once during onLoad() and then drawn each frame using
canvas.drawImageRect() with the component’s Paint. Because the icon is rendered as a cached
image rather than as text, all paint-based effects work out of the box, including tint(),
setOpacity(), ColorEffect, OpacityEffect, GlowEffect, and custom ColorFilters.
Basic usage¶
import 'package:flame/components.dart';
import 'package:flutter/material.dart';
class MyGame extends FlameGame {
@override
Future<void> onLoad() async {
final star = IconComponent(
icon: Icons.star,
iconSize: 64,
position: Vector2(100, 100),
);
add(star);
}
}
Tinting and effects¶
The icon is rasterized in white, which allows you to tint it to any color using
HasPaint methods:
// Tint the icon gold
final star = IconComponent(
icon: Icons.star,
iconSize: 64,
position: Vector2(100, 100),
)..tint(const Color(0xFFFFD700));
// Set opacity
star.setOpacity(0.5);
// Or use a custom paint
final icon = IconComponent(
icon: Icons.favorite,
iconSize: 48,
paint: Paint()..colorFilter = const ColorFilter.mode(
Color(0xFFFF0000),
BlendMode.srcATop,
),
);
Constructor parameters¶
icon: TheIconDatato render (e.g.,Icons.star,Icons.favorite).iconSize: The resolution at which the icon is rasterized (default64). This is independent of the component’s displaysize.size: The display size of the component. Defaults toVector2.all(iconSize)if not provided.paint: OptionalPaintfor rendering effects.All standard
PositionComponentparameters (position,scale,angle,anchor, etc.).
Changing the icon at runtime¶
Both the icon and iconSize properties can be changed after creation. The component will
automatically re-rasterize the icon on the next frame:
final iconComponent = IconComponent(
icon: Icons.play_arrow,
iconSize: 64,
);
// Later, swap the icon
iconComponent.icon = Icons.pause;
// Or change the rasterization resolution
iconComponent.iconSize = 128;