diff --git a/lib/game/components/jetpack_ramp.dart b/lib/game/components/jetpack_ramp.dart index f769e39c..ed0b4079 100644 --- a/lib/game/components/jetpack_ramp.dart +++ b/lib/game/components/jetpack_ramp.dart @@ -1,13 +1,12 @@ +import 'dart:math' as math; + import 'package:flame/components.dart'; import 'package:flame/extensions.dart'; import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:pinball/game/game.dart'; /// {@template jetpack_ramp} -/// Represents the upper left blue ramp for the game. -/// -/// Composed of a [Pathway.arc] defining the ramp, and two -/// [JetpackRampOpening]s at the entrance and exit of the ramp. +/// Represents the upper left blue ramp of the [Board]. /// {@endtemplate} class JetpackRamp extends Component with HasGameRef { /// {@macro jetpack_ramp} @@ -15,21 +14,13 @@ class JetpackRamp extends Component with HasGameRef { required this.position, }); - final double _radius = 200; - final double _width = 80; - // TODO(ruialonso): Avoid using radians. - final double _angle = radians(210); - final double _rotation = radians(-10); - - /// The position of this [JetpackRamp] + /// The position of this [JetpackRamp]. final Vector2 position; - static const _layer = Layer.jetpack; - @override Future onLoad() async { gameRef.addContactCallback( - RampOpeningBallContactCallback(), + RampOpeningBallContactCallback<_JetpackRampOpening>(), ); final curvePath = Pathway.arc( @@ -37,24 +28,22 @@ class JetpackRamp extends Component with HasGameRef { // TODO(ruialonso): Use a bezier curve once control points are defined. color: const Color.fromARGB(255, 8, 218, 241), center: position, - width: _width, - radius: _radius, - angle: _angle, - rotation: _rotation, + width: 80, + radius: 200, + angle: 7 * math.pi / 6, + rotation: -math.pi / 18, ) ..initialPosition = position - ..layer = _layer; - final leftOpening = JetpackRampOpening( - orientation: RampOrientation.down, - rotation: radians(15), + ..layer = Layer.jetpack; + final leftOpening = _JetpackRampOpening( + rotation: 15 * math.pi / 180, ) - ..initialPosition = position + Vector2(-25.7, 25) + ..initialPosition = position + Vector2(-27, 21) ..layer = Layer.opening; - final rightOpening = JetpackRampOpening( - orientation: RampOrientation.down, - rotation: radians(-9), + final rightOpening = _JetpackRampOpening( + rotation: -math.pi / 20, ) - ..initialPosition = position + Vector2(-10, 26.2) + ..initialPosition = position + Vector2(-11, 22.5) ..layer = Layer.opening; await addAll([ @@ -69,32 +58,21 @@ class JetpackRamp extends Component with HasGameRef { /// [RampOpening] with [Layer.jetpack] to filter [Ball] collisions /// inside [JetpackRamp]. /// {@endtemplate} -class JetpackRampOpening extends RampOpening { +class _JetpackRampOpening extends RampOpening { /// {@macro jetpack_ramp_opening} - JetpackRampOpening({ - required RampOrientation orientation, - double rotation = 0, + _JetpackRampOpening({ + required double rotation, }) : _rotation = rotation, - _orientation = orientation, super( pathwayLayer: Layer.jetpack, + orientation: RampOrientation.down, ); - /// Orientation of entrance/exit of [JetpackRamp] where - /// this [JetpackRampOpening] is placed. - final RampOrientation _orientation; - - /// Rotation of the [RampOpening] to place it right at the - /// entrance/exit of [JetpackRamp]. final double _rotation; - /// Size of the [RampOpening] placed at the entrance/exit of [JetpackRamp]. // TODO(ruialonso): Avoid magic number 3, should be propotional to // [JetpackRamp]. - final Vector2 _size = Vector2(3, .1); - - @override - RampOrientation get orientation => _orientation; + static final Vector2 _size = Vector2(3, .1); @override Shape get shape => PolygonShape() diff --git a/lib/game/components/launcher_ramp.dart b/lib/game/components/launcher_ramp.dart index 5cf2ff95..21d4d666 100644 --- a/lib/game/components/launcher_ramp.dart +++ b/lib/game/components/launcher_ramp.dart @@ -1,15 +1,13 @@ +import 'dart:math' as math; + import 'package:flame/components.dart'; import 'package:flame/extensions.dart'; import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:pinball/game/game.dart'; /// {@template launcher_ramp} -/// Represent the launcher green and upper right yellow ramps for the game. -/// -/// Group of [Component]s composed by a [Pathway.arc] as the upper curve ramp, -/// a [Pathway.straight] for the launcher straight ramp, and two -/// [LauncherRampOpening] at the entrance and exit of the ramp, to detect when -/// a ball gets into/out of the 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} @@ -17,48 +15,39 @@ class LauncherRamp extends Component with HasGameRef { required this.position, }); - final double _radius = 300; - final double _width = 80; - final double _angle = radians(200); - - /// The position of this [LauncherRamp] + /// The position of this [LauncherRamp]. final Vector2 position; - static const _layer = Layer.launcher; - @override Future onLoad() async { + const layer = Layer.launcher; + gameRef.addContactCallback( - RampOpeningBallContactCallback(), + RampOpeningBallContactCallback<_LauncherRampOpening>(), ); final straightPath = Pathway.straight( color: const Color.fromARGB(255, 34, 255, 0), start: Vector2(0, 0), - end: Vector2(0, 600), + end: Vector2(0, 700), width: 80, ) ..initialPosition = position - ..layer = _layer; + ..layer = layer; final curvedPath = Pathway.arc( color: const Color.fromARGB(255, 251, 255, 0), - center: position + Vector2(-24, -6), - radius: _radius, - angle: _angle, - width: _width, + center: position + Vector2(-29, -8), + radius: 300, + angle: 10 * math.pi / 9, + width: 80, ) ..initialPosition = position + Vector2(-28.8, -6) - ..layer = _layer; - final leftOpening = LauncherRampOpening( - orientation: RampOrientation.down, - rotation: radians(13), - ) - ..initialPosition = position + Vector2(-46.5, -8.5) + ..layer = layer; + final leftOpening = _LauncherRampOpening(rotation: 13 * math.pi / 180) + ..initialPosition = position + Vector2(-72.5, 12) ..layer = Layer.opening; - final rightOpening = LauncherRampOpening( - orientation: RampOrientation.down, - ) - ..initialPosition = position + Vector2(4, 0) + final rightOpening = _LauncherRampOpening(rotation: 0) + ..initialPosition = position + Vector2(-46.8, 17) ..layer = Layer.opening; await addAll([ @@ -74,41 +63,28 @@ class LauncherRamp extends Component with HasGameRef { /// [RampOpening] with [Layer.launcher] to filter [Ball]s collisions /// inside [LauncherRamp]. /// {@endtemplate} -class LauncherRampOpening extends RampOpening { +class _LauncherRampOpening extends RampOpening { /// {@macro launcher_ramp_opening} - LauncherRampOpening({ - double rotation = 0, - required RampOrientation orientation, + _LauncherRampOpening({ + required double rotation, }) : _rotation = rotation, - _orientation = orientation, super( pathwayLayer: Layer.launcher, + orientation: RampOrientation.down, ); - /// Orientation of entrance/exit of [LauncherRamp] where - /// this [LauncherRampOpening] is placed. - final RampOrientation _orientation; - - /// Rotation of the [RampOpening] to place it right at the - /// entrance/exit of [LauncherRamp]. final double _rotation; - /// Size of the [RampOpening] placed at the entrance/exit of [JetpackRamp]. // TODO(ruialonso): Avoid magic number 3, should be propotional to // [JetpackRamp]. - final Vector2 _size = Vector2(3, .1); - - @override - RampOrientation get orientation => _orientation; + static final Vector2 _size = Vector2(3, .1); @override - Shape get shape { - final area = PolygonShape()..setAsBoxXY(_size.x, _size.y); - // TODO(alestiago): Use shape.rotate() once it's implemented. - for (final vertex in area.vertices) { - vertex.rotate(_rotation); - } - - return area; - } + Shape get shape => PolygonShape() + ..setAsBox( + _size.x, + _size.y, + initialPosition, + _rotation, + ); } diff --git a/lib/game/components/ramp_opening.dart b/lib/game/components/ramp_opening.dart index 2032fb7c..9f25287e 100644 --- a/lib/game/components/ramp_opening.dart +++ b/lib/game/components/ramp_opening.dart @@ -4,14 +4,14 @@ import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flutter/material.dart'; import 'package:pinball/game/game.dart'; -/// Indicates the orientation of a ramp entrance/exit. -/// -/// Used to know if a ramp is facing up or down on the board. +/// {@template ramp_orientation} +/// Determines if a ramp is facing [up] or [down] on the [Board]. +/// {@endtemplate} enum RampOrientation { - /// Facing up on the board. + /// Facing up on the [Board]. up, - /// Facing down on the board. + /// Facing down on the [Board]. down, } @@ -19,13 +19,16 @@ enum RampOrientation { /// [BodyComponent] located at the entrance and exit of a ramp. /// /// [RampOpeningBallContactCallback] detects when a [Ball] passes -/// through this opening. By default openings are [Layer.board] that -/// means opening are at ground level, not over board. +/// through this opening. +/// +/// By default the [layer] is set to [Layer.board]. /// {@endtemplate} +// TODO(ruialonso): Consider renaming the class. abstract class RampOpening extends BodyComponent with InitialPosition, Layered { /// {@macro ramp_opening} RampOpening({ required Layer pathwayLayer, + required this.orientation, }) : _pathwayLayer = pathwayLayer { layer = Layer.board; } @@ -37,10 +40,10 @@ abstract class RampOpening extends BodyComponent with InitialPosition, Layered { /// The [Shape] of the [RampOpening]. Shape get shape; - /// Orientation of the [RampOpening] entrance/exit + /// {@macro ramp_orientation} // TODO(ruimiguel): Try to remove the need of [RampOrientation] for collision // calculations. - RampOrientation get orientation; + final RampOrientation orientation; @override Body createBody() { @@ -85,8 +88,8 @@ class RampOpeningBallContactCallback @override void end(Ball ball, Opening opening, Contact _) { final isBallOutsideOpening = opening.orientation == RampOrientation.up - ? ball.body.position.y > opening.body.position.y - : ball.body.position.y < opening.body.position.y; + ? ball.body.position.y > opening.initialPosition.y + : ball.body.position.y < opening.initialPosition.y; if (isBallOutsideOpening) ball.layer = Layer.board; } diff --git a/lib/game/pinball_game.dart b/lib/game/pinball_game.dart index b7700d04..18835c98 100644 --- a/lib/game/pinball_game.dart +++ b/lib/game/pinball_game.dart @@ -102,15 +102,15 @@ class PinballGame extends Forge2DGame position: screenToWorld( Vector2( camera.viewport.effectiveSize.x / 2 - 150, - camera.viewport.effectiveSize.y / 2 - 150, + camera.viewport.effectiveSize.y / 2 - 250, ), ), ); final launcherRamp = LauncherRamp( position: screenToWorld( Vector2( - camera.viewport.effectiveSize.x / 2 + 373, - camera.viewport.effectiveSize.y / 2 - 155, + camera.viewport.effectiveSize.x / 2 + 400, + camera.viewport.effectiveSize.y / 2 - 330, ), ), ); @@ -124,7 +124,7 @@ class PinballGame extends Forge2DGame ); plunger.initialPosition = screenToWorld( Vector2( - camera.viewport.effectiveSize.x / 1.035, + camera.viewport.effectiveSize.x / 2 + 450, camera.viewport.effectiveSize.y - plunger.compressionDistance, ), ); diff --git a/test/game/components/jetpack_ramp_test.dart b/test/game/components/jetpack_ramp_test.dart index 731d9b26..925e9fbd 100644 --- a/test/game/components/jetpack_ramp_test.dart +++ b/test/game/components/jetpack_ramp_test.dart @@ -9,8 +9,6 @@ import 'package:pinball/game/game.dart'; import '../../helpers/helpers.dart'; -class MockJetpackRampArea extends Mock implements JetpackRampOpening {} - void main() { TestWidgetsFlutterBinding.ensureInitialized(); final flameTester = FlameTester(PinballGameTest.create); @@ -49,7 +47,7 @@ void main() { ); flameTester.test( - 'has a two sensors for the ramp', + 'has a two RampOpenings for the ramp', (game) async { final ramp = JetpackRamp( position: Vector2.zero(), @@ -57,27 +55,10 @@ void main() { await game.ready(); await game.ensureAdd(ramp); - final rampAreas = - ramp.children.whereType().toList(); + final rampAreas = ramp.children.whereType(); expect(rampAreas.length, 2); }, ); }); }); - - group('JetpackRampOpening', () { - flameTester.test( - 'orientation is down', - (game) async { - final position = Vector2.all(10); - final ramp = JetpackRampOpening( - orientation: RampOrientation.down, - )..initialPosition = position; - await game.ready(); - await game.ensureAdd(ramp); - - expect(ramp.orientation, RampOrientation.down); - }, - ); - }); } diff --git a/test/game/components/launcher_ramp_test.dart b/test/game/components/launcher_ramp_test.dart index 7d38f6c7..a9928839 100644 --- a/test/game/components/launcher_ramp_test.dart +++ b/test/game/components/launcher_ramp_test.dart @@ -9,8 +9,6 @@ import 'package:pinball/game/game.dart'; import '../../helpers/helpers.dart'; -class MockLauncherRampArea extends Mock implements LauncherRampOpening {} - void main() { TestWidgetsFlutterBinding.ensureInitialized(); final flameTester = FlameTester(PinballGameTest.create); @@ -60,7 +58,7 @@ void main() { ); flameTester.test( - 'has a two sensors for the ramp', + 'has a two RampOpenings for the ramp', (game) async { final ramp = LauncherRamp( position: Vector2.zero(), @@ -68,27 +66,10 @@ void main() { await game.ready(); await game.ensureAdd(ramp); - final rampAreas = - ramp.children.whereType().toList(); + final rampAreas = ramp.children.whereType().toList(); expect(rampAreas.length, 2); }, ); }); }); - - group('LauncherRampOpening', () { - flameTester.test( - 'orientation is down', - (game) async { - final position = Vector2.all(10); - final ramp = LauncherRampOpening( - orientation: RampOrientation.down, - )..initialPosition = position; - await game.ready(); - await game.ensureAdd(ramp); - - expect(ramp.orientation, RampOrientation.down); - }, - ); - }); }