Skip to content

Commit

Permalink
feat: MoveAlongPathEffect should maintain initial angle of the compon…
Browse files Browse the repository at this point in the history
…ent (#2835)

See #2833
  • Loading branch information
ivanfemia committed Nov 10, 2023
1 parent 87b69df commit e6e78c0
Show file tree
Hide file tree
Showing 2 changed files with 117 additions and 8 deletions.
9 changes: 7 additions & 2 deletions packages/flame/lib/src/effects/move_along_path_effect.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import 'dart:ui';

import 'package:flame/components.dart';
import 'package:flame/src/effects/controllers/effect_controller.dart';
import 'package:flame/src/effects/move_effect.dart';
import 'package:flame/src/effects/provider_interfaces.dart';
import 'package:vector_math/vector_math_64.dart';

/// This effect will move the target along the specified path, which may
/// contain curved segments, but must be simply-connected.
Expand Down Expand Up @@ -85,7 +85,12 @@ class MoveAlongPathEffect extends MoveEffect {
'An `oriented` MoveAlongPathEffect cannot be applied to a target that '
'does not support rotation',
);
(target as AngleProvider).angle = _lastAngle = -start.angle;
_lastAngle = -start.angle;
final targetProvider = target;
(targetProvider as AngleProvider).angle = -start.angle;
if (targetProvider is PositionComponent) {
targetProvider.angle += targetProvider.nativeAngle;
}
}
if (_isAbsolute) {
target.position.x = _lastOffset.x = start.position.dx;
Expand Down
116 changes: 110 additions & 6 deletions packages/flame/test/effects/move_along_path_effect_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -82,15 +82,67 @@ void main() {
if (i <= 15) {
expect(component.position.x, closeTo(200 + 6 * i, 1e-10));
expect(component.position.y, closeTo(200 - 8 * i, 1e-10));
expect(component.angle, closeTo(-asin(0.8), 1e-7));
expect(
component.angle,
closeTo(-asin(0.8) + component.nativeAngle, 1e-7),
);
} else if (i <= 35) {
expect(component.position.x, closeTo(290 + 8 * (i - 15), 1e-10));
expect(component.position.y, closeTo(80 + 6 * (i - 15), 1e-10));
expect(component.angle, closeTo(asin(0.6), 1e-7));
expect(
component.angle,
closeTo(asin(0.6) + component.nativeAngle, 1e-7),
);
} else {
expect(component.position.x, closeTo(450 - 10 * (i - 35), 1e-10));
expect(component.position.y, closeTo(200, 1e-10));
expect(component.angle, closeTo(pi, 1e-7));
expect(component.angle, closeTo(pi + component.nativeAngle, 1e-7));
}
game.update(0.1);
}
});

testWithFlameGame('absolute oriented path with nativeAngle', (game) async {
final component = PositionComponent(
position: Vector2(17, -5),
angle: -30.5,
nativeAngle: 20.5,
);
game.add(component);
game.update(0);

component.add(
MoveAlongPathEffect(
Path() // pythagorean triangle, perimeter=600
..moveTo(200, 200)
..lineTo(290, 80)
..lineTo(450, 200)
..lineTo(200, 200),
EffectController(duration: 6),
absolute: true,
oriented: true,
),
);
game.update(0);
for (var i = 0; i < 60; i++) {
if (i <= 15) {
expect(component.position.x, closeTo(200 + 6 * i, 1e-10));
expect(component.position.y, closeTo(200 - 8 * i, 1e-10));
expect(
component.angle,
closeTo(-asin(0.8) + component.nativeAngle, 1e-7),
);
} else if (i <= 35) {
expect(component.position.x, closeTo(290 + 8 * (i - 15), 1e-10));
expect(component.position.y, closeTo(80 + 6 * (i - 15), 1e-10));
expect(
component.angle,
closeTo(asin(0.6) + component.nativeAngle, 1e-7),
);
} else {
expect(component.position.x, closeTo(450 - 10 * (i - 35), 1e-10));
expect(component.position.y, closeTo(200, 1e-10));
expect(component.angle, closeTo(pi + component.nativeAngle, 1e-7));
}
game.update(0.1);
}
Expand Down Expand Up @@ -143,15 +195,67 @@ void main() {
if (i <= 15) {
expect(component.position.x, closeTo(200 + 6 * i, 1e-10));
expect(component.position.y, closeTo(200 - 8 * i, 1e-10));
expect(component.angle, closeTo(-asin(0.8), 1e-7));
expect(
component.angle,
closeTo(-asin(0.8) + component.nativeAngle, 1e-7),
);
} else if (i <= 35) {
expect(component.position.x, closeTo(290 + 8 * (i - 15), 1e-10));
expect(component.position.y, closeTo(80 + 6 * (i - 15), 1e-10));
expect(
component.angle,
closeTo(asin(0.6) + component.nativeAngle, 1e-7),
);
} else {
expect(component.position.x, closeTo(450 - 10 * (i - 35), 1e-10));
expect(component.position.y, closeTo(200, 1e-10));
expect(component.angle, closeTo(pi + component.nativeAngle, 1e-7));
}
game.update(0.1);
}
});

testWithFlameGame('non-absolute oriented path with nativeAngle',
(game) async {
final component = PositionComponent(
position: Vector2.zero(),
angle: -30.5,
nativeAngle: 20.5,
);
game.add(component);
game.update(0);

component.add(
MoveAlongPathEffect(
Path() // pythagorean triangle, perimeter=600
..moveTo(200, 200)
..lineTo(290, 80)
..lineTo(450, 200)
..lineTo(200, 200),
EffectController(duration: 6),
oriented: true,
),
);
game.update(0);
for (var i = 0; i < 60; i++) {
if (i <= 15) {
expect(component.position.x, closeTo(200 + 6 * i, 1e-10));
expect(component.position.y, closeTo(200 - 8 * i, 1e-10));
expect(
component.angle,
closeTo(-asin(0.8) + component.nativeAngle, 1e-7),
);
} else if (i <= 35) {
expect(component.position.x, closeTo(290 + 8 * (i - 15), 1e-10));
expect(component.position.y, closeTo(80 + 6 * (i - 15), 1e-10));
expect(component.angle, closeTo(asin(0.6), 1e-7));
expect(
component.angle,
closeTo(asin(0.6) + component.nativeAngle, 1e-7),
);
} else {
expect(component.position.x, closeTo(450 - 10 * (i - 35), 1e-10));
expect(component.position.y, closeTo(200, 1e-10));
expect(component.angle, closeTo(pi, 1e-7));
expect(component.angle, closeTo(pi + component.nativeAngle, 1e-7));
}
game.update(0.1);
}
Expand Down

0 comments on commit e6e78c0

Please sign in to comment.