mirror of
https://github.com/flame-engine/flame.git
synced 2025-11-01 10:38:17 +08:00
RemoveEffect (#1063)
* Added FlameAnimationController class * Added MainAnimationController class * Update doc comments * formatting * rename MainAnimationController * Added tests for StandardAnimationController * Added more tests * comment * Added changelog note * Export StandardAnimationController * formatting * Use a default for 'curve' parameter * rename onsetDelay -> startDelay * Added Transofm2DEffect * Added EffectComponent * Added .transform getter * formatting * Rename EffectComponent -> Effect * Add documentation for the Effect class * minor * Added a test for Effect class * Adding tests for removeOnFinish * Adding tests for onStart and onFinish * Also check the effect after reset * Fix-up merge * formatting * added doc-comments * changelog note * Added test for transform2DEffect * Adjusted comments * Make PositionComponent._transform public * change changelog * Added SimpleEffectController * Added DestroyEffect * Changelog note * Rename DestroyEffect -> RemoveEffect * Added example for RemoveEffect * flutter format * Move description of the RemoveEffectExample game * move the description again * minor
This commit is contained in:
@ -18,4 +18,11 @@ class CircleComponent extends PositionComponent {
|
|||||||
super.render(canvas);
|
super.render(canvas);
|
||||||
canvas.drawCircle(Offset(radius, radius), radius, paint);
|
canvas.drawCircle(Offset(radius, radius), radius, paint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool containsPoint(Vector2 point) {
|
||||||
|
final local = absoluteToLocal(point);
|
||||||
|
final center = Vector2.all(radius);
|
||||||
|
return local.distanceToSquared(center) <= radius * radius;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import 'combined_effect.dart';
|
|||||||
import 'infinite_effect.dart';
|
import 'infinite_effect.dart';
|
||||||
import 'move_effect.dart';
|
import 'move_effect.dart';
|
||||||
import 'opacity_effect.dart';
|
import 'opacity_effect.dart';
|
||||||
|
import 'remove_effect_example.dart';
|
||||||
import 'rotate_effect.dart';
|
import 'rotate_effect.dart';
|
||||||
import 'scale_effect.dart';
|
import 'scale_effect.dart';
|
||||||
import 'sequence_effect.dart';
|
import 'sequence_effect.dart';
|
||||||
@ -67,5 +68,11 @@ void addEffectsStories(Dashbook dashbook) {
|
|||||||
'Color Effect',
|
'Color Effect',
|
||||||
(_) => GameWidget(game: ColorEffectGame()),
|
(_) => GameWidget(game: ColorEffectGame()),
|
||||||
codeLink: baseLink('effects/color_effect.dart'),
|
codeLink: baseLink('effects/color_effect.dart'),
|
||||||
|
)
|
||||||
|
..add(
|
||||||
|
'Remove Effect',
|
||||||
|
(_) => GameWidget(game: RemoveEffectExample()),
|
||||||
|
codeLink: baseLink('effects/remove_effect_example.dart'),
|
||||||
|
info: RemoveEffectExample.description,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
41
examples/lib/stories/effects/remove_effect_example.dart
Normal file
41
examples/lib/stories/effects/remove_effect_example.dart
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'package:flame/components.dart';
|
||||||
|
import 'package:flame/game.dart';
|
||||||
|
import 'package:flame/input.dart';
|
||||||
|
import 'package:flame/src/effects2/remove_effect.dart'; // ignore: implementation_imports
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import '../../commons/circle_component.dart';
|
||||||
|
|
||||||
|
class RemoveEffectExample extends FlameGame with HasTappableComponents {
|
||||||
|
static const description = '''
|
||||||
|
Click on any circle to apply a RemoveEffect, which will make the circle
|
||||||
|
disappear after a 0.5 second delay.
|
||||||
|
''';
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onMount() {
|
||||||
|
super.onMount();
|
||||||
|
camera.viewport = FixedResolutionViewport(Vector2(400, 600));
|
||||||
|
final rng = Random();
|
||||||
|
for (var i = 0; i < 20; i++) {
|
||||||
|
add(_RandomCircle(rng));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _RandomCircle extends CircleComponent with Tappable {
|
||||||
|
_RandomCircle(Random rng) : super(radius: rng.nextDouble() * 30 + 10) {
|
||||||
|
position.setValues(
|
||||||
|
rng.nextDouble() * 320 + 40,
|
||||||
|
rng.nextDouble() * 520 + 40,
|
||||||
|
);
|
||||||
|
paint.color = Colors.primaries[rng.nextInt(Colors.primaries.length)];
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool onTapDown(TapDownInfo info) {
|
||||||
|
add(RemoveEffect(delay: 0.5));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -4,6 +4,7 @@
|
|||||||
- Added `StandardEffectController` class
|
- Added `StandardEffectController` class
|
||||||
- Refactored `Effect` class to use `EffectController`, added `Transform2DEffect` class
|
- Refactored `Effect` class to use `EffectController`, added `Transform2DEffect` class
|
||||||
- Clarified `TimerComponent` example
|
- Clarified `TimerComponent` example
|
||||||
|
- Added `RemoveEffect` and `SimpleEffectController`
|
||||||
|
|
||||||
## [1.0.0-releasecandidate.16]
|
## [1.0.0-releasecandidate.16]
|
||||||
- `changePriority` no longer breaks game loop iteration
|
- `changePriority` no longer breaks game loop iteration
|
||||||
|
|||||||
16
packages/flame/lib/src/effects2/remove_effect.dart
Normal file
16
packages/flame/lib/src/effects2/remove_effect.dart
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import 'effect.dart';
|
||||||
|
import 'simple_effect_controller.dart';
|
||||||
|
|
||||||
|
/// This simple effect, when attached to a component, will cause that component
|
||||||
|
/// to be removed from the game tree after `delay` seconds.
|
||||||
|
class RemoveEffect extends Effect {
|
||||||
|
RemoveEffect({double delay = 0.0})
|
||||||
|
: super(SimpleEffectController(delay: delay));
|
||||||
|
|
||||||
|
@override
|
||||||
|
void apply(double progress) {
|
||||||
|
if (progress == 1) {
|
||||||
|
parent?.removeFromParent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,54 @@
|
|||||||
|
import 'effect_controller.dart';
|
||||||
|
import 'standard_effect_controller.dart';
|
||||||
|
|
||||||
|
/// Simplest possible [EffectController], which supports an effect progressing
|
||||||
|
/// linearly over [duration] seconds.
|
||||||
|
///
|
||||||
|
/// The [duration] can be 0, in which case the effect will jump from 0 to 1
|
||||||
|
/// instantaneously.
|
||||||
|
///
|
||||||
|
/// The [delay] parameter allows to delay the start of the effect by the
|
||||||
|
/// specified number of seconds.
|
||||||
|
///
|
||||||
|
/// See also: [StandardEffectController]
|
||||||
|
class SimpleEffectController extends EffectController {
|
||||||
|
SimpleEffectController({
|
||||||
|
this.duration = 0.0,
|
||||||
|
this.delay = 0.0,
|
||||||
|
}) : assert(duration >= 0, 'duration cannot be negative: $duration'),
|
||||||
|
assert(delay >= 0, 'delay cannot be negative: $delay');
|
||||||
|
|
||||||
|
final double duration;
|
||||||
|
final double delay;
|
||||||
|
double _timer = 0.0;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get started => _timer >= delay;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get completed => _timer >= delay + duration;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get isInfinite => false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
double get progress {
|
||||||
|
// If duration == 0, then `completed == started`, and the middle case
|
||||||
|
// (which divides by duration) cannot occur.
|
||||||
|
return completed
|
||||||
|
? 1
|
||||||
|
: started
|
||||||
|
? (_timer - delay) / duration
|
||||||
|
: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void update(double dt) {
|
||||||
|
_timer += dt;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void reset() {
|
||||||
|
_timer = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
45
packages/flame/test/effects2/remove_effect_test.dart
Normal file
45
packages/flame/test/effects2/remove_effect_test.dart
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import 'package:flame/components.dart';
|
||||||
|
import 'package:flame/game.dart';
|
||||||
|
import 'package:flame/src/effects2/remove_effect.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('RemoveEffect', () {
|
||||||
|
test('no delay', () {
|
||||||
|
final game = FlameGame();
|
||||||
|
game.onGameResize(Vector2.all(1));
|
||||||
|
expect(game.children.length, 0);
|
||||||
|
final obj = Component();
|
||||||
|
game.add(obj);
|
||||||
|
game.update(0);
|
||||||
|
expect(game.children.length, 1);
|
||||||
|
|
||||||
|
// First `game.update()` invokes the destroy effect and schedules `obj`
|
||||||
|
// for deletion; second `game.update()` processes the deletion queue and
|
||||||
|
// actually removes the component
|
||||||
|
obj.add(RemoveEffect());
|
||||||
|
game.update(0);
|
||||||
|
game.update(0);
|
||||||
|
expect(game.children.length, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('delayed', () {
|
||||||
|
final game = FlameGame();
|
||||||
|
game.onGameResize(Vector2.all(1));
|
||||||
|
expect(game.children.length, 0);
|
||||||
|
final obj = Component();
|
||||||
|
game.add(obj);
|
||||||
|
game.update(0);
|
||||||
|
expect(game.children.length, 1);
|
||||||
|
|
||||||
|
obj.add(RemoveEffect(delay: 1));
|
||||||
|
game.update(0.5);
|
||||||
|
game.update(0);
|
||||||
|
expect(game.children.length, 1);
|
||||||
|
|
||||||
|
game.update(0.5);
|
||||||
|
game.update(0);
|
||||||
|
expect(game.children.length, 0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
129
packages/flame/test/effects2/simple_effect_controller_test.dart
Normal file
129
packages/flame/test/effects2/simple_effect_controller_test.dart
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
import 'package:flame/src/effects2/simple_effect_controller.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('SimpleEffectController', () {
|
||||||
|
test('default', () {
|
||||||
|
final ec = SimpleEffectController();
|
||||||
|
expect(ec.duration, 0);
|
||||||
|
expect(ec.delay, 0);
|
||||||
|
expect(ec.isInfinite, false);
|
||||||
|
expect(ec.started, true);
|
||||||
|
expect(ec.completed, true);
|
||||||
|
expect(ec.progress, 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('simple with duration', () {
|
||||||
|
final ec = SimpleEffectController(duration: 1);
|
||||||
|
expect(ec.delay, 0);
|
||||||
|
expect(ec.duration, 1);
|
||||||
|
expect(ec.progress, 0);
|
||||||
|
expect(ec.started, true);
|
||||||
|
expect(ec.completed, false);
|
||||||
|
expect(ec.isInfinite, false);
|
||||||
|
|
||||||
|
ec.update(0.5);
|
||||||
|
expect(ec.progress, 0.5);
|
||||||
|
expect(ec.started, true);
|
||||||
|
expect(ec.completed, false);
|
||||||
|
|
||||||
|
ec.update(0.5);
|
||||||
|
expect(ec.progress, 1);
|
||||||
|
expect(ec.started, true);
|
||||||
|
expect(ec.completed, true);
|
||||||
|
|
||||||
|
ec.update(0.00001);
|
||||||
|
expect(ec.progress, 1);
|
||||||
|
expect(ec.started, true);
|
||||||
|
expect(ec.completed, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('simple with delay', () {
|
||||||
|
final ec = SimpleEffectController(delay: 1);
|
||||||
|
expect(ec.isInfinite, false);
|
||||||
|
expect(ec.started, false);
|
||||||
|
expect(ec.completed, false);
|
||||||
|
expect(ec.progress, 0);
|
||||||
|
expect(ec.delay, 1);
|
||||||
|
expect(ec.duration, 0);
|
||||||
|
|
||||||
|
ec.update(0.5);
|
||||||
|
expect(ec.started, false);
|
||||||
|
expect(ec.completed, false);
|
||||||
|
expect(ec.progress, 0);
|
||||||
|
|
||||||
|
ec.update(0.5);
|
||||||
|
expect(ec.started, true);
|
||||||
|
expect(ec.completed, true);
|
||||||
|
expect(ec.progress, 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('duration + delay', () {
|
||||||
|
final ec = SimpleEffectController(duration: 1, delay: 2);
|
||||||
|
expect(ec.isInfinite, false);
|
||||||
|
expect(ec.started, false);
|
||||||
|
expect(ec.completed, false);
|
||||||
|
expect(ec.duration, 1);
|
||||||
|
expect(ec.delay, 2);
|
||||||
|
expect(ec.progress, 0);
|
||||||
|
|
||||||
|
ec.update(0.5);
|
||||||
|
expect(ec.started, false);
|
||||||
|
expect(ec.progress, 0);
|
||||||
|
|
||||||
|
ec.update(0.5);
|
||||||
|
expect(ec.started, false);
|
||||||
|
expect(ec.progress, 0);
|
||||||
|
|
||||||
|
ec.update(1);
|
||||||
|
expect(ec.started, true);
|
||||||
|
expect(ec.completed, false);
|
||||||
|
expect(ec.progress, 0);
|
||||||
|
|
||||||
|
ec.update(0.5);
|
||||||
|
expect(ec.started, true);
|
||||||
|
expect(ec.completed, false);
|
||||||
|
expect(ec.progress, 0.5);
|
||||||
|
|
||||||
|
ec.update(0.5);
|
||||||
|
expect(ec.started, true);
|
||||||
|
expect(ec.completed, true);
|
||||||
|
expect(ec.progress, 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('reset', () {
|
||||||
|
final ec = SimpleEffectController();
|
||||||
|
ec.reset();
|
||||||
|
expect(ec.started, true);
|
||||||
|
expect(ec.completed, true);
|
||||||
|
expect(ec.progress, 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('reset 2', () {
|
||||||
|
final ec = SimpleEffectController(duration: 2, delay: 1);
|
||||||
|
ec.update(3);
|
||||||
|
expect(ec.completed, true);
|
||||||
|
expect(ec.progress, 1);
|
||||||
|
|
||||||
|
ec.reset();
|
||||||
|
expect(ec.started, false);
|
||||||
|
expect(ec.completed, false);
|
||||||
|
expect(ec.progress, 0);
|
||||||
|
|
||||||
|
ec.update(1);
|
||||||
|
expect(ec.started, true);
|
||||||
|
expect(ec.completed, false);
|
||||||
|
expect(ec.progress, 0);
|
||||||
|
|
||||||
|
ec.update(1);
|
||||||
|
expect(ec.started, true);
|
||||||
|
expect(ec.completed, false);
|
||||||
|
expect(ec.progress, closeTo(0.5, 1e-15));
|
||||||
|
|
||||||
|
ec.update(1);
|
||||||
|
expect(ec.started, true);
|
||||||
|
expect(ec.completed, true);
|
||||||
|
expect(ec.progress, 1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user