diff --git a/assets/images/components/spaceship-drop-tube.png b/assets/images/components/spaceship-drop-tube.png new file mode 100644 index 00000000..4b299c2c Binary files /dev/null and b/assets/images/components/spaceship-drop-tube.png differ diff --git a/lib/game/components/jetpack_ramp.dart b/lib/game/components/jetpack_ramp.dart index 06d1e66d..a4df6301 100644 --- a/lib/game/components/jetpack_ramp.dart +++ b/lib/game/components/jetpack_ramp.dart @@ -49,8 +49,8 @@ class Jetpack extends Forge2DBlueprint { addAll([ rightOpening, leftOpening, - jetpackRamp, baseRight, + jetpackRamp, jetpackRampWallFg, ]); } diff --git a/lib/game/components/spaceship_exit_rail.dart b/lib/game/components/spaceship_exit_rail.dart index 262f1cc3..e4be9b31 100644 --- a/lib/game/components/spaceship_exit_rail.dart +++ b/lib/game/components/spaceship_exit_rail.dart @@ -1,10 +1,10 @@ // ignore_for_file: avoid_renaming_method_parameters import 'dart:math' as math; -import 'dart:ui'; -import 'package:flame/extensions.dart'; +import 'package:flame/components.dart'; import 'package:flame_forge2d/flame_forge2d.dart'; +import 'package:pinball/gen/assets.gen.dart'; import 'package:pinball_components/pinball_components.dart' hide Assets; /// {@template spaceship_exit_rail} @@ -12,10 +12,10 @@ import 'package:pinball_components/pinball_components.dart' hide Assets; /// {@endtemplate} class SpaceshipExitRail extends Forge2DBlueprint { /// {@macro spaceship_exit_rail} - SpaceshipExitRail({required this.position}); + SpaceshipExitRail(); - /// The [position] where the elements will be created - final Vector2 position; + /// Base priority for wall while be on jetpack ramp. + static const ballPriorityWhenOnSpaceshipExitRail = 2; @override void build(_) { @@ -23,127 +23,101 @@ class SpaceshipExitRail extends Forge2DBlueprint { SpaceshipExitRailEndBallContactCallback(), ]); - final spaceshipExitRailRamp = _SpaceshipExitRailRamp() - ..initialPosition = position; - final exitRail = SpaceshipExitRailEnd() - ..initialPosition = position + _SpaceshipExitRailRamp.exitPoint; + final exitRailRamp = _SpaceshipExitRailRamp(); + final exitRailEnd = SpaceshipExitRailEnd(); + final topBase = _SpaceshipExitRailBase(radius: 0.55) + ..initialPosition = Vector2(-26.15, 18.65); + final bottomBase = _SpaceshipExitRailBase(radius: 0.8) + ..initialPosition = Vector2(-25.5, -12.9); addAll([ - spaceshipExitRailRamp, - exitRail, + exitRailRamp, + exitRailEnd, + topBase, + bottomBase, ]); } } class _SpaceshipExitRailRamp extends BodyComponent with InitialPosition, Layered { - _SpaceshipExitRailRamp() : super(priority: 2) { + _SpaceshipExitRailRamp() + : super( + priority: SpaceshipExitRail.ballPriorityWhenOnSpaceshipExitRail - 1, + ) { + renderBody = false; layer = Layer.spaceshipExitRail; - // TODO(ruimiguel): remove color once asset is placed. - paint = Paint() - ..color = const Color.fromARGB(255, 249, 65, 3) - ..style = PaintingStyle.stroke; } - static final exitPoint = Vector2(9.2, -48.5); - List _createFixtureDefs() { - const entranceRotationAngle = 175 * math.pi / 180; - const curveRotationAngle = 275 * math.pi / 180; - const exitRotationAngle = 340 * math.pi / 180; - const width = 5.5; - final fixturesDefs = []; - final entranceWall = ArcShape( - center: Vector2(width / 2, 0), - arcRadius: width / 2, + final topArcShape = ArcShape( + center: Vector2(-35.5, 30.9), + arcRadius: 2.5, angle: math.pi, - rotation: entranceRotationAngle, + rotation: 2.9, ); - final entranceFixtureDef = FixtureDef(entranceWall); - fixturesDefs.add(entranceFixtureDef); - - final topLeftControlPoints = [ - Vector2(0, 0), - Vector2(10, .5), - Vector2(7, 4), - Vector2(15.5, 8.3), - ]; + final topArcFixtureDef = FixtureDef(topArcShape); + fixturesDefs.add(topArcFixtureDef); + final topLeftCurveShape = BezierCurveShape( - controlPoints: topLeftControlPoints, - )..rotate(curveRotationAngle); - final topLeftFixtureDef = FixtureDef(topLeftCurveShape); - fixturesDefs.add(topLeftFixtureDef); - - final topRightControlPoints = [ - Vector2(0, width), - Vector2(10, 6.5), - Vector2(7, 10), - Vector2(15.5, 13.2), - ]; - final topRightCurveShape = BezierCurveShape( - controlPoints: topRightControlPoints, - )..rotate(curveRotationAngle); - final topRightFixtureDef = FixtureDef(topRightCurveShape); - fixturesDefs.add(topRightFixtureDef); - - final mediumLeftControlPoints = [ - topLeftControlPoints.last, - Vector2(21, 12.9), - Vector2(30, 7.1), - Vector2(32, 4.8), - ]; - final mediumLeftCurveShape = BezierCurveShape( - controlPoints: mediumLeftControlPoints, - )..rotate(curveRotationAngle); - final mediumLeftFixtureDef = FixtureDef(mediumLeftCurveShape); - fixturesDefs.add(mediumLeftFixtureDef); - - final mediumRightControlPoints = [ - topRightControlPoints.last, - Vector2(21, 17.2), - Vector2(30, 12.1), - Vector2(32, 10.2), - ]; - final mediumRightCurveShape = BezierCurveShape( - controlPoints: mediumRightControlPoints, - )..rotate(curveRotationAngle); - final mediumRightFixtureDef = FixtureDef(mediumRightCurveShape); - fixturesDefs.add(mediumRightFixtureDef); - - final bottomLeftControlPoints = [ - mediumLeftControlPoints.last, - Vector2(40, -1), - Vector2(48, 1.9), - Vector2(50.5, 2.5), - ]; + controlPoints: [ + Vector2(-37.9, 30.4), + Vector2(-38, 23.9), + Vector2(-30.93, 18.2), + ], + ); + final topLeftCurveFixtureDef = FixtureDef(topLeftCurveShape); + fixturesDefs.add(topLeftCurveFixtureDef); + + final middleLeftCurveShape = BezierCurveShape( + controlPoints: [ + Vector2(-30.93, 18.2), + Vector2(-22.6, 10.3), + Vector2(-30, 0.2), + ], + ); + final middleLeftCurveFixtureDef = FixtureDef(middleLeftCurveShape); + fixturesDefs.add(middleLeftCurveFixtureDef); + final bottomLeftCurveShape = BezierCurveShape( - controlPoints: bottomLeftControlPoints, - )..rotate(curveRotationAngle); - final bottomLeftFixtureDef = FixtureDef(bottomLeftCurveShape); - fixturesDefs.add(bottomLeftFixtureDef); - - final bottomRightControlPoints = [ - mediumRightControlPoints.last, - Vector2(40, 4), - Vector2(46, 6.5), - Vector2(48.8, 7.6), - ]; + controlPoints: [ + Vector2(-30, 0.2), + Vector2(-36, -8.6), + Vector2(-32.04, -18.3), + ], + ); + final bottomLeftCurveFixtureDef = FixtureDef(bottomLeftCurveShape); + fixturesDefs.add(bottomLeftCurveFixtureDef); + + final topRightStraightShape = EdgeShape() + ..set( + Vector2(-33, 31.3), + Vector2(-27.2, 21.3), + ); + final topRightStraightFixtureDef = FixtureDef(topRightStraightShape); + fixturesDefs.add(topRightStraightFixtureDef); + + final middleRightCurveShape = BezierCurveShape( + controlPoints: [ + Vector2(-27.2, 21.3), + Vector2(-16.5, 11.4), + Vector2(-25.29, -1.7), + ], + ); + final middleRightCurveFixtureDef = FixtureDef(middleRightCurveShape); + fixturesDefs.add(middleRightCurveFixtureDef); + final bottomRightCurveShape = BezierCurveShape( - controlPoints: bottomRightControlPoints, - )..rotate(curveRotationAngle); - final bottomRightFixtureDef = FixtureDef(bottomRightCurveShape); - fixturesDefs.add(bottomRightFixtureDef); - - final exitWall = ArcShape( - center: exitPoint, - arcRadius: width / 2, - angle: math.pi, - rotation: exitRotationAngle, + controlPoints: [ + Vector2(-25.29, -1.7), + Vector2(-29.91, -8.5), + Vector2(-26.8, -15.7), + ], ); - final exitFixtureDef = FixtureDef(exitWall); - fixturesDefs.add(exitFixtureDef); + final bottomRightCurveFixtureDef = FixtureDef(bottomRightCurveShape); + fixturesDefs.add(bottomRightCurveFixtureDef); return fixturesDefs; } @@ -159,6 +133,52 @@ class _SpaceshipExitRailRamp extends BodyComponent return body; } + + @override + Future onLoad() async { + await super.onLoad(); + await _loadSprite(); + } + + Future _loadSprite() async { + final sprite = await gameRef.loadSprite( + Assets.images.components.spaceshipDropTube.path, + ); + final spriteComponent = SpriteComponent( + sprite: sprite, + size: Vector2(17.5, 55.7), + anchor: Anchor.center, + position: Vector2(-29.4, -5.7), + ); + + await add(spriteComponent); + } +} + +class _SpaceshipExitRailBase extends BodyComponent + with InitialPosition, Layered { + _SpaceshipExitRailBase({required this.radius}) + : super( + priority: SpaceshipExitRail.ballPriorityWhenOnSpaceshipExitRail + 1, + ) { + renderBody = false; + layer = Layer.board; + } + + final double radius; + + @override + Body createBody() { + final shape = CircleShape()..radius = radius; + + final fixtureDef = FixtureDef(shape); + + final bodyDef = BodyDef() + ..position = initialPosition + ..userData = this; + + return world.createBody(bodyDef)..createFixture(fixtureDef); + } } /// {@template spaceship_exit_rail_end} @@ -173,12 +193,18 @@ class SpaceshipExitRailEnd extends RampOpening { orientation: RampOrientation.down, insidePriority: 3, ) { + renderBody = false; layer = Layer.spaceshipExitRail; } @override Shape get shape { - return CircleShape()..radius = 1; + return ArcShape( + center: Vector2(-29, -17.8), + arcRadius: 2.5, + angle: math.pi * 0.8, + rotation: -0.16, + ); } } @@ -192,8 +218,7 @@ class SpaceshipExitRailEndBallContactCallback @override void begin(SpaceshipExitRailEnd exitRail, Ball ball, _) { ball - ..priority = 1 - ..gameRef.reorderChildren() + ..sendTo(exitRail.outsidePriority) ..layer = exitRail.outsideLayer; } } diff --git a/lib/game/game_assets.dart b/lib/game/game_assets.dart index dd8a5a26..dcb740a1 100644 --- a/lib/game/game_assets.dart +++ b/lib/game/game_assets.dart @@ -25,6 +25,7 @@ extension PinballGameAssetsX on PinballGame { images.load(Assets.images.components.spaceshipRamp.path), images.load(Assets.images.components.spaceshipRailingBg.path), images.load(Assets.images.components.spaceshipRailingFg.path), + images.load(Assets.images.components.spaceshipDropTube.path), ]); } } diff --git a/lib/game/pinball_game.dart b/lib/game/pinball_game.dart index cfda7241..cac338fe 100644 --- a/lib/game/pinball_game.dart +++ b/lib/game/pinball_game.dart @@ -45,9 +45,7 @@ class PinballGame extends Forge2DGame ); unawaited( addFromBlueprint( - SpaceshipExitRail( - position: Vector2(-34.3, 23.8), - ), + SpaceshipExitRail(), ), ); diff --git a/lib/gen/assets.gen.dart b/lib/gen/assets.gen.dart index 833327d3..7796df34 100644 --- a/lib/gen/assets.gen.dart +++ b/lib/gen/assets.gen.dart @@ -21,6 +21,10 @@ class $AssetsImagesComponentsGen { AssetGenImage get background => const AssetGenImage('assets/images/components/background.png'); + /// File path: assets/images/components/spaceship-drop-tube.png + AssetGenImage get spaceshipDropTube => + const AssetGenImage('assets/images/components/spaceship-drop-tube.png'); + /// File path: assets/images/components/spaceship_railing_bg.png AssetGenImage get spaceshipRailingBg => const AssetGenImage('assets/images/components/spaceship_railing_bg.png'); diff --git a/packages/geometry/test/src/geometry_test.dart b/packages/geometry/test/src/geometry_test.dart index e702f043..eb5e8d74 100644 --- a/packages/geometry/test/src/geometry_test.dart +++ b/packages/geometry/test/src/geometry_test.dart @@ -114,9 +114,9 @@ void main() { Vector2(0, 0), Vector2(10, 10), ], - step: 0.001, + step: 0.02, ); - expect(points.length, 1000); + expect(points.length, 50); }); }); diff --git a/packages/pinball_components/lib/src/components/spaceship.dart b/packages/pinball_components/lib/src/components/spaceship.dart index 78c6a990..588c0e33 100644 --- a/packages/pinball_components/lib/src/components/spaceship.dart +++ b/packages/pinball_components/lib/src/components/spaceship.dart @@ -196,8 +196,8 @@ class SpaceshipHole extends RampOpening { @override Shape get shape { return ArcShape( - center: Vector2(0, 4.2), - arcRadius: 6, + center: Vector2(0, 3.2), + arcRadius: 5, angle: 1, rotation: 60 * pi / 180, ); diff --git a/test/game/components/spaceship_exit_rail_test.dart b/test/game/components/spaceship_exit_rail_test.dart index 99afc808..edd81aab 100644 --- a/test/game/components/spaceship_exit_rail_test.dart +++ b/test/game/components/spaceship_exit_rail_test.dart @@ -31,29 +31,33 @@ void main() { when(() => fixture.filterData).thenReturn(filterData); }); + // TODO(alestiago): Make ContactCallback private and use `beginContact` + // instead. group('SpaceshipExitHoleBallContactCallback', () { - test('changes the ball priority on contact', () { + setUp(() { + when(() => ball.priority).thenReturn(1); when(() => exitRailEnd.outsideLayer).thenReturn(Layer.board); + when(() => exitRailEnd.outsidePriority).thenReturn(0); + }); + test('changes the ball priority on contact', () { SpaceshipExitRailEndBallContactCallback().begin( exitRailEnd, ball, MockContact(), ); - verify(() => ball.priority = 1).called(1); + verify(() => ball.sendTo(exitRailEnd.outsidePriority)).called(1); }); - test('reorders the game children', () { - when(() => exitRailEnd.outsideLayer).thenReturn(Layer.board); - + test('changes the ball layer on contact', () { SpaceshipExitRailEndBallContactCallback().begin( exitRailEnd, ball, MockContact(), ); - verify(game.reorderChildren).called(1); + verify(() => ball.layer = exitRailEnd.outsideLayer).called(1); }); }); });