From e6e78c0d66bc958dbe1c2295a7cc946dc5852455 Mon Sep 17 00:00:00 2001 From: Ivan Date: Fri, 10 Nov 2023 03:10:26 -0600 Subject: [PATCH] feat: MoveAlongPathEffect should maintain initial angle of the component (#2835) See #2833 --- .../src/effects/move_along_path_effect.dart | 9 +- .../effects/move_along_path_effect_test.dart | 116 +++++++++++++++++- 2 files changed, 117 insertions(+), 8 deletions(-) diff --git a/packages/flame/lib/src/effects/move_along_path_effect.dart b/packages/flame/lib/src/effects/move_along_path_effect.dart index ea1762633b..296cf8f9d0 100644 --- a/packages/flame/lib/src/effects/move_along_path_effect.dart +++ b/packages/flame/lib/src/effects/move_along_path_effect.dart @@ -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. @@ -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; diff --git a/packages/flame/test/effects/move_along_path_effect_test.dart b/packages/flame/test/effects/move_along_path_effect_test.dart index 9927f23e02..db2483f673 100644 --- a/packages/flame/test/effects/move_along_path_effect_test.dart +++ b/packages/flame/test/effects/move_along_path_effect_test.dart @@ -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); } @@ -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); }