From 411b489e1eaacca43dda15e9071384654cdddedc Mon Sep 17 00:00:00 2001 From: Alejandro Santiago Date: Mon, 28 Mar 2022 12:00:52 +0100 Subject: [PATCH] refactor: Pathway to Shapes (#87) * refactor: removed findNested extensions (#77) * refactor: changed Pathway for Shapes * refactor: renamed pathway to shape * refactor: moved shapes to components package * fix: fixed arc radius on shapes * refactor: changed jetpack to shapes * refactor: modified jetpack ramp to use shapes and blueprint * refactor: launcher ramp * test: removed unnecessary tests for ramps * refactor: refactored baseboard with arcshapes * chore: doc refactor * test: coverage tests * refactor: refactored launcher ramp * test: tests for shapes * test: added removed ellipse tests * test: arcshape coverage * test: unnecessary tests removed * chore: params names * chore: modified doc for Layered and added one test for nested * test: changed tests names * test: not layered nested children * refactor: moved static param and made other private on ramps * Update lib/game/components/jetpack_ramp.dart Co-authored-by: Allison Ryan <77211884+allisonryan0002@users.noreply.github.com> * Update lib/game/components/jetpack_ramp.dart Co-authored-by: Allison Ryan <77211884+allisonryan0002@users.noreply.github.com> * Update lib/game/components/launcher_ramp.dart Co-authored-by: Allison Ryan <77211884+allisonryan0002@users.noreply.github.com> * chore: renamed straight path vars * Update packages/pinball_components/lib/src/components/shapes/arc_shape.dart Co-authored-by: Allison Ryan <77211884+allisonryan0002@users.noreply.github.com> * refactor: constructors with rotation instead of separated method * test: refactored tests * refactor: moved rotate to separate method * refactor: rotation on shapes Co-authored-by: RuiAlonso Co-authored-by: Rui Miguel Alonso Co-authored-by: Allison Ryan <77211884+allisonryan0002@users.noreply.github.com> --- lib/game/components/baseboard.dart | 22 +- lib/game/components/components.dart | 1 - lib/game/components/jetpack_ramp.dart | 115 ++++++--- lib/game/components/launcher_ramp.dart | 139 ++++++---- lib/game/components/pathway.dart | 209 --------------- lib/game/components/ramp_opening.dart | 6 +- lib/game/pinball_game.dart | 19 +- .../lib/src/components/components.dart | 1 + .../lib/src/components/layer.dart | 3 + .../lib/src/components/shapes/arc_shape.dart | 54 ++++ .../components/shapes/bezier_curve_shape.dart | 28 ++ .../src/components/shapes/ellipse_shape.dart | 53 ++++ .../lib/src/components/shapes/shapes.dart | 3 + packages/pinball_components/pubspec.yaml | 3 + .../test/src/components/layer_test.dart | 66 ++++- .../src/components/shapes/arc_shape_test.dart | 66 +++++ .../shapes/bezier_curve_shape_test.dart | 51 ++++ .../components/shapes/ellipse_shape_test.dart | 83 ++++++ test/game/bloc/game_state_test.dart | 4 +- test/game/components/jetpack_ramp_test.dart | 63 ----- test/game/components/launcher_ramp_test.dart | 74 ------ test/game/components/pathway_test.dart | 243 ------------------ test/game/pinball_game_test.dart | 26 -- 23 files changed, 591 insertions(+), 741 deletions(-) delete mode 100644 lib/game/components/pathway.dart create mode 100644 packages/pinball_components/lib/src/components/shapes/arc_shape.dart create mode 100644 packages/pinball_components/lib/src/components/shapes/bezier_curve_shape.dart create mode 100644 packages/pinball_components/lib/src/components/shapes/ellipse_shape.dart create mode 100644 packages/pinball_components/lib/src/components/shapes/shapes.dart create mode 100644 packages/pinball_components/test/src/components/shapes/arc_shape_test.dart create mode 100644 packages/pinball_components/test/src/components/shapes/bezier_curve_shape_test.dart create mode 100644 packages/pinball_components/test/src/components/shapes/ellipse_shape_test.dart delete mode 100644 test/game/components/jetpack_ramp_test.dart delete mode 100644 test/game/components/launcher_ramp_test.dart delete mode 100644 test/game/components/pathway_test.dart diff --git a/lib/game/components/baseboard.dart b/lib/game/components/baseboard.dart index d9dc3512..d4c326e3 100644 --- a/lib/game/components/baseboard.dart +++ b/lib/game/components/baseboard.dart @@ -46,25 +46,23 @@ class Baseboard extends BodyComponent with InitialPosition { final outerEdgeShapeFixtureDef = FixtureDef(outerEdgeShape); fixturesDef.add(outerEdgeShapeFixtureDef); - final upperArcFixtureDefs = Pathway.arc( + final upperArcShape = ArcShape( center: Vector2(1.76 * direction, 3.25), - width: 0, - radius: 6.1, + arcRadius: 6.1, angle: arcsAngle, rotation: arcsRotation, - singleWall: true, - ).createFixtureDefs(); - fixturesDef.addAll(upperArcFixtureDefs); + ); + final upperArcFixtureDefs = FixtureDef(upperArcShape); + fixturesDef.add(upperArcFixtureDefs); - final lowerArcFixtureDefs = Pathway.arc( + final lowerArcShape = ArcShape( center: Vector2(1.85 * direction, -2.15), - width: 0, - radius: 4.5, + arcRadius: 4.5, angle: arcsAngle, rotation: arcsRotation, - singleWall: true, - ).createFixtureDefs(); - fixturesDef.addAll(lowerArcFixtureDefs); + ); + final lowerArcFixtureDefs = FixtureDef(lowerArcShape); + fixturesDef.add(lowerArcFixtureDefs); final bottomRectangle = PolygonShape() ..setAsBox( diff --git a/lib/game/components/components.dart b/lib/game/components/components.dart index 07b036f6..d2c7c72d 100644 --- a/lib/game/components/components.dart +++ b/lib/game/components/components.dart @@ -9,7 +9,6 @@ export 'jetpack_ramp.dart'; export 'joint_anchor.dart'; export 'kicker.dart'; export 'launcher_ramp.dart'; -export 'pathway.dart'; export 'plunger.dart'; export 'ramp_opening.dart'; export 'score_points.dart'; diff --git a/lib/game/components/jetpack_ramp.dart b/lib/game/components/jetpack_ramp.dart index a69b2111..a24e1438 100644 --- a/lib/game/components/jetpack_ramp.dart +++ b/lib/game/components/jetpack_ramp.dart @@ -1,63 +1,102 @@ +// ignore_for_file: public_member_api_docs, avoid_renaming_method_parameters + import 'dart:math' as math; -import 'package:flame/components.dart'; import 'package:flame/extensions.dart'; import 'package:flame_forge2d/flame_forge2d.dart'; +import 'package:flutter/material.dart'; +import 'package:pinball/flame/blueprint.dart'; import 'package:pinball/game/game.dart'; import 'package:pinball_components/pinball_components.dart'; -/// {@template jetpack_ramp} -/// Represents the upper left blue ramp of the [Board]. -/// {@endtemplate} -class JetpackRamp extends Component with HasGameRef { - /// {@macro jetpack_ramp} - JetpackRamp({ - required this.position, - }); - - /// The position of this [JetpackRamp]. - final Vector2 position; - +/// A [Blueprint] which creates the [JetpackRamp]. +class Jetpack extends Forge2DBlueprint { @override - Future onLoad() async { - const layer = Layer.jetpack; + void build(_) { + final position = Vector2( + PinballGame.boardBounds.left + 40.5, + PinballGame.boardBounds.top - 31.5, + ); - gameRef.addContactCallback( + addAllContactCallback([ RampOpeningBallContactCallback<_JetpackRampOpening>(), - ); + ]); - final curvePath = Pathway.arc( - // TODO(ruialonso): Remove color when not needed. - // TODO(ruialonso): Use a bezier curve once control points are defined. - color: const Color.fromARGB(255, 8, 218, 241), - center: position, - width: 5, - radius: 18, - angle: math.pi, + final rightOpening = _JetpackRampOpening( rotation: math.pi, - )..layer = layer; - + ) + ..initialPosition = position + Vector2(12.9, -20) + ..layer = Layer.opening; final leftOpening = _JetpackRampOpening( outsideLayer: Layer.spaceship, rotation: math.pi, ) - ..initialPosition = position + Vector2(-2.5, -20.2) + ..initialPosition = position + Vector2(-2.5, -20) ..layer = Layer.jetpack; - final rightOpening = _JetpackRampOpening( - rotation: math.pi, - ) - ..initialPosition = position + Vector2(12.9, -20.2) - ..layer = Layer.opening; + final jetpackRamp = JetpackRamp() + ..initialPosition = position + Vector2(5, -20.2) + ..layer = Layer.jetpack; - await addAll([ - curvePath, - leftOpening, + addAll([ rightOpening, + leftOpening, + jetpackRamp, ]); } } +/// {@template jetpack_ramp} +/// Represents the upper left blue ramp of the [Board]. +/// {@endtemplate} +class JetpackRamp extends BodyComponent with InitialPosition, Layered { + JetpackRamp() : super(priority: 2) { + layer = Layer.jetpack; + paint = Paint() + ..color = const Color.fromARGB(255, 8, 218, 241) + ..style = PaintingStyle.stroke; + } + + /// Radius of the external arc. + static const _externalRadius = 18.0; + + /// Width between walls of the ramp. + static const width = 5.0; + + List _createFixtureDefs() { + final fixturesDef = []; + + final externalCurveShape = ArcShape( + center: initialPosition, + arcRadius: _externalRadius, + angle: math.pi, + rotation: math.pi, + ); + final externalFixtureDef = FixtureDef(externalCurveShape); + fixturesDef.add(externalFixtureDef); + + final internalCurveShape = externalCurveShape.copyWith( + arcRadius: _externalRadius - width, + ); + final internalFixtureDef = FixtureDef(internalCurveShape); + fixturesDef.add(internalFixtureDef); + + return fixturesDef; + } + + @override + Body createBody() { + final bodyDef = BodyDef() + ..userData = this + ..position = initialPosition; + + final body = world.createBody(bodyDef); + _createFixtureDefs().forEach(body.createFixture); + + return body; + } +} + /// {@template jetpack_ramp_opening} /// [RampOpening] with [Layer.jetpack] to filter [Ball] collisions /// inside [JetpackRamp]. @@ -76,9 +115,7 @@ class _JetpackRampOpening extends RampOpening { final double _rotation; - // TODO(ruialonso): Avoid magic number 3, should be propotional to - // [JetpackRamp]. - static final Vector2 _size = Vector2(3, .1); + static final Vector2 _size = Vector2(JetpackRamp.width / 3, .1); @override Shape get shape => PolygonShape() diff --git a/lib/game/components/launcher_ramp.dart b/lib/game/components/launcher_ramp.dart index 61b9e26f..5313a2d9 100644 --- a/lib/game/components/launcher_ramp.dart +++ b/lib/game/components/launcher_ramp.dart @@ -1,67 +1,118 @@ +// ignore_for_file: public_member_api_docs, avoid_renaming_method_parameters + import 'dart:math' as math; -import 'package:flame/components.dart'; import 'package:flame/extensions.dart'; import 'package:flame_forge2d/flame_forge2d.dart'; +import 'package:flutter/material.dart'; +import 'package:pinball/flame/blueprint.dart'; import 'package:pinball/game/game.dart'; import 'package:pinball_components/pinball_components.dart'; -/// {@template launcher_ramp} -/// The yellow left ramp, where the [Ball] goes through when launched from the -/// [Plunger]. -/// {@endtemplate} -class LauncherRamp extends Component with HasGameRef { - /// {@macro launcher_ramp} - LauncherRamp({ - required this.position, - }); - - /// The position of this [LauncherRamp]. - final Vector2 position; - +/// A [Blueprint] which creates the [LauncherRamp]. +class Launcher extends Forge2DBlueprint { @override - Future onLoad() async { - const layer = Layer.launcher; - - gameRef.addContactCallback( - RampOpeningBallContactCallback<_LauncherRampOpening>(), + void build(_) { + final position = Vector2( + PinballGame.boardBounds.right - 31.3, + PinballGame.boardBounds.bottom + 33, ); - final straightPath = Pathway.straight( - color: const Color.fromARGB(255, 34, 255, 0), - start: position + Vector2(-4.5, -10), - end: position + Vector2(-4.5, 117), - width: 4, - rotation: PinballGame.boardPerspectiveAngle, - ) - ..initialPosition = position - ..layer = layer; - - final curvedPath = Pathway.arc( - color: const Color.fromARGB(255, 251, 255, 0), - center: position + Vector2(-7, 87.2), - radius: 16.3, - angle: math.pi / 2, - width: 4, - rotation: 3 * math.pi / 2, - )..layer = layer; + addAllContactCallback([ + RampOpeningBallContactCallback<_LauncherRampOpening>(), + ]); final leftOpening = _LauncherRampOpening(rotation: math.pi / 2) - ..initialPosition = position + Vector2(-13.8, 66.7) + ..initialPosition = position + Vector2(-11.8, 72.7) ..layer = Layer.opening; final rightOpening = _LauncherRampOpening(rotation: 0) - ..initialPosition = position + Vector2(-6.8, 59.4) + ..initialPosition = position + Vector2(-5.4, 65.4) ..layer = Layer.opening; - await addAll([ - straightPath, - curvedPath, + final launcherRamp = LauncherRamp() + ..initialPosition = position + Vector2(1.7, 0) + ..layer = Layer.launcher; + + addAll([ leftOpening, rightOpening, + launcherRamp, ]); } } +/// {@template launcher_ramp} +/// The yellow right ramp, where the [Ball] goes through when launched from the +/// [Plunger]. +/// {@endtemplate} +class LauncherRamp extends BodyComponent with InitialPosition, Layered { + /// {@macro launcher_ramp} + LauncherRamp() : super(priority: 2) { + layer = Layer.launcher; + paint = Paint() + ..color = const Color.fromARGB(255, 251, 255, 0) + ..style = PaintingStyle.stroke; + } + + /// Width between walls of the ramp. + static const width = 5.0; + + /// Radius of the external arc at the top of the ramp. + static const _externalRadius = 16.3; + + List _createFixtureDefs() { + final fixturesDef = []; + + final startPosition = initialPosition + Vector2(0, 3); + final endPosition = initialPosition + Vector2(0, 130); + + final rightStraightShape = EdgeShape() + ..set( + startPosition..rotate(PinballGame.boardPerspectiveAngle), + endPosition..rotate(PinballGame.boardPerspectiveAngle), + ); + final rightStraightFixtureDef = FixtureDef(rightStraightShape); + fixturesDef.add(rightStraightFixtureDef); + + final leftStraightShape = EdgeShape() + ..set( + startPosition - Vector2(width, 0), + endPosition - Vector2(width, 0), + ); + final leftStraightFixtureDef = FixtureDef(leftStraightShape); + fixturesDef.add(leftStraightFixtureDef); + + final externalCurveShape = ArcShape( + center: initialPosition + Vector2(-28.2, 132), + arcRadius: _externalRadius, + angle: math.pi / 2, + rotation: 3 * math.pi / 2, + ); + final externalCurveFixtureDef = FixtureDef(externalCurveShape); + fixturesDef.add(externalCurveFixtureDef); + + final internalCurveShape = externalCurveShape.copyWith( + arcRadius: _externalRadius - width, + ); + final internalCurveFixtureDef = FixtureDef(internalCurveShape); + fixturesDef.add(internalCurveFixtureDef); + + return fixturesDef; + } + + @override + Body createBody() { + final bodyDef = BodyDef() + ..userData = this + ..position = initialPosition; + + final body = world.createBody(bodyDef); + _createFixtureDefs().forEach(body.createFixture); + + return body; + } +} + /// {@template launcher_ramp_opening} /// [RampOpening] with [Layer.launcher] to filter [Ball]s collisions /// inside [LauncherRamp]. @@ -78,9 +129,7 @@ class _LauncherRampOpening extends RampOpening { final double _rotation; - // TODO(ruialonso): Avoid magic number 2.5, should be propotional to - // [JetpackRamp]. - static final Vector2 _size = Vector2(2.5, .1); + static final Vector2 _size = Vector2(LauncherRamp.width / 3, .1); @override Shape get shape => PolygonShape() diff --git a/lib/game/components/pathway.dart b/lib/game/components/pathway.dart deleted file mode 100644 index 54c81464..00000000 --- a/lib/game/components/pathway.dart +++ /dev/null @@ -1,209 +0,0 @@ -import 'package:flame/extensions.dart'; -import 'package:flame_forge2d/flame_forge2d.dart'; -import 'package:flutter/material.dart'; -import 'package:geometry/geometry.dart'; -import 'package:pinball_components/pinball_components.dart'; - -/// {@template pathway} -/// [Pathway] creates lines of various shapes. -/// -/// [BodyComponent]s such as a Ball can collide and move along a [Pathway]. -/// {@endtemplate} -class Pathway extends BodyComponent with InitialPosition, Layered { - Pathway._({ - // TODO(ruialonso): remove color when assets added. - Color? color, - required List> paths, - }) : _paths = paths { - paint = Paint() - ..color = color ?? const Color.fromARGB(0, 0, 0, 0) - ..style = PaintingStyle.stroke; - } - - /// Creates a uniform unidirectional (straight) [Pathway]. - /// - /// Does so with two [ChainShape] separated by a [width]. Can - /// be rotated by a given [rotation] in radians. - /// - /// If [singleWall] is true, just one [ChainShape] is created. - factory Pathway.straight({ - Color? color, - required Vector2 start, - required Vector2 end, - required double width, - double rotation = 0, - bool singleWall = false, - }) { - final paths = >[]; - - // TODO(ruialonso): Refactor repetitive logic - final firstWall = [ - start.clone(), - end.clone(), - ].map((vector) => vector..rotate(rotation)).toList(); - paths.add(firstWall); - - if (!singleWall) { - final secondWall = [ - start + Vector2(width, 0), - end + Vector2(width, 0), - ].map((vector) => vector..rotate(rotation)).toList(); - paths.add(secondWall); - } - - return Pathway._( - color: color, - paths: paths, - ); - } - - /// Creates an arc [Pathway]. - /// - /// The [angle], in radians, specifies the size of the arc. For example, two - /// pi returns a complete circumference. - /// - /// Does so with two [ChainShape] separated by a [width]. Which can be - /// rotated by a given [rotation] in radians. - /// - /// The outer radius is specified by [radius], whilst the inner one is - /// equivalent to the [radius] minus the [width]. - /// - /// If [singleWall] is true, just one [ChainShape] is created. - factory Pathway.arc({ - Color? color, - required Vector2 center, - required double width, - required double radius, - required double angle, - double rotation = 0, - bool singleWall = false, - }) { - final paths = >[]; - - // TODO(ruialonso): Refactor repetitive logic - final outerWall = calculateArc( - center: center, - radius: radius, - angle: angle, - offsetAngle: rotation, - ); - paths.add(outerWall); - - if (!singleWall) { - final innerWall = calculateArc( - center: center, - radius: radius - width, - angle: angle, - offsetAngle: rotation, - ); - paths.add(innerWall); - } - - return Pathway._( - color: color, - paths: paths, - ); - } - - /// Creates a bezier curve [Pathway]. - /// - /// Does so with two [ChainShape] separated by a [width]. Which can be - /// rotated by a given [rotation] in radians. - /// - /// First and last [controlPoints] set the beginning and end of the curve, - /// inner points between them set its final shape. - /// - /// If [singleWall] is true, just one [ChainShape] is created. - factory Pathway.bezierCurve({ - Color? color, - required List controlPoints, - required double width, - double rotation = 0, - bool singleWall = false, - }) { - final paths = >[]; - - // TODO(ruialonso): Refactor repetitive logic - final firstWall = calculateBezierCurve(controlPoints: controlPoints) - .map((vector) => vector..rotate(rotation)) - .toList(); - paths.add(firstWall); - - if (!singleWall) { - final secondWall = calculateBezierCurve( - controlPoints: controlPoints - .map((vector) => vector + Vector2(width, -width)) - .toList(), - ).map((vector) => vector..rotate(rotation)).toList(); - paths.add(secondWall); - } - - return Pathway._( - color: color, - paths: paths, - ); - } - - /// Creates an ellipse [Pathway]. - /// - /// Does so with two [ChainShape]s separated by a [width]. Can - /// be rotated by a given [rotation] in radians. - /// - /// If [singleWall] is true, just one [ChainShape] is created. - factory Pathway.ellipse({ - Color? color, - required Vector2 center, - required double width, - required double majorRadius, - required double minorRadius, - double rotation = 0, - bool singleWall = false, - }) { - final paths = >[]; - - // TODO(ruialonso): Refactor repetitive logic - final outerWall = calculateEllipse( - center: center, - majorRadius: majorRadius, - minorRadius: minorRadius, - ).map((vector) => vector..rotate(rotation)).toList(); - paths.add(outerWall); - - if (!singleWall) { - final innerWall = calculateEllipse( - center: center, - majorRadius: majorRadius - width, - minorRadius: minorRadius - width, - ).map((vector) => vector..rotate(rotation)).toList(); - paths.add(innerWall); - } - - return Pathway._( - color: color, - paths: paths, - ); - } - - final List> _paths; - - /// Constructs different [ChainShape]s to form the [Pathway] shape. - List createFixtureDefs() { - final fixturesDef = []; - - for (final path in _paths) { - final chain = ChainShape()..createChain(path); - fixturesDef.add(FixtureDef(chain)); - } - - return fixturesDef; - } - - @override - Body createBody() { - final bodyDef = BodyDef()..position = initialPosition; - final body = world.createBody(bodyDef); - createFixtureDefs().forEach(body.createFixture); - - return body; - } -} diff --git a/lib/game/components/ramp_opening.dart b/lib/game/components/ramp_opening.dart index d8ddcc35..ee1ecdea 100644 --- a/lib/game/components/ramp_opening.dart +++ b/lib/game/components/ramp_opening.dart @@ -37,10 +37,10 @@ abstract class RampOpening extends BodyComponent with InitialPosition, Layered { final Layer _pathwayLayer; final Layer _outsideLayer; - /// Mask of category bits for collision inside [Pathway]. + /// Mask of category bits for collision inside pathway. Layer get pathwayLayer => _pathwayLayer; - /// Mask of category bits for collision outside [Pathway]. + /// Mask of category bits for collision outside pathway. Layer get outsideLayer => _outsideLayer; /// The [Shape] of the [RampOpening]. @@ -65,7 +65,7 @@ abstract class RampOpening extends BodyComponent with InitialPosition, Layered { } /// {@template ramp_opening_ball_contact_callback} -/// Detects when a [Ball] enters or exits a [Pathway] ramp through a +/// Detects when a [Ball] enters or exits a pathway ramp through a /// [RampOpening]. /// /// Modifies [Ball]'s [Layer] accordingly depending on whether the [Ball] is diff --git a/lib/game/pinball_game.dart b/lib/game/pinball_game.dart index 057809be..2a8ddd42 100644 --- a/lib/game/pinball_game.dart +++ b/lib/game/pinball_game.dart @@ -81,23 +81,8 @@ class PinballGame extends Forge2DGame } Future _addPaths() async { - final jetpackRamp = JetpackRamp( - position: Vector2( - PinballGame.boardBounds.left + 40.5, - PinballGame.boardBounds.top - 31.5, - ), - ); - final launcherRamp = LauncherRamp( - position: Vector2( - PinballGame.boardBounds.right - 30, - PinballGame.boardBounds.bottom + 40, - ), - ); - - await addAll([ - jetpackRamp, - launcherRamp, - ]); + unawaited(addFromBlueprint(Jetpack())); + unawaited(addFromBlueprint(Launcher())); } void spawnBall() { diff --git a/packages/pinball_components/lib/src/components/components.dart b/packages/pinball_components/lib/src/components/components.dart index c1ef3e14..a55f9566 100644 --- a/packages/pinball_components/lib/src/components/components.dart +++ b/packages/pinball_components/lib/src/components/components.dart @@ -2,3 +2,4 @@ export 'ball.dart'; export 'fire_effect.dart'; export 'initial_position.dart'; export 'layer.dart'; +export 'shapes/shapes.dart'; diff --git a/packages/pinball_components/lib/src/components/layer.dart b/packages/pinball_components/lib/src/components/layer.dart index 3cc0471f..a3f11f46 100644 --- a/packages/pinball_components/lib/src/components/layer.dart +++ b/packages/pinball_components/lib/src/components/layer.dart @@ -8,6 +8,9 @@ import 'package:flutter/material.dart'; /// [BodyComponent]s with compatible [Layer]s can collide with each other, /// ignoring others. This compatibility depends on bit masking operation /// between layers. For more information read: https://en.wikipedia.org/wiki/Mask_(computing). +/// +/// A parent [Layered] have priority against its children's layer. Them won't be +/// changed but will be ignored. /// {@endtemplate} mixin Layered on BodyComponent { Layer _layer = Layer.all; diff --git a/packages/pinball_components/lib/src/components/shapes/arc_shape.dart b/packages/pinball_components/lib/src/components/shapes/arc_shape.dart new file mode 100644 index 00000000..8b31e223 --- /dev/null +++ b/packages/pinball_components/lib/src/components/shapes/arc_shape.dart @@ -0,0 +1,54 @@ +// ignore_for_file: public_member_api_docs + +import 'package:flame_forge2d/flame_forge2d.dart'; +import 'package:geometry/geometry.dart'; + +/// {@template arc_shape} +/// Creates an arc. +/// {@endtemplate} +class ArcShape extends ChainShape { + /// {@macro arc_shape} + ArcShape({ + required this.center, + required this.arcRadius, + required this.angle, + this.rotation = 0, + }) { + createChain( + calculateArc( + center: center, + radius: arcRadius, + angle: angle, + offsetAngle: rotation, + ), + ); + } + + /// The center of the arc. + final Vector2 center; + + /// The radius of the arc. + // TODO(alestiago): Check if modifying the parent radius makes sense. + final double arcRadius; + + /// Specifies the size of the arc, in radians. + /// + /// For example, two pi returns a complete circumference. + final double angle; + + /// Angle in radians to rotate the arc around its [center]. + final double rotation; + + ArcShape copyWith({ + Vector2? center, + double? arcRadius, + double? angle, + double? rotation, + }) => + ArcShape( + center: center ?? this.center, + arcRadius: arcRadius ?? this.arcRadius, + angle: angle ?? this.angle, + rotation: rotation ?? this.rotation, + ); +} diff --git a/packages/pinball_components/lib/src/components/shapes/bezier_curve_shape.dart b/packages/pinball_components/lib/src/components/shapes/bezier_curve_shape.dart new file mode 100644 index 00000000..0b4e8fef --- /dev/null +++ b/packages/pinball_components/lib/src/components/shapes/bezier_curve_shape.dart @@ -0,0 +1,28 @@ +// ignore_for_file: public_member_api_docs + +import 'package:flame/extensions.dart'; +import 'package:flame_forge2d/flame_forge2d.dart'; +import 'package:geometry/geometry.dart'; + +/// {@template bezier_curve_shape} +/// Creates a bezier curve. +/// {@endtemplate} +class BezierCurveShape extends ChainShape { + /// {@macro bezier_curve_shape} + BezierCurveShape({ + required this.controlPoints, + }) { + createChain(calculateBezierCurve(controlPoints: controlPoints)); + } + + /// Specifies the control points of the curve. + /// + /// First and last [controlPoints] set the beginning and end of the curve, + /// inner points between them set its final shape. + final List controlPoints; + + /// Rotates the bezier curve by a given [angle] in radians. + void rotate(double angle) { + vertices.map((vector) => vector..rotate(angle)).toList(); + } +} diff --git a/packages/pinball_components/lib/src/components/shapes/ellipse_shape.dart b/packages/pinball_components/lib/src/components/shapes/ellipse_shape.dart new file mode 100644 index 00000000..bfa7d435 --- /dev/null +++ b/packages/pinball_components/lib/src/components/shapes/ellipse_shape.dart @@ -0,0 +1,53 @@ +// ignore_for_file: public_member_api_docs + +import 'package:flame/extensions.dart'; +import 'package:flame_forge2d/flame_forge2d.dart'; +import 'package:geometry/geometry.dart'; + +/// {@template ellipse_shape} +/// Creates an ellipse. +/// {@endtemplate} +class EllipseShape extends ChainShape { + /// {@macro ellipse_shape} + EllipseShape({ + required this.center, + required this.majorRadius, + required this.minorRadius, + }) { + createChain( + calculateEllipse( + center: center, + majorRadius: majorRadius, + minorRadius: minorRadius, + ), + ); + } + + /// The top left corner of the ellipse. + /// + /// Where the initial painting begins. + // TODO(ruialonso): Change to use appropiate center. + final Vector2 center; + + /// Major radius is specified by [majorRadius]. + final double majorRadius; + + /// Minor radius is specified by [minorRadius]. + final double minorRadius; + + /// Rotates the ellipse by a given [angle] in radians. + void rotate(double angle) { + vertices.map((vector) => vector..rotate(angle)).toList(); + } + + EllipseShape copyWith({ + Vector2? center, + double? majorRadius, + double? minorRadius, + }) => + EllipseShape( + center: center ?? this.center, + majorRadius: majorRadius ?? this.majorRadius, + minorRadius: minorRadius ?? this.minorRadius, + ); +} diff --git a/packages/pinball_components/lib/src/components/shapes/shapes.dart b/packages/pinball_components/lib/src/components/shapes/shapes.dart new file mode 100644 index 00000000..80e1584d --- /dev/null +++ b/packages/pinball_components/lib/src/components/shapes/shapes.dart @@ -0,0 +1,3 @@ +export 'arc_shape.dart'; +export 'bezier_curve_shape.dart'; +export 'ellipse_shape.dart'; diff --git a/packages/pinball_components/pubspec.yaml b/packages/pinball_components/pubspec.yaml index 56645a30..1c8dfbe3 100644 --- a/packages/pinball_components/pubspec.yaml +++ b/packages/pinball_components/pubspec.yaml @@ -11,6 +11,9 @@ dependencies: flame_forge2d: ^0.9.0-releasecandidate.6 flutter: sdk: flutter + geometry: + path: ../geometry + dev_dependencies: flame_test: ^1.1.0 diff --git a/packages/pinball_components/test/src/components/layer_test.dart b/packages/pinball_components/test/src/components/layer_test.dart index 1eaa9135..537e01b6 100644 --- a/packages/pinball_components/test/src/components/layer_test.dart +++ b/packages/pinball_components/test/src/components/layer_test.dart @@ -6,7 +6,15 @@ import 'package:flame_test/flame_test.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:pinball_components/pinball_components.dart'; -class TestBodyComponent extends BodyComponent with Layered { +class TestLayeredBodyComponent extends BodyComponent with Layered { + @override + Body createBody() { + final fixtureDef = FixtureDef(CircleShape()); + return world.createBody(BodyDef())..createFixture(fixtureDef); + } +} + +class TestBodyComponent extends BodyComponent { @override Body createBody() { final fixtureDef = FixtureDef(CircleShape()); @@ -38,7 +46,7 @@ void main() { }); test('correctly sets and gets', () { - final component = TestBodyComponent()..layer = Layer.jetpack; + final component = TestLayeredBodyComponent()..layer = Layer.jetpack; expect(component.layer, Layer.jetpack); }); @@ -46,7 +54,7 @@ void main() { 'layers correctly before being loaded', (game) async { const expectedLayer = Layer.jetpack; - final component = TestBodyComponent()..layer = expectedLayer; + final component = TestLayeredBodyComponent()..layer = expectedLayer; await game.ensureAdd(component); // TODO(alestiago): modify once component.loaded is available. await component.mounted; @@ -63,7 +71,7 @@ void main() { 'when multiple different sets', (game) async { const expectedLayer = Layer.launcher; - final component = TestBodyComponent()..layer = Layer.jetpack; + final component = TestLayeredBodyComponent()..layer = Layer.jetpack; expect(component.layer, isNot(equals(expectedLayer))); component.layer = expectedLayer; @@ -83,7 +91,7 @@ void main() { 'layers correctly after being loaded', (game) async { const expectedLayer = Layer.jetpack; - final component = TestBodyComponent(); + final component = TestLayeredBodyComponent(); await game.ensureAdd(component); component.layer = expectedLayer; _expectLayerOnFixtures( @@ -98,7 +106,7 @@ void main() { 'when multiple different sets', (game) async { const expectedLayer = Layer.launcher; - final component = TestBodyComponent(); + final component = TestLayeredBodyComponent(); await game.ensureAdd(component); component.layer = Layer.jetpack; @@ -116,11 +124,55 @@ void main() { 'defaults to Layer.all ' 'when no layer is given', (game) async { - final component = TestBodyComponent(); + final component = TestLayeredBodyComponent(); await game.ensureAdd(component); expect(component.layer, equals(Layer.all)); }, ); + + flameTester.test( + 'nested Layered children will keep their layer', + (game) async { + const parentLayer = Layer.jetpack; + const childLayer = Layer.board; + + final component = TestLayeredBodyComponent()..layer = parentLayer; + final childComponent = TestLayeredBodyComponent()..layer = childLayer; + await component.add(childComponent); + + await game.ensureAdd(component); + expect(childLayer, isNot(equals(parentLayer))); + + for (final child in component.children) { + expect((child as TestLayeredBodyComponent).layer, equals(childLayer)); + } + }, + ); + + flameTester.test( + 'nested children will keep their layer', + (game) async { + const parentLayer = Layer.jetpack; + + final component = TestLayeredBodyComponent()..layer = parentLayer; + final childComponent = TestBodyComponent(); + await component.add(childComponent); + + await game.ensureAdd(component); + + for (final child in component.children) { + expect( + (child as TestBodyComponent) + .body + .fixtures + .first + .filterData + .maskBits, + equals(Filter().maskBits), + ); + } + }, + ); }); group('LayerMaskBits', () { diff --git a/packages/pinball_components/test/src/components/shapes/arc_shape_test.dart b/packages/pinball_components/test/src/components/shapes/arc_shape_test.dart new file mode 100644 index 00000000..fe778872 --- /dev/null +++ b/packages/pinball_components/test/src/components/shapes/arc_shape_test.dart @@ -0,0 +1,66 @@ +import 'dart:math' as math; +import 'package:flame/extensions.dart'; +import 'package:flame/game.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:pinball_components/src/components/components.dart'; + +void main() { + group('ArcShape', () { + test('can be instantiated', () { + expect( + ArcShape( + center: Vector2.zero(), + arcRadius: 10, + angle: 2 * math.pi, + ), + isNotNull, + ); + }); + + group('copyWith', () { + test( + 'copies correctly ' + 'when no argument specified', () { + final arcShape = ArcShape( + center: Vector2.zero(), + arcRadius: 10, + angle: 2 * math.pi, + ); + final arcShapeCopied = arcShape.copyWith(); + + for (var index = 0; index < arcShape.vertices.length; index++) { + expect( + arcShape.vertices[index], + equals(arcShapeCopied.vertices[index]), + ); + } + }); + + test( + 'copies correctly ' + 'when all arguments specified', () { + final arcShapeExpected = ArcShape( + center: Vector2.all(10), + arcRadius: 15, + angle: 2 * math.pi, + ); + final arcShapeCopied = ArcShape( + center: Vector2.zero(), + arcRadius: 10, + angle: math.pi, + ).copyWith( + center: Vector2.all(10), + arcRadius: 15, + angle: 2 * math.pi, + ); + + for (var index = 0; index < arcShapeCopied.vertices.length; index++) { + expect( + arcShapeCopied.vertices[index], + equals(arcShapeExpected.vertices[index]), + ); + } + }); + }); + }); +} diff --git a/packages/pinball_components/test/src/components/shapes/bezier_curve_shape_test.dart b/packages/pinball_components/test/src/components/shapes/bezier_curve_shape_test.dart new file mode 100644 index 00000000..6a6adeb7 --- /dev/null +++ b/packages/pinball_components/test/src/components/shapes/bezier_curve_shape_test.dart @@ -0,0 +1,51 @@ +import 'dart:math' as math; +import 'package:flame/extensions.dart'; +import 'package:flame/game.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:pinball_components/src/components/components.dart'; + +void main() { + group('BezierCurveShape', () { + final controlPoints = [ + Vector2(0, 0), + Vector2(10, 0), + Vector2(0, 10), + Vector2(10, 10), + ]; + + test('can be instantiated', () { + expect( + BezierCurveShape( + controlPoints: controlPoints, + ), + isNotNull, + ); + }); + + group('rotate', () { + test('returns vertices rotated', () { + const rotationAngle = 2 * math.pi; + final controlPoints = [ + Vector2(0, 0), + Vector2(10, 0), + Vector2(0, 10), + Vector2(10, 10), + ]; + + final bezierCurveShape = BezierCurveShape( + controlPoints: controlPoints, + ); + final bezierCurveShapeRotated = BezierCurveShape( + controlPoints: controlPoints, + )..rotate(rotationAngle); + + for (var index = 0; index < bezierCurveShape.vertices.length; index++) { + expect( + bezierCurveShape.vertices[index]..rotate(rotationAngle), + equals(bezierCurveShapeRotated.vertices[index]), + ); + } + }); + }); + }); +} diff --git a/packages/pinball_components/test/src/components/shapes/ellipse_shape_test.dart b/packages/pinball_components/test/src/components/shapes/ellipse_shape_test.dart new file mode 100644 index 00000000..31f45cc1 --- /dev/null +++ b/packages/pinball_components/test/src/components/shapes/ellipse_shape_test.dart @@ -0,0 +1,83 @@ +import 'dart:math' as math; +import 'package:flame/extensions.dart'; +import 'package:flame/game.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:pinball_components/src/components/components.dart'; + +void main() { + group('EllipseShape', () { + test('can be instantiated', () { + expect( + EllipseShape( + center: Vector2.zero(), + majorRadius: 10, + minorRadius: 8, + ), + isNotNull, + ); + }); + + group('rotate', () { + test('returns vertices rotated', () { + const rotationAngle = 2 * math.pi; + final ellipseShape = EllipseShape( + center: Vector2.zero(), + majorRadius: 10, + minorRadius: 8, + ); + final ellipseShapeRotated = EllipseShape( + center: Vector2.zero(), + majorRadius: 10, + minorRadius: 8, + )..rotate(rotationAngle); + + for (var index = 0; index < ellipseShape.vertices.length; index++) { + expect( + ellipseShape.vertices[index]..rotate(rotationAngle), + equals(ellipseShapeRotated.vertices[index]), + ); + } + }); + }); + + group('copyWith', () { + test('returns same shape when no properties are passed', () { + final ellipseShape = EllipseShape( + center: Vector2.zero(), + majorRadius: 10, + minorRadius: 8, + ); + final ellipseShapeCopied = ellipseShape.copyWith(); + + for (var index = 0; index < ellipseShape.vertices.length; index++) { + expect( + ellipseShape.vertices[index], + equals(ellipseShapeCopied.vertices[index]), + ); + } + }); + + test('returns object with updated properties when are passed', () { + final ellipseShapeExpected = EllipseShape( + center: Vector2.all(10), + majorRadius: 10, + minorRadius: 8, + ); + final ellipseShapeCopied = EllipseShape( + center: Vector2.zero(), + majorRadius: 10, + minorRadius: 8, + ).copyWith(center: Vector2.all(10)); + + for (var index = 0; + index < ellipseShapeCopied.vertices.length; + index++) { + expect( + ellipseShapeCopied.vertices[index], + equals(ellipseShapeExpected.vertices[index]), + ); + } + }); + }); + }); +} diff --git a/test/game/bloc/game_state_test.dart b/test/game/bloc/game_state_test.dart index aa8bdf66..9ca913ab 100644 --- a/test/game/bloc/game_state_test.dart +++ b/test/game/bloc/game_state_test.dart @@ -186,7 +186,7 @@ void main() { test( 'copies correctly ' - 'when no arguement specified', + 'when no argument specified', () { const gameState = GameState( balls: 0, @@ -204,7 +204,7 @@ void main() { test( 'copies correctly ' - 'when all arguements specified', + 'when all arguments specified', () { const gameState = GameState( score: 2, diff --git a/test/game/components/jetpack_ramp_test.dart b/test/game/components/jetpack_ramp_test.dart deleted file mode 100644 index b88bdf23..00000000 --- a/test/game/components/jetpack_ramp_test.dart +++ /dev/null @@ -1,63 +0,0 @@ -// ignore_for_file: cascade_invocations - -import 'package:flame/components.dart'; -import 'package:flame_forge2d/flame_forge2d.dart'; -import 'package:flame_test/flame_test.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:pinball/game/game.dart'; - -import '../../helpers/helpers.dart'; - -void main() { - TestWidgetsFlutterBinding.ensureInitialized(); - final flameTester = FlameTester(PinballGameTest.create); - - group('JetpackRamp', () { - flameTester.test( - 'loads correctly', - (game) async { - final ramp = JetpackRamp( - position: Vector2.zero(), - ); - await game.ready(); - await game.ensureAdd(ramp); - - expect(game.contains(ramp), isTrue); - }, - ); - - group('children', () { - flameTester.test( - 'has only one Pathway.arc', - (game) async { - final ramp = JetpackRamp( - position: Vector2.zero(), - ); - await game.ready(); - await game.ensureAdd(ramp); - - expect( - () => ramp.children.singleWhere( - (component) => component is Pathway, - ), - returnsNormally, - ); - }, - ); - - flameTester.test( - 'has a two RampOpenings for the ramp', - (game) async { - final ramp = JetpackRamp( - position: Vector2.zero(), - ); - await game.ready(); - await game.ensureAdd(ramp); - - final rampAreas = ramp.children.whereType(); - expect(rampAreas.length, 2); - }, - ); - }); - }); -} diff --git a/test/game/components/launcher_ramp_test.dart b/test/game/components/launcher_ramp_test.dart deleted file mode 100644 index e9350f05..00000000 --- a/test/game/components/launcher_ramp_test.dart +++ /dev/null @@ -1,74 +0,0 @@ -// ignore_for_file: cascade_invocations - -import 'package:flame/components.dart'; -import 'package:flame_forge2d/flame_forge2d.dart'; -import 'package:flame_test/flame_test.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:pinball/game/game.dart'; - -import '../../helpers/helpers.dart'; - -void main() { - TestWidgetsFlutterBinding.ensureInitialized(); - final flameTester = FlameTester(PinballGameTest.create); - - group('LauncherRamp', () { - flameTester.test( - 'loads correctly', - (game) async { - final ramp = LauncherRamp( - position: Vector2.zero(), - ); - await game.ready(); - await game.ensureAdd(ramp); - - expect(game.contains(ramp), isTrue); - }, - ); - - group('constructor', () { - flameTester.test( - 'positions correctly', - (game) async { - final position = Vector2.all(10); - final ramp = LauncherRamp( - position: position, - ); - await game.ensureAdd(ramp); - - expect(ramp.position, equals(position)); - }, - ); - }); - - group('children', () { - flameTester.test( - 'has two Pathway', - (game) async { - final ramp = LauncherRamp( - position: Vector2.zero(), - ); - await game.ready(); - await game.ensureAdd(ramp); - - final pathways = ramp.children.whereType().toList(); - expect(pathways.length, 2); - }, - ); - - flameTester.test( - 'has a two RampOpenings for the ramp', - (game) async { - final ramp = LauncherRamp( - position: Vector2.zero(), - ); - await game.ready(); - await game.ensureAdd(ramp); - - final rampAreas = ramp.children.whereType().toList(); - expect(rampAreas.length, 2); - }, - ); - }); - }); -} diff --git a/test/game/components/pathway_test.dart b/test/game/components/pathway_test.dart deleted file mode 100644 index 63e74d4d..00000000 --- a/test/game/components/pathway_test.dart +++ /dev/null @@ -1,243 +0,0 @@ -// ignore_for_file: cascade_invocations, prefer_const_constructors -import 'dart:math' as math; -import 'package:flame_forge2d/flame_forge2d.dart'; -import 'package:flame_test/flame_test.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:pinball/game/game.dart'; - -void main() { - TestWidgetsFlutterBinding.ensureInitialized(); - final flameTester = FlameTester(Forge2DGame.new); - - group('Pathway', () { - const width = 50.0; - - group('straight', () { - flameTester.test( - 'loads correctly', - (game) async { - final pathway = Pathway.straight( - start: Vector2(10, 10), - end: Vector2(20, 20), - width: width, - ); - await game.ready(); - await game.ensureAdd(pathway); - - expect(game.contains(pathway), isTrue); - }, - ); - - group('color', () { - flameTester.test( - 'has transparent color by default when no color is specified', - (game) async { - final pathway = Pathway.straight( - start: Vector2(10, 10), - end: Vector2(20, 20), - width: width, - ); - await game.ready(); - await game.ensureAdd(pathway); - - expect(game.contains(pathway), isTrue); - expect(pathway.paint, isNotNull); - expect( - pathway.paint.color, - equals(Color.fromARGB(0, 0, 0, 0)), - ); - }, - ); - - flameTester.test( - 'has a color when is specified', - (game) async { - const defaultColor = Colors.blue; - - final pathway = Pathway.straight( - color: defaultColor, - start: Vector2(10, 10), - end: Vector2(20, 20), - width: width, - ); - await game.ready(); - await game.ensureAdd(pathway); - - expect(game.contains(pathway), isTrue); - expect(pathway.paint, isNotNull); - expect(pathway.paint.color.value, equals(defaultColor.value)); - }, - ); - }); - - group('body', () { - flameTester.test( - 'is static', - (game) async { - final pathway = Pathway.straight( - start: Vector2(10, 10), - end: Vector2(20, 20), - width: width, - ); - await game.ready(); - await game.ensureAdd(pathway); - - expect(pathway.body.bodyType, equals(BodyType.static)); - }, - ); - }); - - group('fixtures', () { - flameTester.test( - 'has only one ChainShape when singleWall is true', - (game) async { - final pathway = Pathway.straight( - start: Vector2(10, 10), - end: Vector2(20, 20), - width: width, - singleWall: true, - ); - await game.ready(); - await game.ensureAdd(pathway); - - expect(pathway.body.fixtures.length, 1); - final fixture = pathway.body.fixtures[0]; - expect(fixture, isA()); - expect(fixture.shape.shapeType, equals(ShapeType.chain)); - }, - ); - - flameTester.test( - 'has two ChainShape when singleWall is false (default)', - (game) async { - final pathway = Pathway.straight( - start: Vector2(10, 10), - end: Vector2(20, 20), - width: width, - ); - await game.ready(); - await game.ensureAdd(pathway); - - expect(pathway.body.fixtures.length, 2); - for (final fixture in pathway.body.fixtures) { - expect(fixture, isA()); - expect(fixture.shape.shapeType, equals(ShapeType.chain)); - } - }, - ); - }); - }); - - group('arc', () { - flameTester.test( - 'loads correctly', - (game) async { - final pathway = Pathway.arc( - center: Vector2.zero(), - width: width, - radius: 100, - angle: math.pi / 2, - ); - await game.ready(); - await game.ensureAdd(pathway); - - expect(game.contains(pathway), isTrue); - }, - ); - - group('body', () { - flameTester.test( - 'is static', - (game) async { - final pathway = Pathway.arc( - center: Vector2.zero(), - width: width, - radius: 100, - angle: math.pi / 2, - ); - await game.ready(); - await game.ensureAdd(pathway); - - expect(pathway.body.bodyType, equals(BodyType.static)); - }, - ); - }); - }); - - group('ellipse', () { - flameTester.test( - 'loads correctly', - (game) async { - final pathway = Pathway.ellipse( - center: Vector2.zero(), - width: width, - majorRadius: 150, - minorRadius: 70, - ); - await game.ready(); - await game.ensureAdd(pathway); - - expect(game.contains(pathway), isTrue); - }, - ); - - group('body', () { - flameTester.test( - 'is static', - (game) async { - final pathway = Pathway.ellipse( - center: Vector2.zero(), - width: width, - majorRadius: 150, - minorRadius: 70, - ); - await game.ready(); - await game.ensureAdd(pathway); - - expect(pathway.body.bodyType, equals(BodyType.static)); - }, - ); - }); - }); - - group('bezier curve', () { - final controlPoints = [ - Vector2(0, 0), - Vector2(50, 0), - Vector2(0, 50), - Vector2(50, 50), - ]; - - flameTester.test( - 'loads correctly', - (game) async { - final pathway = Pathway.bezierCurve( - controlPoints: controlPoints, - width: width, - ); - await game.ready(); - await game.ensureAdd(pathway); - - expect(game.contains(pathway), isTrue); - }, - ); - - group('body', () { - flameTester.test( - 'is static', - (game) async { - final pathway = Pathway.bezierCurve( - controlPoints: controlPoints, - width: width, - ); - await game.ready(); - await game.ensureAdd(pathway); - - expect(pathway.body.bodyType, equals(BodyType.static)); - }, - ); - }); - }); - }); -} diff --git a/test/game/pinball_game_test.dart b/test/game/pinball_game_test.dart index 65732608..52008074 100644 --- a/test/game/pinball_game_test.dart +++ b/test/game/pinball_game_test.dart @@ -62,32 +62,6 @@ void main() { }); }); - group('Paths', () { - flameTester.test( - 'has only one JetpackRamp', - (game) async { - await game.ready(); - - expect( - () => game.children.singleWhere( - (component) => component is JetpackRamp, - ), - returnsNormally, - ); - }, - ); - - flameTester.test( - 'has only one LauncherRamp', - (game) async { - await game.ready(); - - final rampAreas = game.children.whereType().toList(); - expect(rampAreas.length, 1); - }, - ); - }); - debugModeFlameTester.test('adds a ball on tap up', (game) async { await game.ready();