Generalize position effect tests

This commit is contained in:
Lukas Klingsbo
2020-10-28 23:01:39 +01:00
parent 56baa115d8
commit c4e2049450
9 changed files with 113 additions and 113 deletions

View File

@ -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`

View File

@ -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());
}
}

View File

@ -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,

View File

@ -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,

View File

@ -73,10 +73,6 @@ abstract class ComponentEffect<T extends Component> {
@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;

View File

@ -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) {

View File

@ -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;

View File

@ -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<Vector2> 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,
);
});
}

View File

@ -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<Vector2> 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);
}
}