Skip to content

Commit

Permalink
refactor!: The method onLoad() now returns FutureOr<void> (#2228)
Browse files Browse the repository at this point in the history
Before this PR, the return type of onLoad() was Future<void>?, after this PR the return type is FutureOr<void> -- both for classes Component and Game.

Reasons:

The use of FutureOr is more idiomatic in Dart, this class was specifically created in order to be used in situations like ours.
This makes learning Flame easier for beginners, since you no longer need to explain what asynchronous programming is from the start (and onLoad() is one of the first methods the user encounters in Flame).
The code can be cleaner in case when onLoad doesn't need to be async.
With new approach, the onLoad() method can be overridden as either

@OverRide
Future<void> onLoad() async { ... }
or

@OverRide
void onLoad() { ... }
Of course, it can also be overridden as

@OverRide
FutureOr<void> onLoad() { ... }
but this is rare, only for components that are designed to be further subclassed, or for mixins.

The documentation was updated to show the new recommended usage.
  • Loading branch information
st-pasha committed Dec 23, 2022
1 parent 19a1f09 commit d898b53
Show file tree
Hide file tree
Showing 11 changed files with 122 additions and 112 deletions.
177 changes: 96 additions & 81 deletions doc/flame/components.md
Expand Up @@ -88,7 +88,7 @@ Example:
```dart
class MyGame extends FlameGame {
@override
Future<void> onLoad() {
void onLoad() {
final myComponent = PositionComponent(priority: 5);
add(myComponent);
}
Expand Down Expand Up @@ -135,7 +135,7 @@ class GameOverPanel extends PositionComponent {
GameOverPanel(this.spriteImage);
@override
Future<void> onLoad() async {
void onLoad() {
final gameOverText = GameOverText(spriteImage); // GameOverText is a Component
final gameOverButton = GameOverButton(spriteImage); // GameOverRestart is a SpriteComponent
Expand Down Expand Up @@ -163,7 +163,7 @@ constructor. This approach more closely resembles the standard Flutter API:
```dart
class MyGame extends FlameGame {
@override
Future<void> onLoad() async {
void onLoad() {
add(
PositionComponent(
position: Vector2(30, 0),
Expand Down Expand Up @@ -198,7 +198,7 @@ Example:
```dart
class MyComponent extends Component with ParentIsA<MyParentComponent> {
@override
Future<void> onLoad() async {
void onLoad() {
// parent is of type MyParentComponent
print(parent.myValue);
}
Expand All @@ -221,7 +221,7 @@ Example:
```dart
class MyComponent extends Component with HasAncestor<MyAncestorComponent> {
@override
Future<void> onLoad() async {
void onLoad() {
// ancestor is of type MyAncestorComponent.
print(ancestor.myValue);
}
Expand All @@ -247,7 +247,7 @@ Example:

```dart
@override
Future<void> onLoad() async {
void onLoad() {
children.register<PositionComponent>();
}
```
Expand Down Expand Up @@ -442,10 +442,10 @@ class MyGame extends FlameGame {
final player = SpriteComponent(size: size, sprite: sprite);
// Vector2(0.0, 0.0) by default, can also be set in the constructor
player.position = ...
player.position = Vector2(10, 20);
// 0 by default, can also be set in the constructor
player.angle = ...
player.angle = 0;
// Adds the component
add(player);
Expand All @@ -461,32 +461,36 @@ This class is used to represent a Component that has sprites that run in a singl
This will create a simple three frame animation using 3 different images:

```dart
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),
);
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 &gt; Animation](rendering/images.md#animation)):

```dart
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,
);
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,
);
}
```

If you are not using `FlameGame`, don't forget this component needs to be updated, because the
Expand Down Expand Up @@ -580,7 +584,7 @@ class ButtonComponent extends SpriteGroupComponent<ButtonState>
@override
Future<void>? onLoad() async {
final pressedSprite = await gameRef.loadSprite(/* omitted */);
final unpressedSprite = await gameRef.loadSprite(/* omitted /*);
final unpressedSprite = await gameRef.loadSprite(/* omitted */);
sprites = {
ButtonState.pressed: pressedSprite,
Expand All @@ -604,12 +608,14 @@ This component uses an instance of `Svg` class to represent a Component that has
rendered in the game:

```dart
final svg = await Svg.load('android.svg');
final android = SvgComponent.fromSvg(
svg,
position: Vector2.all(100),
size: Vector2.all(100),
);
Future<void> onLoad() async {
final svg = await Svg.load('android.svg');
final android = SvgComponent.fromSvg(
svg,
position: Vector2.all(100),
size: Vector2.all(100),
);
}
```


Expand Down Expand Up @@ -711,7 +717,7 @@ class MyParallaxComponent extends ParallaxComponent<MyGame> {
class MyGame extends FlameGame {
@override
Future<void> onLoad() async {
void onLoad() {
add(MyParallaxComponent());
}
}
Expand All @@ -726,20 +732,24 @@ They simplest way is to set the named optional parameters `baseVelocity` and
background images along the X-axis with a faster speed the "closer" the image is:

```dart
final parallaxComponent = await loadParallaxComponent(
_dataList,
baseVelocity: Vector2(20, 0),
velocityMultiplierDelta: Vector2(1.8, 1.0),
);
Future<void> onLoad() async {
final parallaxComponent = await loadParallaxComponent(
_dataList,
baseVelocity: Vector2(20, 0),
velocityMultiplierDelta: Vector2(1.8, 1.0),
);
}
```

You can set the baseSpeed and layerDelta at any time, for example if your character jumps or your
game speeds up.

```dart
final parallax = parallaxComponent.parallax;
parallax.baseSpeed = Vector2(100, 0);
parallax.velocityMultiplierDelta = Vector2(2.0, 1.0);
Future<void> onLoad() async {
final parallax = parallaxComponent.parallax;
parallax.baseSpeed = Vector2(100, 0);
parallax.velocityMultiplierDelta = Vector2(2.0, 1.0);
}
```

By default, the images are aligned to the bottom left, repeated along the X-axis and scaled
Expand Down Expand Up @@ -997,20 +1007,23 @@ then add animation. Removing the stack will not remove the tiles from the map.
> **Note**: This currently only supports position based effects.
```dart
final stack = map.tileMap.tileStack(4, 0, named: {'floor_under'});
stack.add(
SequenceEffect(
[
MoveEffect.by(
Vector2(5, 0),
NoiseEffectController(duration: 1, frequency: 20),
),
MoveEffect.by(Vector2.zero(), LinearEffectController(2)),
],
repeatCount: 3,
)..onComplete = () => stack.removeFromParent(),
);
map.add(stack);
void onLoad() {
final stack = map.tileMap.tileStack(4, 0, named: {'floor_under'});
stack.add(
SequenceEffect(
[
MoveEffect.by(
Vector2(5, 0),
NoiseEffectController(duration: 1, frequency: 20),
),
MoveEffect.by(Vector2.zero(), LinearEffectController(2)),
],
repeatCount: 3,
)
..onComplete = () => stack.removeFromParent(),
);
map.add(stack);
}
```


Expand Down Expand Up @@ -1111,19 +1124,20 @@ be used:
class MyGame extends FlameGame {
int lives = 2;
Future<void> onLoad() {
final playerNotifier = componentsNotifier<Player>()
..addListener(() {
final player = playerNotifier.single;
if (player == null) {
lives--;
if (lives == 0) {
add(GameOverComponent());
} else {
add(Player());
}
}
});
@override
void onLoad() {
final playerNotifier = componentsNotifier<Player>()
..addListener(() {
final player = playerNotifier.single;
if (player == null) {
lives--;
if (lives == 0) {
add(GameOverComponent());
} else {
add(Player());
}
}
});
}
}
```
Expand Down Expand Up @@ -1152,16 +1166,17 @@ Then our hud component could look like:
```dart
class Hud extends PositionComponent with HasGameRef {
Future<void> onLoad() {
final playerNotifier = gameRef.componentsNotifier<Player>()
..addListener(() {
final player = playerNotifier.single;
if (player != null) {
if (player.health <= .5) {
add(BlinkEffect());
}
}
});
@override
void onLoad() {
final playerNotifier = gameRef.componentsNotifier<Player>()
..addListener(() {
final player = playerNotifier.single;
if (player != null) {
if (player.health <= .5) {
add(BlinkEffect());
}
}
});
}
}
```
Expand Down
Expand Up @@ -79,10 +79,9 @@ class CalculatePrimeNumber extends PositionComponent
DiscardNewBackPressureStrategy();

@override
Future<void>? onLoad() {
void onLoad() {
width = 200;
height = 70;
return super.onLoad();
}

@override
Expand Down
Expand Up @@ -97,7 +97,7 @@ class Bezel extends PositionComponent {
late final Paint specularPaint;

@override
Future<void> onLoad() async {
void onLoad() {
rim = Path()..addOval(Rect.fromLTRB(-radius, -radius, radius, radius));
final outer = radius + rimWidth / 2;
final inner = radius - rimWidth / 2;
Expand Down
Expand Up @@ -12,12 +12,11 @@ class BouncingBallExample extends FlameGame with HasCollisionDetection {
collides with the screen boundaries and then update it to bounce off these boundaries.
''';
@override
Future<void>? onLoad() {
void onLoad() {
addAll([
ScreenHitbox(),
Ball(),
]);
return super.onLoad();
}
}

Expand Down
Expand Up @@ -30,7 +30,7 @@ This examples showcases how raycast APIs can be used to detect hits within certa
)..positionType = PositionType.viewport;

@override
Future<void>? onLoad() {
void onLoad() {
camera.viewport = FixedResolutionViewport(Vector2(320, 180));

_addMovingWall();
Expand All @@ -49,8 +49,6 @@ This examples showcases how raycast APIs can be used to detect hits within certa
origin: _character.absolutePosition,
direction: Vector2(1, 0),
);

return super.onLoad();
}

void _addMovingWall() {
Expand Down

0 comments on commit d898b53

Please sign in to comment.