mirror of
https://github.com/flame-engine/flame.git
synced 2025-10-30 08:27:36 +08:00
feat: adding ParticleSystemComponent (#1489)
This commit is contained in:
@ -1,21 +1,21 @@
|
|||||||
# Particles
|
# Particles
|
||||||
|
|
||||||
Flame offers a basic, yet robust and extendable particle system. The core concept of this system is
|
Flame offers a basic, yet robust and extendable particle system. The core concept of this system is
|
||||||
the `Particle` class, which is very similar in its behavior to the `ParticleComponent`.
|
the `Particle` class, which is very similar in its behavior to the `ParticleSystemComponent`.
|
||||||
|
|
||||||
The most basic usage of a `Particle` with `FlameGame` would look as following:
|
The most basic usage of a `Particle` with `FlameGame` would look as following:
|
||||||
|
|
||||||
```dart
|
```dart
|
||||||
import 'package:flame/components.dart';
|
import 'package:flame/components.dart';
|
||||||
|
|
||||||
// ...
|
// ...
|
||||||
|
|
||||||
game.add(
|
game.add(
|
||||||
// Wrapping a Particle with ParticleComponent
|
// Wrapping a Particle with ParticleSystemComponent
|
||||||
// which maps Component lifecycle hooks to Particle ones
|
// which maps Component lifecycle hooks to Particle ones
|
||||||
// and embeds a trigger for removing the component.
|
// and embeds a trigger for removing the component.
|
||||||
ParticleComponent(
|
ParticleSystemComponent(
|
||||||
CircleParticle(),
|
particle: CircleParticle(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
@ -40,15 +40,15 @@ Random rnd = Random();
|
|||||||
Vector2 randomVector2() => (Vector2.random(rnd) - Vector2.random(rnd)) * 200;
|
Vector2 randomVector2() => (Vector2.random(rnd) - Vector2.random(rnd)) * 200;
|
||||||
|
|
||||||
// Composition.
|
// Composition.
|
||||||
//
|
//
|
||||||
// Defining a particle effect as a set of nested behaviors from top to bottom, one within another:
|
// Defining a particle effect as a set of nested behaviors from top to bottom, one within another:
|
||||||
// ParticleComponent
|
// ParticleSystemComponent
|
||||||
// > ComposedParticle
|
// > ComposedParticle
|
||||||
// > AcceleratedParticle
|
// > AcceleratedParticle
|
||||||
// > CircleParticle
|
// > CircleParticle
|
||||||
game.add(
|
game.add(
|
||||||
ParticleComponent(
|
ParticleSystemComponent(
|
||||||
Particle.generate(
|
particle: Particle.generate(
|
||||||
count: 10,
|
count: 10,
|
||||||
generator: (i) => AcceleratedParticle(
|
generator: (i) => AcceleratedParticle(
|
||||||
acceleration: randomVector2(),
|
acceleration: randomVector2(),
|
||||||
@ -61,12 +61,12 @@ game.add(
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Chaining.
|
// Chaining.
|
||||||
//
|
//
|
||||||
// Expresses the same behavior as above, but with a more fluent API.
|
// Expresses the same behavior as above, but with a more fluent API.
|
||||||
// Only Particles with SingleChildParticle mixin can be used as chainable behaviors.
|
// Only Particles with SingleChildParticle mixin can be used as chainable behaviors.
|
||||||
game.add(
|
game.add(
|
||||||
ParticleComponent(
|
ParticleSystemComponent(
|
||||||
Particle.generate(
|
particle: Particle.generate(
|
||||||
count: 10,
|
count: 10,
|
||||||
generator: (i) => pt.CircleParticle(paint: Paint()..color = Colors.red)
|
generator: (i) => pt.CircleParticle(paint: Paint()..color = Colors.red)
|
||||||
)
|
)
|
||||||
@ -74,19 +74,19 @@ game.add(
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Computed Particle.
|
// Computed Particle.
|
||||||
//
|
//
|
||||||
// All the behaviors are defined explicitly. Offers greater flexibility
|
// All the behaviors are defined explicitly. Offers greater flexibility
|
||||||
// compared to built-in behaviors.
|
// compared to built-in behaviors.
|
||||||
game.add(
|
game.add(
|
||||||
ParticleComponent(
|
ParticleSystemComponent(
|
||||||
Particle.generate(
|
particle: Particle.generate(
|
||||||
count: 10,
|
count: 10,
|
||||||
generator: (i) {
|
generator: (i) {
|
||||||
Vector2 position = Vector2.zero();
|
Vector2 position = Vector2.zero();
|
||||||
Vector2 speed = Vector2.zero();
|
Vector2 speed = Vector2.zero();
|
||||||
final acceleration = randomVector2();
|
final acceleration = randomVector2();
|
||||||
final paint = Paint()..color = Colors.red;
|
final paint = Paint()..color = Colors.red;
|
||||||
|
|
||||||
return ComputedParticle(
|
return ComputedParticle(
|
||||||
renderer: (canvas, _) {
|
renderer: (canvas, _) {
|
||||||
speed += acceleration;
|
speed += acceleration;
|
||||||
@ -107,10 +107,10 @@ You can find more examples of how to use different built-in particles in various
|
|||||||
## Lifecycle
|
## Lifecycle
|
||||||
|
|
||||||
A behavior common to all `Particle`s is that all of them accept a `lifespan` argument. This value is
|
A behavior common to all `Particle`s is that all of them accept a `lifespan` argument. This value is
|
||||||
used to make the `ParticleComponent` remove itself once its internal `Particle` has reached the end
|
used to make the `ParticleSystemComponent` remove itself once its internal `Particle` has reached
|
||||||
of its life. Time within the `Particle` itself is tracked using the Flame `Timer` class. It can be
|
the end of its life. Time within the `Particle` itself is tracked using the Flame `Timer` class. It
|
||||||
configured with a `double`, represented in seconds (with microsecond precision) by passing it into
|
can be configured with a `double`, represented in seconds (with microsecond precision) by passing
|
||||||
the corresponding `Particle` constructor.
|
it into the corresponding `Particle` constructor.
|
||||||
|
|
||||||
```dart
|
```dart
|
||||||
Particle(lifespan: .2); // will live for 200ms.
|
Particle(lifespan: .2); // will live for 200ms.
|
||||||
@ -118,7 +118,7 @@ Particle(lifespan: 4); // will live for 4s.
|
|||||||
```
|
```
|
||||||
|
|
||||||
It is also possible to reset a `Particle`'s lifespan by using the `setLifespan` method, which also
|
It is also possible to reset a `Particle`'s lifespan by using the `setLifespan` method, which also
|
||||||
accepts a `double` of seconds.
|
accepts a `double` of seconds.
|
||||||
|
|
||||||
```dart
|
```dart
|
||||||
final particle = Particle(lifespan: 2);
|
final particle = Particle(lifespan: 2);
|
||||||
@ -134,7 +134,7 @@ as the `value` property of the `AnimationController` class in Flutter.
|
|||||||
```dart
|
```dart
|
||||||
final particle = Particle(lifespan: 2.0);
|
final particle = Particle(lifespan: 2.0);
|
||||||
|
|
||||||
game.add(ParticleComponent(particle));
|
game.add(ParticleSystemComponent(particle: particle));
|
||||||
|
|
||||||
// Will print values from 0 to 1 with step of .1: 0, 0.1, 0.2 ... 0.9, 1.0.
|
// Will print values from 0 to 1 with step of .1: 0, 0.1, 0.2 ... 0.9, 1.0.
|
||||||
Timer.periodic(duration * .1, () => print(particle.progress));
|
Timer.periodic(duration * .1, () => print(particle.progress));
|
||||||
@ -172,8 +172,8 @@ layer.
|
|||||||
|
|
||||||
```dart
|
```dart
|
||||||
game.add(
|
game.add(
|
||||||
ParticleComponent(
|
ParticleSystemComponent(
|
||||||
TranslatedParticle(
|
particle: TranslatedParticle(
|
||||||
// Will translate the child Particle effect to the center of game canvas.
|
// Will translate the child Particle effect to the center of game canvas.
|
||||||
offset: game.size / 2,
|
offset: game.size / 2,
|
||||||
child: Particle(),
|
child: Particle(),
|
||||||
@ -189,8 +189,8 @@ Moves the child `Particle` between the `from` and `to` `Vector2`s during its lif
|
|||||||
|
|
||||||
```dart
|
```dart
|
||||||
game.add(
|
game.add(
|
||||||
ParticleComponent(
|
ParticleSystemComponent(
|
||||||
MovingParticle(
|
particle: MovingParticle(
|
||||||
// Will move from corner to corner of the game canvas.
|
// Will move from corner to corner of the game canvas.
|
||||||
from: Vector2.zero(),
|
from: Vector2.zero(),
|
||||||
to: game.size,
|
to: game.size,
|
||||||
@ -216,8 +216,8 @@ final rnd = Random();
|
|||||||
Vector2 randomVector2() => (Vector2.random(rnd) - Vector2.random(rnd)) * 100;
|
Vector2 randomVector2() => (Vector2.random(rnd) - Vector2.random(rnd)) * 100;
|
||||||
|
|
||||||
game.add(
|
game.add(
|
||||||
ParticleComponent(
|
ParticleSystemComponent(
|
||||||
AcceleratedParticle(
|
particle: AcceleratedParticle(
|
||||||
// Will fire off in the center of game canvas
|
// Will fire off in the center of game canvas
|
||||||
position: game.canvasSize/2,
|
position: game.canvasSize/2,
|
||||||
// With random initial speed of Vector2(-100..100, 0..-100)
|
// With random initial speed of Vector2(-100..100, 0..-100)
|
||||||
@ -241,8 +241,8 @@ desired positioning.
|
|||||||
|
|
||||||
```dart
|
```dart
|
||||||
game.add(
|
game.add(
|
||||||
ParticleComponent(
|
ParticleSystemComponent(
|
||||||
CircleParticle(
|
particle: CircleParticle(
|
||||||
radius: game.size.x / 2,
|
radius: game.size.x / 2,
|
||||||
paint: Paint()..color = Colors.red.withOpacity(.5),
|
paint: Paint()..color = Colors.red.withOpacity(.5),
|
||||||
),
|
),
|
||||||
@ -256,8 +256,8 @@ Allows you to embed a `Sprite` into your particle effects.
|
|||||||
|
|
||||||
```dart
|
```dart
|
||||||
game.add(
|
game.add(
|
||||||
ParticleComponent(
|
ParticleSystemComponent(
|
||||||
SpriteParticle(
|
particle: SpriteParticle(
|
||||||
sprite: Sprite('sprite.png'),
|
sprite: Sprite('sprite.png'),
|
||||||
size: Vector2(64, 64),
|
size: Vector2(64, 64),
|
||||||
),
|
),
|
||||||
@ -267,7 +267,7 @@ game.add(
|
|||||||
|
|
||||||
## ImageParticle
|
## ImageParticle
|
||||||
|
|
||||||
Renders given `dart:ui` image within the particle tree.
|
Renders given `dart:ui` image within the particle tree.
|
||||||
|
|
||||||
```dart
|
```dart
|
||||||
// During game initialisation
|
// During game initialisation
|
||||||
@ -281,8 +281,8 @@ await Flame.images.loadAll(const [
|
|||||||
final image = await Flame.images.load('image.png');
|
final image = await Flame.images.load('image.png');
|
||||||
|
|
||||||
game.add(
|
game.add(
|
||||||
ParticleComponent(
|
ParticleSystemComponent(
|
||||||
ImageParticle(
|
particle: ImageParticle(
|
||||||
size: Vector2.all(24),
|
size: Vector2.all(24),
|
||||||
image: image,
|
image: image,
|
||||||
);
|
);
|
||||||
@ -303,8 +303,8 @@ final spritesheet = SpriteSheet(
|
|||||||
);
|
);
|
||||||
|
|
||||||
game.add(
|
game.add(
|
||||||
ParticleComponent(
|
ParticleSystemComponent(
|
||||||
AnimationParticle(
|
particle: AnimationParticle(
|
||||||
animation: spritesheet.createAnimation(0, stepTime: 0.1),
|
animation: spritesheet.createAnimation(0, stepTime: 0.1),
|
||||||
);
|
);
|
||||||
),
|
),
|
||||||
@ -322,8 +322,8 @@ to the `game` directly, without the `Particle` in the middle.
|
|||||||
final longLivingRect = RectComponent();
|
final longLivingRect = RectComponent();
|
||||||
|
|
||||||
game.add(
|
game.add(
|
||||||
ParticleComponent(
|
ParticleSystemComponent(
|
||||||
ComponentParticle(
|
particle: ComponentParticle(
|
||||||
component: longLivingRect
|
component: longLivingRect
|
||||||
);
|
);
|
||||||
),
|
),
|
||||||
@ -332,7 +332,7 @@ game.add(
|
|||||||
class RectComponent extends Component {
|
class RectComponent extends Component {
|
||||||
void render(Canvas c) {
|
void render(Canvas c) {
|
||||||
c.drawRect(
|
c.drawRect(
|
||||||
Rect.fromCenter(center: Offset.zero, width: 100, height: 100),
|
Rect.fromCenter(center: Offset.zero, width: 100, height: 100),
|
||||||
Paint()..color = Colors.red
|
Paint()..color = Colors.red
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -357,13 +357,13 @@ import 'package:flame_flare/flame_flare.dart';
|
|||||||
// Within your game or component's `onLoad` method
|
// Within your game or component's `onLoad` method
|
||||||
const flareSize = 32.0;
|
const flareSize = 32.0;
|
||||||
final flareAnimation = FlareActorAnimation('assets/sparkle.flr');
|
final flareAnimation = FlareActorAnimation('assets/sparkle.flr');
|
||||||
flareAnimation.width = flareSize;
|
flareAnimation.width = flareSize;
|
||||||
flareAnimation.height = flareSize;
|
flareAnimation.height = flareSize;
|
||||||
|
|
||||||
// Somewhere in game
|
// Somewhere in game
|
||||||
game.add(
|
game.add(
|
||||||
ParticleComponent(
|
ParticleSystemComponent(
|
||||||
FlareParticle(flare: flareAnimation),
|
particle: FlareParticle(flare: flareAnimation),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
@ -380,9 +380,9 @@ on each frame to perform necessary computations and render something to the `Can
|
|||||||
|
|
||||||
```dart
|
```dart
|
||||||
game.add(
|
game.add(
|
||||||
ParticleComponent(
|
ParticleSystemComponent(
|
||||||
// Renders a circle which gradually changes its color and size during the particle lifespan.
|
// Renders a circle which gradually changes its color and size during the particle lifespan.
|
||||||
ComputedParticle(
|
particle: ComputedParticle(
|
||||||
renderer: (canvas, particle) => canvas.drawCircle(
|
renderer: (canvas, particle) => canvas.drawCircle(
|
||||||
Offset.zero,
|
Offset.zero,
|
||||||
particle.progress * 10,
|
particle.progress * 10,
|
||||||
|
|||||||
@ -96,8 +96,8 @@ class ParticlesExample extends FlameGame with FPSCounter {
|
|||||||
add(
|
add(
|
||||||
// Bind all the particles to a [Component] update
|
// Bind all the particles to a [Component] update
|
||||||
// lifecycle from the [FlameGame].
|
// lifecycle from the [FlameGame].
|
||||||
ParticleComponent(
|
ParticleSystemComponent(
|
||||||
TranslatedParticle(
|
particle: TranslatedParticle(
|
||||||
lifespan: 1,
|
lifespan: 1,
|
||||||
offset: cellCenter,
|
offset: cellCenter,
|
||||||
child: particle,
|
child: particle,
|
||||||
|
|||||||
@ -0,0 +1,59 @@
|
|||||||
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'package:flame/components.dart';
|
||||||
|
import 'package:flame/game.dart';
|
||||||
|
import 'package:flame/input.dart';
|
||||||
|
import 'package:flame/particles.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class ParticlesInteractiveExample extends FlameGame with PanDetector {
|
||||||
|
static const description = 'An example which shows how '
|
||||||
|
'ParticleSystemComponent can be added in runtime '
|
||||||
|
'following an event, in this example, the mouse '
|
||||||
|
'dragging';
|
||||||
|
|
||||||
|
final random = Random();
|
||||||
|
final Tween<double> noise = Tween(begin: -1, end: 1);
|
||||||
|
final ColorTween colorTween;
|
||||||
|
final double zoom;
|
||||||
|
|
||||||
|
ParticlesInteractiveExample({
|
||||||
|
required Color from,
|
||||||
|
required Color to,
|
||||||
|
required this.zoom,
|
||||||
|
}) : colorTween = ColorTween(begin: from, end: to);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> onLoad() async {
|
||||||
|
camera.followVector2(Vector2.zero());
|
||||||
|
|
||||||
|
camera.zoom = zoom;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onPanUpdate(DragUpdateInfo info) {
|
||||||
|
add(
|
||||||
|
ParticleSystemComponent(
|
||||||
|
position: info.eventPosition.game,
|
||||||
|
particle: Particle.generate(
|
||||||
|
count: 40,
|
||||||
|
generator: (i) {
|
||||||
|
return AcceleratedParticle(
|
||||||
|
lifespan: 2,
|
||||||
|
speed: Vector2(
|
||||||
|
noise.transform(random.nextDouble()),
|
||||||
|
noise.transform(random.nextDouble()),
|
||||||
|
) *
|
||||||
|
i.toDouble(),
|
||||||
|
child: CircleParticle(
|
||||||
|
radius: 2,
|
||||||
|
paint: Paint()
|
||||||
|
..color = colorTween.transform(random.nextDouble())!,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,5 +1,6 @@
|
|||||||
import 'package:dashbook/dashbook.dart';
|
import 'package:dashbook/dashbook.dart';
|
||||||
import 'package:flame/game.dart';
|
import 'package:flame/game.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import '../../commons/commons.dart';
|
import '../../commons/commons.dart';
|
||||||
import 'flip_sprite_example.dart';
|
import 'flip_sprite_example.dart';
|
||||||
@ -7,6 +8,7 @@ import 'isometric_tile_map_example.dart';
|
|||||||
import 'layers_example.dart';
|
import 'layers_example.dart';
|
||||||
import 'nine_tile_box_example.dart';
|
import 'nine_tile_box_example.dart';
|
||||||
import 'particles_example.dart';
|
import 'particles_example.dart';
|
||||||
|
import 'particles_interactive_example.dart';
|
||||||
import 'text_example.dart';
|
import 'text_example.dart';
|
||||||
|
|
||||||
void addRenderingStories(Dashbook dashbook) {
|
void addRenderingStories(Dashbook dashbook) {
|
||||||
@ -46,5 +48,17 @@ void addRenderingStories(Dashbook dashbook) {
|
|||||||
(_) => GameWidget(game: ParticlesExample()),
|
(_) => GameWidget(game: ParticlesExample()),
|
||||||
codeLink: baseLink('rendering/particles_example.dart'),
|
codeLink: baseLink('rendering/particles_example.dart'),
|
||||||
info: ParticlesExample.description,
|
info: ParticlesExample.description,
|
||||||
|
)
|
||||||
|
..add(
|
||||||
|
'Particles (Interactive)',
|
||||||
|
(context) => GameWidget(
|
||||||
|
game: ParticlesInteractiveExample(
|
||||||
|
from: context.colorProperty('From color', Colors.pink),
|
||||||
|
to: context.colorProperty('To color', Colors.blue),
|
||||||
|
zoom: context.numberProperty('Zoom', 1),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
codeLink: baseLink('rendering/particles_interactive_example.dart'),
|
||||||
|
info: ParticlesInteractiveExample.description,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,6 +18,7 @@ export 'src/components/mixins/tappable.dart';
|
|||||||
export 'src/components/nine_tile_box_component.dart';
|
export 'src/components/nine_tile_box_component.dart';
|
||||||
export 'src/components/parallax_component.dart';
|
export 'src/components/parallax_component.dart';
|
||||||
export 'src/components/particle_component.dart';
|
export 'src/components/particle_component.dart';
|
||||||
|
export 'src/components/particle_system_component.dart';
|
||||||
export 'src/components/position_component.dart';
|
export 'src/components/position_component.dart';
|
||||||
export 'src/components/position_type.dart';
|
export 'src/components/position_type.dart';
|
||||||
export 'src/components/sprite_animation_component.dart';
|
export 'src/components/sprite_animation_component.dart';
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import 'component.dart';
|
|||||||
/// to a [Component] tree. Could be added either to FlameGame
|
/// to a [Component] tree. Could be added either to FlameGame
|
||||||
/// or an implementation of [Component].
|
/// or an implementation of [Component].
|
||||||
/// Proxies [Component] lifecycle hooks to nested [Particle].
|
/// Proxies [Component] lifecycle hooks to nested [Particle].
|
||||||
|
@Deprecated('Will be removed after v1.1, use ParticleSystemComponent instead')
|
||||||
class ParticleComponent extends Component {
|
class ParticleComponent extends Component {
|
||||||
Particle particle;
|
Particle particle;
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,53 @@
|
|||||||
|
import 'dart:ui';
|
||||||
|
|
||||||
|
import '../../components.dart';
|
||||||
|
import '../../particles.dart';
|
||||||
|
|
||||||
|
/// {@template particle_system_component}
|
||||||
|
/// A [PositionComponent] that renders a [Particle] at the designated
|
||||||
|
/// position, scaled to have the designated size and rotated to the specified
|
||||||
|
/// angle.
|
||||||
|
/// {endtempalte}
|
||||||
|
class ParticleSystemComponent extends PositionComponent {
|
||||||
|
Particle? particle;
|
||||||
|
|
||||||
|
/// {@macro particle_system_component}
|
||||||
|
ParticleSystemComponent({
|
||||||
|
this.particle,
|
||||||
|
Vector2? position,
|
||||||
|
Vector2? size,
|
||||||
|
Vector2? scale,
|
||||||
|
double? angle,
|
||||||
|
Anchor? anchor,
|
||||||
|
int? priority,
|
||||||
|
}) : super(
|
||||||
|
position: position,
|
||||||
|
size: size,
|
||||||
|
scale: scale,
|
||||||
|
angle: angle,
|
||||||
|
anchor: anchor,
|
||||||
|
priority: priority,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Returns progress of the child [Particle].
|
||||||
|
///
|
||||||
|
/// Could be used by external code if needed.
|
||||||
|
double get progress => particle?.progress ?? 0;
|
||||||
|
|
||||||
|
/// Passes rendering chain down to the inset
|
||||||
|
/// [Particle] within this [Component].
|
||||||
|
@override
|
||||||
|
void render(Canvas canvas) {
|
||||||
|
super.render(canvas);
|
||||||
|
particle?.render(canvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Passes update chain to child [Particle].
|
||||||
|
@override
|
||||||
|
void update(double dt) {
|
||||||
|
particle?.update(dt);
|
||||||
|
if (particle?.shouldRemove ?? false) {
|
||||||
|
removeFromParent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,53 @@
|
|||||||
|
import 'package:canvas_test/canvas_test.dart';
|
||||||
|
import 'package:flame/components.dart';
|
||||||
|
import 'package:flame/game.dart';
|
||||||
|
import 'package:flame/particles.dart';
|
||||||
|
import 'package:flame_test/flame_test.dart';
|
||||||
|
import 'package:mocktail/mocktail.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
class MockParticle extends Mock implements Particle {}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('ParticleSystem', () {
|
||||||
|
test('returns the progress of its particle', () {
|
||||||
|
final particle = MockParticle();
|
||||||
|
when(() => particle.progress).thenReturn(0.2);
|
||||||
|
|
||||||
|
final progress = ParticleSystemComponent(particle: particle).progress;
|
||||||
|
expect(progress, equals(0.2));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('returns the progress of its particle', () {
|
||||||
|
final particle = MockParticle();
|
||||||
|
|
||||||
|
final canvas = MockCanvas();
|
||||||
|
ParticleSystemComponent(particle: particle).render(canvas);
|
||||||
|
|
||||||
|
verify(() => particle.render(canvas)).called(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('updates its particle', () {
|
||||||
|
final particle = MockParticle();
|
||||||
|
when(() => particle.shouldRemove).thenReturn(false);
|
||||||
|
|
||||||
|
ParticleSystemComponent(particle: particle).update(0.1);
|
||||||
|
verify(() => particle.update(0.1)).called(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWithFlameGame(
|
||||||
|
'is removed when its particle is finished',
|
||||||
|
(FlameGame game) async {
|
||||||
|
final particle = MockParticle();
|
||||||
|
when(() => particle.shouldRemove).thenReturn(true);
|
||||||
|
|
||||||
|
final component = ParticleSystemComponent(particle: particle);
|
||||||
|
await game.ensureAdd(component);
|
||||||
|
|
||||||
|
game.update(1);
|
||||||
|
|
||||||
|
expect(component.shouldRemove, isTrue);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user