diff --git a/doc/effects.md b/doc/effects.md index 04323ed28..6ff3fdf1a 100644 --- a/doc/effects.md +++ b/doc/effects.md @@ -23,7 +23,7 @@ When an effect is completed the callback `onComplete` will be called, it can be ## Common for MoveEffect, ScaleEffect and RotateEffect (SimplePositionComponentEffects) A common thing for `MoveEffect`, `ScaleEffect` and `RotateEffect` is that it takes `duration` and `speed` as arguments. -- Duration means the time it takes for one iteration from beginning to end without alternation +- Duration means the time it takes for one iteration from beginning to end, with alternation taken into account (but not `isInfinite`). - Speed is the speed of the effect - pixels/s for `MoveEffect` - pixels/s for `ScaleEffect` diff --git a/doc/examples/effects/combined_effects/lib/main.dart b/doc/examples/effects/combined_effects/lib/main.dart index 2505ca27b..40445326f 100644 --- a/doc/examples/effects/combined_effects/lib/main.dart +++ b/doc/examples/effects/combined_effects/lib/main.dart @@ -43,10 +43,11 @@ class MyGame extends BaseGame with TapDetector { currentTap - Vector2(50, 20), currentTap + Vector2.all(30), ], - duration: 1.0, + duration: 5.0, curve: Curves.linear, - isInfinite: false, + isInfinite: true, isAlternating: false, + onComplete: () => print(DateTime.now()), ); final scale = ScaleEffect( @@ -58,20 +59,21 @@ class MyGame extends BaseGame with TapDetector { ); final rotate = RotateEffect( - radians: (dx + dy) % pi, + angle: (dx + dy) % pi, duration: 3, curve: Curves.decelerate, isInfinite: false, isAlternating: false, ); - final combination = CombinedEffect( - effects: [move, rotate, scale], - isInfinite: false, - isAlternating: true, - offset: 0.5, - onComplete: () => print("onComplete callback"), - ); + //final combination = CombinedEffect( + // effects: [move, rotate, scale], + // isInfinite: false, + // isAlternating: true, + // offset: 0.5, + // onComplete: () => print("onComplete callback"), + //); greenSquare.addEffect(move); + print(DateTime.now()); } } diff --git a/doc/examples/effects/infinite_effects/lib/main.dart b/doc/examples/effects/infinite_effects/lib/main.dart index 2d3b5cc8e..4ce25c54f 100644 --- a/doc/examples/effects/infinite_effects/lib/main.dart +++ b/doc/examples/effects/infinite_effects/lib/main.dart @@ -60,7 +60,7 @@ class MyGame extends BaseGame with TapDetector { )); orangeSquare.addEffect(RotateEffect( - radians: (dx + dy) % (2 * pi), + angle: (dx + dy) % (2 * pi), speed: 1.0, // Radians per second curve: Curves.easeInOut, isInfinite: true, diff --git a/doc/examples/effects/sequence_effect/lib/main.dart b/doc/examples/effects/sequence_effect/lib/main.dart index fca274bea..7b9892126 100644 --- a/doc/examples/effects/sequence_effect/lib/main.dart +++ b/doc/examples/effects/sequence_effect/lib/main.dart @@ -63,7 +63,7 @@ class MyGame extends BaseGame with TapDetector { ); final rotate = RotateEffect( - radians: (dx + dy) % pi, + angle: (dx + dy) % pi, duration: 0.8, curve: Curves.decelerate, isInfinite: false, diff --git a/lib/effects/effects.dart b/lib/effects/effects.dart index 8e1d3e2f6..5b2fe5106 100644 --- a/lib/effects/effects.dart +++ b/lib/effects/effects.dart @@ -73,10 +73,6 @@ abstract class ComponentEffect { @mustCallSuper void initialize(T component) { this.component = component; - - /// You need to set the travelTime during the initialization of the - /// extending effect - travelTime = null; } void dispose() => _isDisposed = true; diff --git a/lib/effects/move_effect.dart b/lib/effects/move_effect.dart index 43347d7e8..2f399fefa 100644 --- a/lib/effects/move_effect.dart +++ b/lib/effects/move_effect.dart @@ -93,9 +93,10 @@ class MoveEffect extends SimplePositionComponentEffect { ); lastPosition = v; } - speed ??= pathLength / duration; - duration ??= pathLength / speed; - travelTime = duration; + final double totalPathLength = isAlternating ? pathLength * 2 : pathLength; + speed ??= totalPathLength / duration; + duration ??= totalPathLength / speed; + travelTime = isAlternating ? duration / 2 : duration; } @override @@ -108,10 +109,10 @@ class MoveEffect extends SimplePositionComponentEffect { @override void update(double dt) { - super.update(dt); if (hasFinished()) { return; } + super.update(dt); _currentSubPath ??= _percentagePath.first; if (!curveDirection.isNegative && _currentSubPath.endAt < curveProgress || curveDirection.isNegative && _currentSubPath.startAt > curveProgress) { diff --git a/lib/effects/rotate_effect.dart b/lib/effects/rotate_effect.dart index 9f4c8d07b..3a23276ac 100644 --- a/lib/effects/rotate_effect.dart +++ b/lib/effects/rotate_effect.dart @@ -4,12 +4,12 @@ import 'package:meta/meta.dart'; import 'effects.dart'; class RotateEffect extends SimplePositionComponentEffect { - double radians; + double angle; double _startAngle; double _delta; RotateEffect({ - @required this.radians, // As many radians as you want to rotate + @required this.angle, // As many radians as you want to rotate double duration, // How long it should take for completion double speed, // The speed of rotation in radians/s Curve curve, @@ -32,10 +32,10 @@ class RotateEffect extends SimplePositionComponentEffect { void initialize(_comp) { super.initialize(_comp); if (!isAlternating) { - endAngle = _comp.angle + radians; + endAngle = _comp.angle + angle; } _startAngle = component.angle; - _delta = isRelative ? radians : radians - _startAngle; + _delta = isRelative ? angle : angle - _startAngle; speed ??= _delta / duration; duration ??= _delta / speed; travelTime = duration; diff --git a/test/effects/move_effect_test.dart b/test/effects/move_effect_test.dart new file mode 100644 index 000000000..664b9a27b --- /dev/null +++ b/test/effects/move_effect_test.dart @@ -0,0 +1,89 @@ +import 'dart:math'; + +import 'package:flame/components/position_component.dart'; +import 'package:flame/effects/effects.dart'; +import 'package:flame/extensions/vector2.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'effect_test_utils.dart'; + +void main() { + final Random random = Random(); + Vector2 randomVector2() => (Vector2.random(random) * 100)..round(); + final List path = List.generate(3, (i) => randomVector2()); + Square component() => Square(position: randomVector2()); + + MoveEffect effect(bool isInfinite, bool isAlternating) { + return MoveEffect( + path: path, + duration: random.nextDouble() * 10, + isInfinite: isInfinite, + isAlternating: isAlternating, + ); + } + + testWidgets('MoveEffect can move', (WidgetTester tester) async { + final MoveEffect moveEffect = effect(false, false); + effectTest( + tester, + component(), + moveEffect, + expectedPosition: path.last, + ); + }); + + testWidgets( + 'MoveEffect will stop moving after it is done', + (WidgetTester tester) async { + final MoveEffect moveEffect = effect(false, false); + effectTest( + tester, + component(), + moveEffect, + expectedPosition: path.last, + iterations: 1.5, + ); + }, + ); + + testWidgets('MoveEffect can alternate', (WidgetTester tester) async { + final MoveEffect moveEffect = effect(false, true); + final PositionComponent positionComponent = component(); + effectTest( + tester, + positionComponent, + moveEffect, + expectedPosition: positionComponent.position.clone(), + ); + }); + + testWidgets('MoveEffect alternation can peak', (WidgetTester tester) async { + final MoveEffect moveEffect = effect(false, true); + final PositionComponent positionComponent = component(); + print(path); + print(positionComponent.position); + effectTest( + tester, + positionComponent, + moveEffect, + expectedPosition: path.last, + hitEdges: true, + iterations: 1.4, + ); + }); + + testWidgets('MoveEffect can be infinite', (WidgetTester tester) async { + final MoveEffect moveEffect = effect(true, false); + final PositionComponent positionComponent = component(); + print(path); + print(positionComponent.position); + effectTest( + tester, + positionComponent, + moveEffect, + expectedPosition: path.last, + iterations: 1.0, + hasFinished: false, + ); + }); +} diff --git a/test/effects_test.dart b/test/effects_test.dart deleted file mode 100644 index edf598132..000000000 --- a/test/effects_test.dart +++ /dev/null @@ -1,88 +0,0 @@ -import 'dart:math'; -import 'dart:ui'; - -import 'package:flame/anchor.dart'; -import 'package:flame/components/position_component.dart'; -import 'package:flame/effects/effects.dart'; -import 'package:flame/extensions/vector2.dart'; -import 'package:flame/game.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; - -void main() { - final Random random = Random(); - Vector2 randomVector2() => (Vector2.random(random) * 100)..round(); - final List path = List.generate(3, (i) => randomVector2()); - - void moveEffectTest( - WidgetTester tester, - bool isInfinite, - bool isAlternating, - Vector2 expected, { - bool hasFinished = true, - double iterations = 1.0, - }) async { - final BaseGame game = BaseGame(); - final Vector2 position = randomVector2(); - final PositionComponent component = Square(position); - final double duration = random.nextDouble() * 10; - bool isCallbackCalled = false; - final MoveEffect effect = MoveEffect( - path: path, - duration: duration, - isInfinite: isInfinite, - isAlternating: isAlternating, - onComplete: () => isCallbackCalled = true, - ); - game.add(component); - component.addEffect(effect); - await tester.pumpWidget(game.widget); - double timeLeft = iterations * duration; - while(timeLeft > 0) { - final double stepDelta = random.nextDouble() / 10; - game.update(stepDelta); - timeLeft -= stepDelta; - } - expect(component.position, expected); - expect(effect.hasFinished(), hasFinished); - expect(isCallbackCalled, hasFinished); - game.update(0.0001); - expect(component.effects.isEmpty, hasFinished); - } - - testWidgets('MoveEffect can move', (WidgetTester tester) async { - moveEffectTest(tester, false, false, path.last); - }); - - testWidgets('MoveEffect can alternate', (WidgetTester tester) async { - print(path); - moveEffectTest(tester, false, true, path.first, iterations: 2.00); - }); - - testWidgets('MoveEffect can be infinite', (WidgetTester tester) async { - print(path); - moveEffectTest( - tester, - true, - false, - path.last, - iterations: 2, - hasFinished: false, - ); - }); -} - -class Square extends PositionComponent { - Square(Vector2 position, {double angle = 0.0}) { - size = Vector2.all(100.0); - this.position = position; - this.angle = angle; - anchor = Anchor.center; - } - - @override - void render(Canvas canvas) { - super.render(canvas); - canvas.drawRect(size.toRect(), Paint()..color = Colors.red); - } -}