diff --git a/lib/game/components/ball.dart b/lib/game/components/ball.dart index 738ceec6..36ed8929 100644 --- a/lib/game/components/ball.dart +++ b/lib/game/components/ball.dart @@ -6,14 +6,9 @@ import 'package:pinball/game/game.dart'; /// A solid, [BodyType.dynamic] sphere that rolls and bounces along the /// [PinballGame]. /// {@endtemplate} -class Ball extends BodyComponent { +class Ball extends BodyComponent with InitialPosition { /// {@macro ball} - Ball({ - required Vector2 position, - }) : _position = position; - - /// The initial position of the [Ball] body. - final Vector2 _position; + Ball(); /// The size of the [Ball] final Vector2 size = Vector2.all(2); @@ -44,8 +39,8 @@ class Ball extends BodyComponent { final fixtureDef = FixtureDef(shape)..density = 1; final bodyDef = BodyDef() + ..position = initialPosition ..userData = this - ..position = Vector2(_position.x, _position.y + size.y) ..type = BodyType.dynamic; return world.createBody(bodyDef)..createFixture(fixtureDef); diff --git a/lib/game/components/baseboard.dart b/lib/game/components/baseboard.dart index 9153d4f3..62c9210c 100644 --- a/lib/game/components/baseboard.dart +++ b/lib/game/components/baseboard.dart @@ -6,29 +6,11 @@ import 'package:pinball/game/game.dart'; /// {@template baseboard} /// Straight, angled board piece to corral the [Ball] towards the [Flipper]s. /// {@endtemplate} -class Baseboard extends BodyComponent { +class Baseboard extends BodyComponent with InitialPosition { /// {@macro baseboard} - Baseboard._({ - required Vector2 position, + Baseboard({ required BoardSide side, - }) : _position = position, - _side = side; - - /// A left positioned [Baseboard]. - Baseboard.left({ - required Vector2 position, - }) : this._( - position: position, - side: BoardSide.left, - ); - - /// A right positioned [Baseboard]. - Baseboard.right({ - required Vector2 position, - }) : this._( - position: position, - side: BoardSide.right, - ); + }) : _side = side; /// The width of the [Baseboard]. static const width = 10.0; @@ -36,9 +18,6 @@ class Baseboard extends BodyComponent { /// The height of the [Baseboard]. static const height = 2.0; - /// The position of the [Baseboard] body. - final Vector2 _position; - /// Whether the [Baseboard] is on the left or right side of the board. final BoardSide _side; @@ -79,8 +58,7 @@ class Baseboard extends BodyComponent { const angle = math.pi / 7; final bodyDef = BodyDef() - ..position = _position - ..type = BodyType.static + ..position = initialPosition ..angle = _side.isLeft ? -angle : angle; final body = world.createBody(bodyDef); diff --git a/lib/game/components/board.dart b/lib/game/components/board.dart new file mode 100644 index 00000000..b9ea85ea --- /dev/null +++ b/lib/game/components/board.dart @@ -0,0 +1,79 @@ +import 'package:flame/components.dart'; +import 'package:pinball/game/game.dart'; + +/// {@template bottom_group} +/// Grouping of the board's bottom [Component]s. +/// +/// The bottom [Component]s are the [Flipper]s and the [Baseboard]s. +/// {@endtemplate} +// TODO(alestiago): Consider renaming once entire Board is defined. +class BottomGroup extends Component { + /// {@macro bottom_group} + BottomGroup({ + required this.position, + required this.spacing, + }); + + /// The amount of space between the line of symmetry. + final double spacing; + + /// The position of this [BottomGroup]. + final Vector2 position; + + @override + Future onLoad() async { + final spacing = this.spacing + Flipper.width / 2; + final rightSide = _BottomGroupSide( + side: BoardSide.right, + position: position + Vector2(spacing, 0), + ); + final leftSide = _BottomGroupSide( + side: BoardSide.left, + position: position + Vector2(-spacing, 0), + ); + + await addAll([rightSide, leftSide]); + } +} + +/// {@template bottom_group_side} +/// Group with one side of [BottomGroup]'s symmetric [Component]s. +/// +/// For example, [Flipper]s are symmetric components. +/// {@endtemplate} +class _BottomGroupSide extends Component { + /// {@macro bottom_group_side} + _BottomGroupSide({ + required BoardSide side, + required Vector2 position, + }) : _side = side, + _position = position; + + final BoardSide _side; + + final Vector2 _position; + + @override + Future onLoad() async { + final direction = _side.direction; + + final flipper = Flipper.fromSide( + side: _side, + )..initialPosition = _position; + final baseboard = Baseboard(side: _side) + ..initialPosition = _position + + Vector2( + (Flipper.width * direction) - direction, + Flipper.height, + ); + final slingShot = SlingShot( + side: _side, + )..initialPosition = _position + + Vector2( + (Flipper.width) * direction, + Flipper.height + SlingShot.size.y, + ); + + await addAll([flipper, baseboard, slingShot]); + } +} diff --git a/lib/game/components/bonus_word.dart b/lib/game/components/bonus_word.dart index 49a1da1d..03f64a11 100644 --- a/lib/game/components/bonus_word.dart +++ b/lib/game/components/bonus_word.dart @@ -74,10 +74,9 @@ class BonusWord extends Component with BlocComponent { unawaited( add( BonusLetter( - position: _position - Vector2(16 - (i * 6), -30), letter: letters[i], index: i, - ), + )..initialPosition = _position - Vector2(16 - (i * 6), -30), ), ); } @@ -89,14 +88,12 @@ class BonusWord extends Component with BlocComponent { /// which will activate its letter after contact with a [Ball]. /// {@endtemplate} class BonusLetter extends BodyComponent - with BlocComponent { + with BlocComponent, InitialPosition { /// {@macro bonus_letter} BonusLetter({ - required Vector2 position, required String letter, required int index, - }) : _position = position, - _letter = letter, + }) : _letter = letter, _index = index { paint = Paint()..color = _disableColor; } @@ -107,7 +104,6 @@ class BonusLetter extends BodyComponent static const _activeColor = Colors.green; static const _disableColor = Colors.red; - final Vector2 _position; final String _letter; final int _index; @@ -133,8 +129,8 @@ class BonusLetter extends BodyComponent final fixtureDef = FixtureDef(shape)..isSensor = true; final bodyDef = BodyDef() + ..position = initialPosition ..userData = this - ..position = _position ..type = BodyType.static; return world.createBody(bodyDef)..createFixture(fixtureDef); diff --git a/lib/game/components/components.dart b/lib/game/components/components.dart index d5c479c7..86fa3845 100644 --- a/lib/game/components/components.dart +++ b/lib/game/components/components.dart @@ -1,8 +1,10 @@ export 'ball.dart'; export 'baseboard.dart'; +export 'board.dart'; export 'board_side.dart'; export 'bonus_word.dart'; export 'flipper.dart'; +export 'initial_position.dart'; export 'joint_anchor.dart'; export 'pathway.dart'; export 'plunger.dart'; diff --git a/lib/game/components/flipper.dart b/lib/game/components/flipper.dart index 9035daaf..8b029b1e 100644 --- a/lib/game/components/flipper.dart +++ b/lib/game/components/flipper.dart @@ -8,61 +8,20 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:pinball/game/game.dart'; -/// {@template flipper_group} -/// Loads a [Flipper.right] and a [Flipper.left]. -/// {@endtemplate} -class FlipperGroup extends Component { - /// {@macro flipper_group} - FlipperGroup({ - required this.position, - required this.spacing, - }); - - /// The amount of space between the [Flipper.right] and [Flipper.left]. - final double spacing; - - /// The position of this [FlipperGroup] - final Vector2 position; - - @override - Future onLoad() async { - final leftFlipper = Flipper.left( - position: Vector2( - position.x - (Flipper.width / 2) - (spacing / 2), - position.y, - ), - ); - await add(leftFlipper); - - final rightFlipper = Flipper.right( - position: Vector2( - position.x + (Flipper.width / 2) + (spacing / 2), - position.y, - ), - ); - await add(rightFlipper); - } -} - /// {@template flipper} /// A bat, typically found in pairs at the bottom of the board. /// /// [Flipper] can be controlled by the player in an arc motion. /// {@endtemplate flipper} -class Flipper extends BodyComponent with KeyboardHandler { +class Flipper extends BodyComponent with KeyboardHandler, InitialPosition { /// {@macro flipper} Flipper._({ - required Vector2 position, required this.side, required List keys, - }) : _position = position, - _keys = keys; - - /// A left positioned [Flipper]. - Flipper.left({ - required Vector2 position, - }) : this._( - position: position, + }) : _keys = keys; + + Flipper._left() + : this._( side: BoardSide.left, keys: [ LogicalKeyboardKey.arrowLeft, @@ -70,11 +29,8 @@ class Flipper extends BodyComponent with KeyboardHandler { ], ); - /// A right positioned [Flipper]. - Flipper.right({ - required Vector2 position, - }) : this._( - position: position, + Flipper._right() + : this._( side: BoardSide.right, keys: [ LogicalKeyboardKey.arrowRight, @@ -82,6 +38,21 @@ class Flipper extends BodyComponent with KeyboardHandler { ], ); + /// Constructs a [Flipper] from a [BoardSide]. + /// + /// A [Flipper._right] and [Flipper._left] besides being mirrored + /// horizontally, also have different [LogicalKeyboardKey]s that control them. + factory Flipper.fromSide({ + required BoardSide side, + }) { + switch (side) { + case BoardSide.left: + return Flipper._left(); + case BoardSide.right: + return Flipper._right(); + } + } + /// Asset location of the sprite that renders with the [Flipper]. /// /// Sprite is preloaded by [PinballGameAssetsX]. @@ -104,9 +75,6 @@ class Flipper extends BodyComponent with KeyboardHandler { /// whereas a [Flipper] with [BoardSide.right] has a clockwise arc motion. final BoardSide side; - /// The initial position of the [Flipper] body. - final Vector2 _position; - /// The [LogicalKeyboardKey]s that will control the [Flipper]. /// /// [onKeyEvent] method listens to when one of these keys is pressed. @@ -221,10 +189,9 @@ class Flipper extends BodyComponent with KeyboardHandler { @override Body createBody() { final bodyDef = BodyDef() + ..position = initialPosition ..gravityScale = 0 - ..type = BodyType.dynamic - ..position = _position; - + ..type = BodyType.dynamic; final body = world.createBody(bodyDef); _createFixtureDefs().forEach(body.createFixture); @@ -260,14 +227,14 @@ class FlipperAnchor extends JointAnchor { /// {@macro flipper_anchor} FlipperAnchor({ required Flipper flipper, - }) : super( - position: Vector2( - flipper.side.isLeft - ? flipper.body.position.x - Flipper.width / 2 - : flipper.body.position.x + Flipper.width / 2, - flipper.body.position.y, - ), - ); + }) { + initialPosition = Vector2( + flipper.side.isLeft + ? flipper.body.position.x - Flipper.width / 2 + : flipper.body.position.x + Flipper.width / 2, + flipper.body.position.y, + ); + } } /// {@template flipper_anchor_revolute_joint_def} diff --git a/lib/game/components/initial_position.dart b/lib/game/components/initial_position.dart new file mode 100644 index 00000000..d79f8d64 --- /dev/null +++ b/lib/game/components/initial_position.dart @@ -0,0 +1,34 @@ +import 'package:flame_forge2d/flame_forge2d.dart'; + +/// Forces a given [BodyComponent] to position their [body] to an +/// [initialPosition]. +/// +/// Note: If the [initialPosition] is set after the [BodyComponent] has been +/// loaded it will have no effect; defaulting to [Vector2.zero]. +mixin InitialPosition on BodyComponent { + final Vector2 _initialPosition = Vector2.zero(); + + set initialPosition(Vector2 value) { + assert( + !isLoaded, + 'Cannot set initialPosition after component has already loaded.', + ); + if (value == initialPosition) return; + + _initialPosition.setFrom(value); + } + + /// The initial position of the [body]. + Vector2 get initialPosition => _initialPosition; + + @override + Future onLoad() async { + await super.onLoad(); + // TODO(alestiago): Investiagate why body.position.setFrom(initialPosition) + // works for some components and not others. + assert( + body.position == initialPosition, + 'Body position does not match initialPosition.', + ); + } +} diff --git a/lib/game/components/joint_anchor.dart b/lib/game/components/joint_anchor.dart index 05e62b73..e945bd72 100644 --- a/lib/game/components/joint_anchor.dart +++ b/lib/game/components/joint_anchor.dart @@ -1,11 +1,12 @@ import 'package:flame_forge2d/flame_forge2d.dart'; +import 'package:pinball/game/game.dart'; /// {@template joint_anchor} /// Non visual [BodyComponent] used to hold a [BodyType.dynamic] in [Joint]s /// with this [BodyType.static]. /// -/// It is recommended to [_position] the anchor first and then use the body -/// position as the anchor point when initializing a [JointDef]. +/// It is recommended to use [JointAnchor.body.position] to position the anchor +/// point when initializing a [JointDef]. /// /// ```dart /// initialize( @@ -15,18 +16,13 @@ import 'package:flame_forge2d/flame_forge2d.dart'; /// ); /// ``` /// {@endtemplate} -class JointAnchor extends BodyComponent { +class JointAnchor extends BodyComponent with InitialPosition { /// {@macro joint_anchor} - JointAnchor({ - required Vector2 position, - }) : _position = position; - - final Vector2 _position; + JointAnchor(); @override Body createBody() { - final bodyDef = BodyDef()..position = _position; - + final bodyDef = BodyDef()..position = initialPosition; return world.createBody(bodyDef); } } diff --git a/lib/game/components/pathway.dart b/lib/game/components/pathway.dart index d41ce3da..e4a6f1f5 100644 --- a/lib/game/components/pathway.dart +++ b/lib/game/components/pathway.dart @@ -2,20 +2,19 @@ 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/game/game.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 { +class Pathway extends BodyComponent with InitialPosition { Pathway._({ // TODO(ruialonso): remove color when assets added. Color? color, - required Vector2 position, required List> paths, - }) : _position = position, - _paths = paths { + }) : _paths = paths { paint = Paint() ..color = color ?? const Color.fromARGB(0, 0, 0, 0) ..style = PaintingStyle.stroke; @@ -23,14 +22,12 @@ class Pathway extends BodyComponent { /// Creates a uniform unidirectional (straight) [Pathway]. /// - /// Does so with two [ChainShape] separated by a [width]. Placed - /// at a [position] between [start] and [end] points. Can + /// 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 position, required Vector2 start, required Vector2 end, required double width, @@ -56,28 +53,25 @@ class Pathway extends BodyComponent { return Pathway._( color: color, - position: position, paths: paths, ); } /// Creates an arc [Pathway]. /// - /// The [angle], in radians, specifies the size of the arc. For example, 2*pi - /// returns a complete circumference and minor angles a semi circumference. - /// - /// The center of the arc is placed at [position]. + /// 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 [radius] - [width]. + /// equivalent to the [radius] minus the [width]. /// /// If [singleWall] is true, just one [ChainShape] is created. factory Pathway.arc({ Color? color, - required Vector2 position, + required Vector2 center, required double width, required double radius, required double angle, @@ -88,7 +82,7 @@ class Pathway extends BodyComponent { // TODO(ruialonso): Refactor repetitive logic final outerWall = calculateArc( - center: position, + center: center, radius: radius, angle: angle, offsetAngle: rotation, @@ -97,7 +91,7 @@ class Pathway extends BodyComponent { if (!singleWall) { final innerWall = calculateArc( - center: position, + center: center, radius: radius - width, angle: angle, offsetAngle: rotation, @@ -107,7 +101,6 @@ class Pathway extends BodyComponent { return Pathway._( color: color, - position: position, paths: paths, ); } @@ -123,7 +116,6 @@ class Pathway extends BodyComponent { /// If [singleWall] is true, just one [ChainShape] is created. factory Pathway.bezierCurve({ Color? color, - required Vector2 position, required List controlPoints, required double width, double rotation = 0, @@ -148,20 +140,15 @@ class Pathway extends BodyComponent { return Pathway._( color: color, - position: position, paths: paths, ); } - final Vector2 _position; final List> _paths; @override Body createBody() { - final bodyDef = BodyDef() - ..type = BodyType.static - ..position = _position; - + final bodyDef = BodyDef()..position = initialPosition; final body = world.createBody(bodyDef); for (final path in _paths) { final chain = ChainShape() diff --git a/lib/game/components/plunger.dart b/lib/game/components/plunger.dart index 9bcde451..6f319bd3 100644 --- a/lib/game/components/plunger.dart +++ b/lib/game/components/plunger.dart @@ -9,15 +9,11 @@ import 'package:pinball/game/game.dart'; /// /// [Plunger] ignores gravity so the player controls its downward [_pull]. /// {@endtemplate} -class Plunger extends BodyComponent with KeyboardHandler { +class Plunger extends BodyComponent with KeyboardHandler, InitialPosition { /// {@macro plunger} Plunger({ - required Vector2 position, required this.compressionDistance, - }) : _position = position; - - /// The initial position of the [Plunger] body. - final Vector2 _position; + }); /// Distance the plunger can lower. final double compressionDistance; @@ -29,8 +25,8 @@ class Plunger extends BodyComponent with KeyboardHandler { final fixtureDef = FixtureDef(shape)..density = 5; final bodyDef = BodyDef() + ..position = initialPosition ..userData = this - ..position = _position ..type = BodyType.dynamic ..gravityScale = 0; @@ -45,9 +41,9 @@ class Plunger extends BodyComponent with KeyboardHandler { /// Set an upward velocity on the [Plunger]. /// /// The velocity's magnitude depends on how far the [Plunger] has been pulled - /// from its original [_position]. + /// from its original [initialPosition]. void _release() { - final velocity = (_position.y - body.position.y) * 9; + final velocity = (initialPosition.y - body.position.y) * 9; body.linearVelocity = Vector2(0, velocity); } @@ -102,12 +98,12 @@ class PlungerAnchor extends JointAnchor { /// {@macro plunger_anchor} PlungerAnchor({ required Plunger plunger, - }) : super( - position: Vector2( - plunger.body.position.x, - plunger.body.position.y - plunger.compressionDistance, - ), - ); + }) { + initialPosition = Vector2( + plunger.body.position.x, + plunger.body.position.y - plunger.compressionDistance, + ); + } } /// {@template plunger_anchor_prismatic_joint_def} diff --git a/lib/game/components/round_bumper.dart b/lib/game/components/round_bumper.dart index 753ed15b..2f43a35b 100644 --- a/lib/game/components/round_bumper.dart +++ b/lib/game/components/round_bumper.dart @@ -4,19 +4,14 @@ import 'package:pinball/game/game.dart'; /// {@template round_bumper} /// Circular body that repels a [Ball] on contact, increasing the score. /// {@endtemplate} -class RoundBumper extends BodyComponent with ScorePoints { +class RoundBumper extends BodyComponent with ScorePoints, InitialPosition { /// {@macro round_bumper} RoundBumper({ - required Vector2 position, required double radius, required int points, - }) : _position = position, - _radius = radius, + }) : _radius = radius, _points = points; - /// The position of the [RoundBumper] body. - final Vector2 _position; - /// The radius of the [RoundBumper]. final double _radius; @@ -32,9 +27,7 @@ class RoundBumper extends BodyComponent with ScorePoints { final fixtureDef = FixtureDef(shape)..restitution = 1; - final bodyDef = BodyDef() - ..position = _position - ..type = BodyType.static; + final bodyDef = BodyDef()..position = initialPosition; return world.createBody(bodyDef)..createFixture(fixtureDef); } diff --git a/lib/game/components/sling_shot.dart b/lib/game/components/sling_shot.dart index dacf1ee5..61d28c29 100644 --- a/lib/game/components/sling_shot.dart +++ b/lib/game/components/sling_shot.dart @@ -10,22 +10,17 @@ import 'package:pinball/game/game.dart'; /// /// [SlingShot]s are usually positioned above each [Flipper]. /// {@endtemplate sling_shot} -class SlingShot extends BodyComponent { +class SlingShot extends BodyComponent with InitialPosition { /// {@macro sling_shot} SlingShot({ - required Vector2 position, required BoardSide side, - }) : _position = position, - _side = side { + }) : _side = side { // TODO(alestiago): Use sprite instead of color when provided. paint = Paint() ..color = const Color(0xFF00FF00) ..style = PaintingStyle.fill; } - /// The initial position of the [SlingShot] body. - final Vector2 _position; - /// Whether the [SlingShot] is on the left or right side of the board. /// /// A [SlingShot] with [BoardSide.left] propels the [Ball] to the right, @@ -33,18 +28,19 @@ class SlingShot extends BodyComponent { /// left. final BoardSide _side; + /// The size of the [SlingShot] body. + // TODO(alestiago): Use size from PositionedBodyComponent instead, + // once a sprite is given. + static final Vector2 size = Vector2(6, 8); + List _createFixtureDefs() { final fixtures = []; - // TODO(alestiago): Use size from PositionedBodyComponent instead, - // once a sprite is given. - final size = Vector2(10, 10); - // TODO(alestiago): This magic number can be deduced by specifying the // angle and using polar coordinate system to place the bottom right // vertex. // Something as: y = -size.y * math.cos(angle) - const additionalIncrement = 2; + const additionalIncrement = 3; final triangleVertices = _side.isLeft ? [ Vector2(0, 0), @@ -78,7 +74,7 @@ class SlingShot extends BodyComponent { ); // TODO(alestiago): Play with restitution value once game is bundled. final kickerFixtureDef = FixtureDef(kicker) - ..restitution = 20.0 + ..restitution = 10.0 ..friction = 0; fixtures.add(kickerFixtureDef); @@ -87,7 +83,7 @@ class SlingShot extends BodyComponent { @override Body createBody() { - final bodyDef = BodyDef()..position = _position; + final bodyDef = BodyDef()..position = initialPosition; final body = world.createBody(bodyDef); _createFixtureDefs().forEach(body.createFixture); diff --git a/lib/game/components/wall.dart b/lib/game/components/wall.dart index c433365c..017f8c4d 100644 --- a/lib/game/components/wall.dart +++ b/lib/game/components/wall.dart @@ -26,7 +26,7 @@ class Wall extends BodyComponent { final fixtureDef = FixtureDef(shape) ..restitution = 0.1 - ..friction = 0.3; + ..friction = 0; final bodyDef = BodyDef() ..userData = this diff --git a/lib/game/pinball_game.dart b/lib/game/pinball_game.dart index b67a8dad..61ef17e0 100644 --- a/lib/game/pinball_game.dart +++ b/lib/game/pinball_game.dart @@ -48,9 +48,20 @@ class PinballGame extends Forge2DGame ), ); - unawaited(_addFlippers()); - unawaited(_addBonusWord()); + unawaited( + add( + BottomGroup( + position: screenToWorld( + Vector2( + camera.viewport.effectiveSize.x / 2, + camera.viewport.effectiveSize.y / 1.25, + ), + ), + spacing: 2, + ), + ), + ); } Future _addBonusWord() async { @@ -66,27 +77,12 @@ class PinballGame extends Forge2DGame ); } - Future _addFlippers() async { - final flippersPosition = screenToWorld( - Vector2( - camera.viewport.effectiveSize.x / 2, - camera.viewport.effectiveSize.y / 1.1, - ), - ); - - unawaited( - add( - FlipperGroup( - position: flippersPosition, - spacing: 2, - ), - ), - ); - unawaited(_addBaseboards()); - } - void spawnBall() { - add(Ball(position: plunger.body.position)); + final ball = Ball(); + add( + ball + ..initialPosition = plunger.body.position + Vector2(0, ball.size.y / 2), + ); } void _addContactCallbacks() { @@ -101,44 +97,17 @@ class PinballGame extends Forge2DGame } Future _addPlunger() async { - final compressionDistance = camera.viewport.effectiveSize.y / 12; - - await add( - plunger = Plunger( - position: screenToWorld( - Vector2( - camera.viewport.effectiveSize.x / 1.035, - camera.viewport.effectiveSize.y - compressionDistance, - ), - ), - compressionDistance: compressionDistance, - ), + plunger = Plunger( + compressionDistance: camera.viewport.effectiveSize.y / 12, ); - } - - Future _addBaseboards() async { - final spaceBetweenBaseboards = camera.viewport.effectiveSize.x / 2; - final baseboardY = camera.viewport.effectiveSize.y / 1.12; - - final leftBaseboard = Baseboard.left( - position: screenToWorld( - Vector2( - camera.viewport.effectiveSize.x / 2 - (spaceBetweenBaseboards / 2), - baseboardY, - ), + plunger.initialPosition = screenToWorld( + Vector2( + camera.viewport.effectiveSize.x / 1.035, + camera.viewport.effectiveSize.y - plunger.compressionDistance, ), ); - await add(leftBaseboard); - final rightBaseboard = Baseboard.right( - position: screenToWorld( - Vector2( - camera.viewport.effectiveSize.x / 2 + (spaceBetweenBaseboards / 2), - baseboardY, - ), - ), - ); - await add(rightBaseboard); + await add(plunger); } } @@ -147,6 +116,8 @@ class DebugPinballGame extends PinballGame with TapDetector { @override void onTapUp(TapUpInfo info) { - add(Ball(position: info.eventPosition.game)); + add( + Ball()..initialPosition = info.eventPosition.game, + ); } } diff --git a/test/game/components/ball_test.dart b/test/game/components/ball_test.dart index e6172d6d..64d41908 100644 --- a/test/game/components/ball_test.dart +++ b/test/game/components/ball_test.dart @@ -17,7 +17,7 @@ void main() { flameTester.test( 'loads correctly', (game) async { - final ball = Ball(position: Vector2.zero()); + final ball = Ball(); await game.ready(); await game.ensureAdd(ball); @@ -26,26 +26,10 @@ void main() { ); group('body', () { - flameTester.test( - 'positions correctly', - (game) async { - final position = Vector2.all(10); - final ball = Ball(position: position); - await game.ensureAdd(ball); - game.contains(ball); - - final expectedPosition = Vector2( - position.x, - position.y + ball.size.y, - ); - expect(ball.body.position, equals(expectedPosition)); - }, - ); - flameTester.test( 'is dynamic', (game) async { - final ball = Ball(position: Vector2.zero()); + final ball = Ball(); await game.ensureAdd(ball); expect(ball.body.bodyType, equals(BodyType.dynamic)); @@ -57,7 +41,7 @@ void main() { flameTester.test( 'exists', (game) async { - final ball = Ball(position: Vector2.zero()); + final ball = Ball(); await game.ensureAdd(ball); expect(ball.body.fixtures[0], isA()); @@ -67,7 +51,7 @@ void main() { flameTester.test( 'is dense', (game) async { - final ball = Ball(position: Vector2.zero()); + final ball = Ball(); await game.ensureAdd(ball); final fixture = ball.body.fixtures[0]; @@ -78,7 +62,7 @@ void main() { flameTester.test( 'shape is circular', (game) async { - final ball = Ball(position: Vector2.zero()); + final ball = Ball(); await game.ensureAdd(ball); final fixture = ball.body.fixtures[0]; diff --git a/test/game/components/baseboard_test.dart b/test/game/components/baseboard_test.dart index bc9f68af..75cc62cc 100644 --- a/test/game/components/baseboard_test.dart +++ b/test/game/components/baseboard_test.dart @@ -14,8 +14,13 @@ void main() { 'loads correctly', (game) async { await game.ready(); - final leftBaseboard = Baseboard.left(position: Vector2.zero()); - final rightBaseboard = Baseboard.right(position: Vector2.zero()); + final leftBaseboard = Baseboard( + side: BoardSide.left, + ); + final rightBaseboard = Baseboard( + side: BoardSide.right, + ); + await game.ensureAddAll([leftBaseboard, rightBaseboard]); expect(game.contains(leftBaseboard), isTrue); @@ -24,22 +29,13 @@ void main() { ); group('body', () { - flameTester.test( - 'positions correctly', - (game) async { - final position = Vector2.all(10); - final baseboard = Baseboard.left(position: position); - await game.ensureAdd(baseboard); - game.contains(baseboard); - - expect(baseboard.body.position, position); - }, - ); - flameTester.test( 'is static', (game) async { - final baseboard = Baseboard.left(position: Vector2.zero()); + final baseboard = Baseboard( + side: BoardSide.left, + ); + await game.ensureAdd(baseboard); expect(baseboard.body.bodyType, equals(BodyType.static)); @@ -49,8 +45,12 @@ void main() { flameTester.test( 'is at an angle', (game) async { - final leftBaseboard = Baseboard.left(position: Vector2.zero()); - final rightBaseboard = Baseboard.right(position: Vector2.zero()); + final leftBaseboard = Baseboard( + side: BoardSide.left, + ); + final rightBaseboard = Baseboard( + side: BoardSide.right, + ); await game.ensureAddAll([leftBaseboard, rightBaseboard]); expect(leftBaseboard.body.angle, isNegative); @@ -63,7 +63,9 @@ void main() { flameTester.test( 'has three', (game) async { - final baseboard = Baseboard.left(position: Vector2.zero()); + final baseboard = Baseboard( + side: BoardSide.left, + ); await game.ensureAdd(baseboard); expect(baseboard.body.fixtures.length, equals(3)); diff --git a/test/game/components/board_test.dart b/test/game/components/board_test.dart new file mode 100644 index 00000000..34a628a8 --- /dev/null +++ b/test/game/components/board_test.dart @@ -0,0 +1,80 @@ +// ignore_for_file: cascade_invocations + +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(Forge2DGame.new); + + group('BottomGroup', () { + flameTester.test( + 'loads correctly', + (game) async { + final bottomGroup = BottomGroup(position: Vector2.zero(), spacing: 0); + await game.ready(); + await game.ensureAdd(bottomGroup); + + expect(game.contains(bottomGroup), isTrue); + }, + ); + + group('children', () { + flameTester.test( + 'has one left flipper', + (game) async { + final bottomGroup = BottomGroup(position: Vector2.zero(), spacing: 0); + await game.ready(); + await game.ensureAdd(bottomGroup); + + final leftFlippers = bottomGroup.findNestedChildren( + condition: (flipper) => flipper.side.isLeft, + ); + expect(leftFlippers.length, equals(1)); + }, + ); + + flameTester.test( + 'has one right flipper', + (game) async { + final bottomGroup = BottomGroup(position: Vector2.zero(), spacing: 0); + await game.ready(); + await game.ensureAdd(bottomGroup); + + final rightFlippers = bottomGroup.findNestedChildren( + condition: (flipper) => flipper.side.isRight, + ); + expect(rightFlippers.length, equals(1)); + }, + ); + + flameTester.test( + 'has two Baseboards', + (game) async { + final bottomGroup = BottomGroup(position: Vector2.zero(), spacing: 0); + await game.ready(); + await game.ensureAdd(bottomGroup); + + final baseboards = bottomGroup.findNestedChildren(); + expect(baseboards.length, equals(2)); + }, + ); + + flameTester.test( + 'has two SlingShots', + (game) async { + final bottomGroup = BottomGroup(position: Vector2.zero(), spacing: 0); + await game.ready(); + await game.ensureAdd(bottomGroup); + + final slingShots = bottomGroup.findNestedChildren(); + expect(slingShots.length, equals(2)); + }, + ); + }); + }); +} diff --git a/test/game/components/bonus_word_test.dart b/test/game/components/bonus_word_test.dart index b45bd61a..a9af305e 100644 --- a/test/game/components/bonus_word_test.dart +++ b/test/game/components/bonus_word_test.dart @@ -123,7 +123,6 @@ void main() { 'loads correctly', (game) async { final bonusLetter = BonusLetter( - position: Vector2.zero(), letter: 'G', index: 0, ); @@ -135,27 +134,10 @@ void main() { ); group('body', () { - flameTester.test( - 'positions correctly', - (game) async { - final position = Vector2.all(10); - final bonusLetter = BonusLetter( - position: position, - letter: 'G', - index: 0, - ); - await game.ensureAdd(bonusLetter); - game.contains(bonusLetter); - - expect(bonusLetter.body.position, position); - }, - ); - flameTester.test( 'is static', (game) async { final bonusLetter = BonusLetter( - position: Vector2.zero(), letter: 'G', index: 0, ); @@ -171,7 +153,6 @@ void main() { 'exists', (game) async { final bonusLetter = BonusLetter( - position: Vector2.zero(), letter: 'G', index: 0, ); @@ -185,7 +166,6 @@ void main() { 'is sensor', (game) async { final bonusLetter = BonusLetter( - position: Vector2.zero(), letter: 'G', index: 0, ); @@ -200,7 +180,6 @@ void main() { 'shape is circular', (game) async { final bonusLetter = BonusLetter( - position: Vector2.zero(), letter: 'G', index: 0, ); diff --git a/test/game/components/flipper_test.dart b/test/game/components/flipper_test.dart index 0e281d07..e6e9ba23 100644 --- a/test/game/components/flipper_test.dart +++ b/test/game/components/flipper_test.dart @@ -2,7 +2,6 @@ import 'dart:collection'; -import 'package:flame/components.dart'; import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flame_test/flame_test.dart'; import 'package:flutter/services.dart'; @@ -15,110 +14,18 @@ void main() { TestWidgetsFlutterBinding.ensureInitialized(); final flameTester = FlameTester(PinballGameTest.create); - group('FlipperGroup', () { - flameTester.test( - 'loads correctly', - (game) async { - final flipperGroup = FlipperGroup( - position: Vector2.zero(), - spacing: 0, - ); - await game.ensureAdd(flipperGroup); - - expect(game.contains(flipperGroup), isTrue); - }, - ); - - group('constructor', () { - flameTester.test( - 'positions correctly', - (game) async { - final position = Vector2.all(10); - final flipperGroup = FlipperGroup( - position: position, - spacing: 0, - ); - await game.ensureAdd(flipperGroup); - - expect(flipperGroup.position, equals(position)); - }, - ); - }); - - group('children', () { - bool Function(Component) flipperSelector(BoardSide side) => - (component) => component is Flipper && component.side == side; - - flameTester.test( - 'has only one left Flipper', - (game) async { - final flipperGroup = FlipperGroup( - position: Vector2.zero(), - spacing: 0, - ); - await game.ensureAdd(flipperGroup); - - expect( - () => flipperGroup.children.singleWhere( - flipperSelector(BoardSide.left), - ), - returnsNormally, - ); - }, - ); - - flameTester.test( - 'has only one right Flipper', - (game) async { - final flipperGroup = FlipperGroup( - position: Vector2.zero(), - spacing: 0, - ); - await game.ensureAdd(flipperGroup); - - expect( - () => flipperGroup.children.singleWhere( - flipperSelector(BoardSide.right), - ), - returnsNormally, - ); - }, - ); - - flameTester.test( - 'spaced correctly', - (game) async { - final flipperGroup = FlipperGroup( - position: Vector2.zero(), - spacing: 2, - ); - await game.ready(); - await game.ensureAdd(flipperGroup); - - final leftFlipper = flipperGroup.children.singleWhere( - flipperSelector(BoardSide.left), - ) as Flipper; - final rightFlipper = flipperGroup.children.singleWhere( - flipperSelector(BoardSide.right), - ) as Flipper; - - expect( - leftFlipper.body.position.x + Flipper.width + flipperGroup.spacing, - equals(rightFlipper.body.position.x), - ); - }, - ); - }); - }); - group( 'Flipper', () { flameTester.test( 'loads correctly', (game) async { - final leftFlipper = Flipper.left(position: Vector2.zero()); - final rightFlipper = Flipper.right(position: Vector2.zero()); + final leftFlipper = Flipper.fromSide( + side: BoardSide.left, + ); + final rightFlipper = Flipper.fromSide( + side: BoardSide.right, + ); await game.ready(); await game.ensureAddAll([leftFlipper, rightFlipper]); @@ -129,31 +36,26 @@ void main() { group('constructor', () { test('sets BoardSide', () { - final leftFlipper = Flipper.left(position: Vector2.zero()); + final leftFlipper = Flipper.fromSide( + side: BoardSide.left, + ); + expect(leftFlipper.side, equals(leftFlipper.side)); - final rightFlipper = Flipper.right(position: Vector2.zero()); + final rightFlipper = Flipper.fromSide( + side: BoardSide.right, + ); expect(rightFlipper.side, equals(rightFlipper.side)); }); }); group('body', () { - flameTester.test( - 'positions correctly', - (game) async { - final position = Vector2.all(10); - final flipper = Flipper.left(position: position); - await game.ensureAdd(flipper); - game.contains(flipper); - - expect(flipper.body.position, position); - }, - ); - flameTester.test( 'is dynamic', (game) async { - final flipper = Flipper.left(position: Vector2.zero()); + final flipper = Flipper.fromSide( + side: BoardSide.left, + ); await game.ensureAdd(flipper); expect(flipper.body.bodyType, equals(BodyType.dynamic)); @@ -163,7 +65,9 @@ void main() { flameTester.test( 'ignores gravity', (game) async { - final flipper = Flipper.left(position: Vector2.zero()); + final flipper = Flipper.fromSide( + side: BoardSide.left, + ); await game.ensureAdd(flipper); expect(flipper.body.gravityScale, isZero); @@ -173,8 +77,10 @@ void main() { flameTester.test( 'has greater mass than Ball', (game) async { - final flipper = Flipper.left(position: Vector2.zero()); - final ball = Ball(position: Vector2.zero()); + final flipper = Flipper.fromSide( + side: BoardSide.left, + ); + final ball = Ball(); await game.ready(); await game.ensureAddAll([flipper, ball]); @@ -191,7 +97,9 @@ void main() { flameTester.test( 'has three', (game) async { - final flipper = Flipper.left(position: Vector2.zero()); + final flipper = Flipper.fromSide( + side: BoardSide.left, + ); await game.ensureAdd(flipper); expect(flipper.body.fixtures.length, equals(3)); @@ -201,7 +109,9 @@ void main() { flameTester.test( 'has density', (game) async { - final flipper = Flipper.left(position: Vector2.zero()); + final flipper = Flipper.fromSide( + side: BoardSide.left, + ); await game.ensureAdd(flipper); final fixtures = flipper.body.fixtures; @@ -229,7 +139,9 @@ void main() { late Flipper flipper; setUp(() { - flipper = Flipper.left(position: Vector2.zero()); + flipper = Flipper.fromSide( + side: BoardSide.left, + ); }); testRawKeyDownEvents(leftKeys, (event) { @@ -293,7 +205,9 @@ void main() { late Flipper flipper; setUp(() { - flipper = Flipper.right(position: Vector2.zero()); + flipper = Flipper.fromSide( + side: BoardSide.right, + ); }); testRawKeyDownEvents(rightKeys, (event) { @@ -360,7 +274,9 @@ void main() { flameTester.test( 'position is at the left of the left Flipper', (game) async { - final flipper = Flipper.left(position: Vector2.zero()); + final flipper = Flipper.fromSide( + side: BoardSide.left, + ); await game.ensureAdd(flipper); final flipperAnchor = FlipperAnchor(flipper: flipper); @@ -373,7 +289,9 @@ void main() { flameTester.test( 'position is at the right of the right Flipper', (game) async { - final flipper = Flipper.right(position: Vector2.zero()); + final flipper = Flipper.fromSide( + side: BoardSide.right, + ); await game.ensureAdd(flipper); final flipperAnchor = FlipperAnchor(flipper: flipper); @@ -389,7 +307,9 @@ void main() { flameTester.test( 'limits enabled', (game) async { - final flipper = Flipper.left(position: Vector2.zero()); + final flipper = Flipper.fromSide( + side: BoardSide.left, + ); await game.ensureAdd(flipper); final flipperAnchor = FlipperAnchor(flipper: flipper); @@ -408,7 +328,9 @@ void main() { flameTester.test( 'when Flipper is left', (game) async { - final flipper = Flipper.left(position: Vector2.zero()); + final flipper = Flipper.fromSide( + side: BoardSide.left, + ); await game.ensureAdd(flipper); final flipperAnchor = FlipperAnchor(flipper: flipper); @@ -426,7 +348,9 @@ void main() { flameTester.test( 'when Flipper is right', (game) async { - final flipper = Flipper.right(position: Vector2.zero()); + final flipper = Flipper.fromSide( + side: BoardSide.right, + ); await game.ensureAdd(flipper); final flipperAnchor = FlipperAnchor(flipper: flipper); @@ -449,7 +373,9 @@ void main() { flameTester.test( 'when Flipper is left', (game) async { - final flipper = Flipper.left(position: Vector2.zero()); + final flipper = Flipper.fromSide( + side: BoardSide.left, + ); await game.ensureAdd(flipper); final flipperAnchor = FlipperAnchor(flipper: flipper); @@ -473,7 +399,9 @@ void main() { flameTester.test( 'when Flipper is right', (game) async { - final flipper = Flipper.right(position: Vector2.zero()); + final flipper = Flipper.fromSide( + side: BoardSide.right, + ); await game.ensureAdd(flipper); final flipperAnchor = FlipperAnchor(flipper: flipper); diff --git a/test/game/components/initial_position_test.dart b/test/game/components/initial_position_test.dart new file mode 100644 index 00000000..ece083cc --- /dev/null +++ b/test/game/components/initial_position_test.dart @@ -0,0 +1,81 @@ +// ignore_for_file: cascade_invocations + +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'; + +class TestBodyComponent extends BodyComponent with InitialPosition { + @override + Body createBody() { + return world.createBody(BodyDef()); + } +} + +class TestPositionedBodyComponent extends BodyComponent with InitialPosition { + @override + Body createBody() { + return world.createBody(BodyDef()..position = initialPosition); + } +} + +void main() { + final flameTester = FlameTester(Forge2DGame.new); + group('InitialPosition', () { + test('correctly sets and gets', () { + final component = TestBodyComponent()..initialPosition = Vector2(1, 2); + expect(component.initialPosition, Vector2(1, 2)); + }); + + flameTester.test( + 'throws AssertionError ' + 'when BodyDef is not positioned with initialPosition', + (game) async { + final component = TestBodyComponent() + ..initialPosition = Vector2.all( + 10, + ); + await expectLater( + () => game.ensureAdd(component), + throwsAssertionError, + ); + }, + ); + + flameTester.test( + 'positions correctly', + (game) async { + final position = Vector2.all(10); + final component = TestPositionedBodyComponent() + ..initialPosition = position; + await game.ensureAdd(component); + expect(component.body.position, equals(position)); + }, + ); + + flameTester.test( + 'deafaults to zero ' + 'when no initialPosition is given', + (game) async { + final component = TestBodyComponent(); + await game.ensureAdd(component); + expect(component.body.position, equals(Vector2.zero())); + }, + ); + + flameTester.test( + 'setting throws AssertionError ' + 'when component has loaded', + (game) async { + final component = TestBodyComponent(); + await game.ensureAdd(component); + + expect(component.isLoaded, isTrue); + expect( + () => component.initialPosition = Vector2.all(4), + throwsAssertionError, + ); + }, + ); + }); +} diff --git a/test/game/components/joint_anchor_test.dart b/test/game/components/joint_anchor_test.dart index 8278d43a..652bd445 100644 --- a/test/game/components/joint_anchor_test.dart +++ b/test/game/components/joint_anchor_test.dart @@ -13,7 +13,7 @@ void main() { flameTester.test( 'loads correctly', (game) async { - final anchor = JointAnchor(position: Vector2.zero()); + final anchor = JointAnchor(); await game.ready(); await game.ensureAdd(anchor); @@ -22,24 +22,11 @@ void main() { ); group('body', () { - flameTester.test( - 'positions correctly', - (game) async { - await game.ready(); - final position = Vector2.all(10); - final anchor = JointAnchor(position: position); - await game.ensureAdd(anchor); - game.contains(anchor); - - expect(anchor.body.position, position); - }, - ); - flameTester.test( 'is static', (game) async { await game.ready(); - final anchor = JointAnchor(position: Vector2.zero()); + final anchor = JointAnchor(); await game.ensureAdd(anchor); expect(anchor.body.bodyType, equals(BodyType.static)); @@ -51,7 +38,7 @@ void main() { flameTester.test( 'has none', (game) async { - final anchor = JointAnchor(position: Vector2.zero()); + final anchor = JointAnchor(); await game.ensureAdd(anchor); expect(anchor.body.fixtures, isEmpty); diff --git a/test/game/components/pathway_test.dart b/test/game/components/pathway_test.dart index 80c968d8..550714cb 100644 --- a/test/game/components/pathway_test.dart +++ b/test/game/components/pathway_test.dart @@ -20,7 +20,6 @@ void main() { (game) async { await game.ready(); final pathway = Pathway.straight( - position: Vector2.zero(), start: Vector2(10, 10), end: Vector2(20, 20), width: width, @@ -44,7 +43,6 @@ void main() { final pathway = Pathway.straight( color: defaultColor, - position: Vector2.zero(), start: Vector2(10, 10), end: Vector2(20, 20), width: width, @@ -63,7 +61,6 @@ void main() { (game) async { await game.ready(); final pathway = Pathway.straight( - position: Vector2.zero(), start: Vector2(10, 10), end: Vector2(20, 20), width: width, @@ -75,30 +72,11 @@ void main() { ); group('body', () { - flameTester.test( - 'positions correctly', - (game) async { - await game.ready(); - final position = Vector2.all(10); - final pathway = Pathway.straight( - position: position, - start: Vector2(10, 10), - end: Vector2(20, 20), - width: width, - ); - await game.ensureAdd(pathway); - - game.contains(pathway); - expect(pathway.body.position, position); - }, - ); - flameTester.test( 'is static', (game) async { await game.ready(); final pathway = Pathway.straight( - position: Vector2.zero(), start: Vector2(10, 10), end: Vector2(20, 20), width: width, @@ -116,7 +94,6 @@ void main() { (game) async { await game.ready(); final pathway = Pathway.straight( - position: Vector2.zero(), start: Vector2(10, 10), end: Vector2(20, 20), width: width, @@ -136,7 +113,6 @@ void main() { (game) async { await game.ready(); final pathway = Pathway.straight( - position: Vector2.zero(), start: Vector2(10, 10), end: Vector2(20, 20), width: width, @@ -159,7 +135,7 @@ void main() { (game) async { await game.ready(); final pathway = Pathway.arc( - position: Vector2.zero(), + center: Vector2.zero(), width: width, radius: 100, angle: math.pi / 2, @@ -171,30 +147,12 @@ void main() { ); group('body', () { - flameTester.test( - 'positions correctly', - (game) async { - await game.ready(); - final position = Vector2.all(10); - final pathway = Pathway.arc( - position: position, - width: width, - radius: 100, - angle: math.pi / 2, - ); - await game.ensureAdd(pathway); - - game.contains(pathway); - expect(pathway.body.position, position); - }, - ); - flameTester.test( 'is static', (game) async { await game.ready(); final pathway = Pathway.arc( - position: Vector2.zero(), + center: Vector2.zero(), width: width, radius: 100, angle: math.pi / 2, @@ -220,7 +178,6 @@ void main() { (game) async { await game.ready(); final pathway = Pathway.bezierCurve( - position: Vector2.zero(), controlPoints: controlPoints, width: width, ); @@ -231,29 +188,11 @@ void main() { ); group('body', () { - flameTester.test( - 'positions correctly', - (game) async { - await game.ready(); - final position = Vector2.all(10); - final pathway = Pathway.bezierCurve( - position: position, - controlPoints: controlPoints, - width: width, - ); - await game.ensureAdd(pathway); - - game.contains(pathway); - expect(pathway.body.position, position); - }, - ); - flameTester.test( 'is static', (game) async { await game.ready(); final pathway = Pathway.bezierCurve( - position: Vector2.zero(), controlPoints: controlPoints, width: width, ); diff --git a/test/game/components/plunger_test.dart b/test/game/components/plunger_test.dart index ecc4265e..1cec7e0c 100644 --- a/test/game/components/plunger_test.dart +++ b/test/game/components/plunger_test.dart @@ -23,7 +23,6 @@ void main() { (game) async { await game.ready(); final plunger = Plunger( - position: Vector2.zero(), compressionDistance: compressionDistance, ); await game.ensureAdd(plunger); @@ -33,26 +32,10 @@ void main() { ); group('body', () { - flameTester.test( - 'positions correctly', - (game) async { - final position = Vector2.all(10); - final plunger = Plunger( - position: position, - compressionDistance: compressionDistance, - ); - await game.ensureAdd(plunger); - game.contains(plunger); - - expect(plunger.body.position, position); - }, - ); - flameTester.test( 'is dynamic', (game) async { final plunger = Plunger( - position: Vector2.zero(), compressionDistance: compressionDistance, ); await game.ensureAdd(plunger); @@ -65,7 +48,6 @@ void main() { 'ignores gravity', (game) async { final plunger = Plunger( - position: Vector2.zero(), compressionDistance: compressionDistance, ); await game.ensureAdd(plunger); @@ -80,7 +62,6 @@ void main() { 'exists', (game) async { final plunger = Plunger( - position: Vector2.zero(), compressionDistance: compressionDistance, ); await game.ensureAdd(plunger); @@ -93,7 +74,6 @@ void main() { 'shape is a polygon', (game) async { final plunger = Plunger( - position: Vector2.zero(), compressionDistance: compressionDistance, ); await game.ensureAdd(plunger); @@ -107,7 +87,6 @@ void main() { 'has density', (game) async { final plunger = Plunger( - position: Vector2.zero(), compressionDistance: compressionDistance, ); await game.ensureAdd(plunger); @@ -129,7 +108,6 @@ void main() { setUp(() { plunger = Plunger( - position: Vector2.zero(), compressionDistance: compressionDistance, ); }); @@ -194,7 +172,6 @@ void main() { 'position is a compression distance below the Plunger', (game) async { final plunger = Plunger( - position: Vector2.zero(), compressionDistance: compressionDistance, ); await game.ensureAdd(plunger); @@ -222,7 +199,6 @@ void main() { initialState: const GameState.initial(), ); plunger = Plunger( - position: Vector2.zero(), compressionDistance: compressionDistance, ); }); diff --git a/test/game/components/round_bumper_test.dart b/test/game/components/round_bumper_test.dart index c780dd0b..437167ad 100644 --- a/test/game/components/round_bumper_test.dart +++ b/test/game/components/round_bumper_test.dart @@ -18,7 +18,6 @@ void main() { (game) async { await game.ready(); final roundBumper = RoundBumper( - position: Vector2.zero(), radius: radius, points: points, ); @@ -32,7 +31,6 @@ void main() { 'has points', (game) async { final roundBumper = RoundBumper( - position: Vector2.zero(), radius: radius, points: points, ); @@ -43,27 +41,10 @@ void main() { ); group('body', () { - flameTester.test( - 'positions correctly', - (game) async { - final position = Vector2.all(10); - final roundBumper = RoundBumper( - position: position, - radius: radius, - points: points, - ); - await game.ensureAdd(roundBumper); - game.contains(roundBumper); - - expect(roundBumper.body.position, equals(position)); - }, - ); - flameTester.test( 'is static', (game) async { final roundBumper = RoundBumper( - position: Vector2.zero(), radius: radius, points: points, ); @@ -79,7 +60,6 @@ void main() { 'exists', (game) async { final roundBumper = RoundBumper( - position: Vector2.zero(), radius: radius, points: points, ); @@ -93,7 +73,6 @@ void main() { 'has restitution', (game) async { final roundBumper = RoundBumper( - position: Vector2.zero(), radius: radius, points: points, ); @@ -108,7 +87,6 @@ void main() { 'shape is circular', (game) async { final roundBumper = RoundBumper( - position: Vector2.zero(), radius: radius, points: points, ); diff --git a/test/game/components/sling_shot_test.dart b/test/game/components/sling_shot_test.dart index e7e89ead..e7c796a1 100644 --- a/test/game/components/sling_shot_test.dart +++ b/test/game/components/sling_shot_test.dart @@ -13,7 +13,6 @@ void main() { 'loads correctly', (game) async { final slingShot = SlingShot( - position: Vector2.zero(), side: BoardSide.left, ); await game.ensureAdd(slingShot); @@ -23,25 +22,10 @@ void main() { ); group('body', () { - flameTester.test( - 'positions correctly', - (game) async { - final position = Vector2.all(10); - final slingShot = SlingShot( - position: position, - side: BoardSide.left, - ); - await game.ensureAdd(slingShot); - - expect(slingShot.body.position, equals(position)); - }, - ); - flameTester.test( 'is static', (game) async { final slingShot = SlingShot( - position: Vector2.zero(), side: BoardSide.left, ); await game.ensureAdd(slingShot); @@ -56,7 +40,6 @@ void main() { 'exists', (game) async { final slingShot = SlingShot( - position: Vector2.zero(), side: BoardSide.left, ); await game.ensureAdd(slingShot); @@ -69,7 +52,6 @@ void main() { 'shape is triangular', (game) async { final slingShot = SlingShot( - position: Vector2.zero(), side: BoardSide.left, ); await game.ensureAdd(slingShot); @@ -85,11 +67,9 @@ void main() { 'when side is left or right', (game) async { final leftSlingShot = SlingShot( - position: Vector2.zero(), side: BoardSide.left, ); final rightSlingShot = SlingShot( - position: Vector2.zero(), side: BoardSide.right, ); @@ -109,7 +89,6 @@ void main() { 'has no friction', (game) async { final slingShot = SlingShot( - position: Vector2.zero(), side: BoardSide.left, ); await game.ensureAdd(slingShot); @@ -125,7 +104,6 @@ void main() { 'exists', (game) async { final slingShot = SlingShot( - position: Vector2.zero(), side: BoardSide.left, ); await game.ensureAdd(slingShot); @@ -138,7 +116,6 @@ void main() { 'shape is edge', (game) async { final slingShot = SlingShot( - position: Vector2.zero(), side: BoardSide.left, ); await game.ensureAdd(slingShot); @@ -152,7 +129,6 @@ void main() { 'has restitution', (game) async { final slingShot = SlingShot( - position: Vector2.zero(), side: BoardSide.left, ); await game.ensureAdd(slingShot); @@ -166,7 +142,6 @@ void main() { 'has no friction', (game) async { final slingShot = SlingShot( - position: Vector2.zero(), side: BoardSide.left, ); await game.ensureAdd(slingShot); diff --git a/test/game/components/wall_test.dart b/test/game/components/wall_test.dart index 774cd675..53d387fa 100644 --- a/test/game/components/wall_test.dart +++ b/test/game/components/wall_test.dart @@ -106,7 +106,7 @@ void main() { ); flameTester.test( - 'has friction', + 'has no friction', (game) async { final wall = Wall( start: Vector2.zero(), @@ -115,7 +115,7 @@ void main() { await game.ensureAdd(wall); final fixture = wall.body.fixtures[0]; - expect(fixture.friction, greaterThan(0)); + expect(fixture.friction, equals(0)); }, ); }); diff --git a/test/game/pinball_game_test.dart b/test/game/pinball_game_test.dart index f79d19d5..f7d0f7db 100644 --- a/test/game/pinball_game_test.dart +++ b/test/game/pinball_game_test.dart @@ -54,22 +54,13 @@ void main() { }, ); - flameTester.test('has only one FlipperGroup', (game) async { + flameTester.test('has only one BottomGroup', (game) async { await game.ready(); expect( - game.children.whereType().length, + game.children.whereType().length, equals(1), ); }); - - flameTester.test( - 'has two Baseboards', - (game) async { - await game.ready(); - final baseboards = game.children.whereType(); - expect(baseboards.length, 2); - }, - ); }); debugModeFlameTester.test('adds a ball on tap up', (game) async { diff --git a/test/helpers/extensions.dart b/test/helpers/extensions.dart index 2a0a7e59..a5c56a86 100644 --- a/test/helpers/extensions.dart +++ b/test/helpers/extensions.dart @@ -1,3 +1,4 @@ +import 'package:flame/components.dart'; import 'package:pinball/game/game.dart'; import 'package:pinball_theme/pinball_theme.dart'; @@ -20,3 +21,41 @@ extension DebugPinballGameTest on DebugPinballGame { ), ); } + +extension ComponentX on Component { + T findNestedChild({ + bool Function(T)? condition, + }) { + T? nestedChild; + propagateToChildren((child) { + final foundChild = (condition ?? (_) => true)(child); + if (foundChild) { + nestedChild = child; + } + + return !foundChild; + }); + + if (nestedChild == null) { + throw Exception('No child of type $T found.'); + } else { + return nestedChild!; + } + } + + List findNestedChildren({ + bool Function(T)? condition, + }) { + final nestedChildren = []; + propagateToChildren((child) { + final foundChild = (condition ?? (_) => true)(child); + if (foundChild) { + nestedChildren.add(child); + } + + return true; + }); + + return nestedChildren; + } +}