From 7a8f8088226c6cdad1be32247f0b8162a458bdff Mon Sep 17 00:00:00 2001 From: Alejandro Santiago Date: Tue, 15 Mar 2022 16:38:17 +0000 Subject: [PATCH 1/5] feat: group Baseboards and Flipper (#47) * feat: connected baseboards to flipper * chore: removed FlipperGroup * refactor: simplified Baseboard constructor * refactor: simplified BottomGroup * refactor: changed constructors * refactor: removed unecessary import * refactor: modified Flipper constructor * docs: updated doc comment * docs: used macro for BottomGroupSide * docs: improved doc comment * refactor: renamed magnitude to direction * refactor: renamed bumper to baseboard * feat: included board tests * docs: improved doc comment * feat: include boardSide.direction (#46) * feat: implemented BoardSide.direction * docs: included doc comment * refactor: used _side.direction * refactor: used ensureAdd * chore: removed old test * docs: improved comments --- lib/game/components/baseboard.dart | 22 +-- lib/game/components/board.dart | 76 +++++++++ lib/game/components/components.dart | 1 + lib/game/components/flipper.dart | 58 +++---- lib/game/pinball_game.dart | 59 ++----- test/game/components/baseboard_test.dart | 35 +++- test/game/components/board_test.dart | 68 ++++++++ test/game/components/flipper_test.dart | 193 +++++++++-------------- test/game/pinball_game_test.dart | 13 +- test/helpers/extensions.dart | 39 +++++ 10 files changed, 325 insertions(+), 239 deletions(-) create mode 100644 lib/game/components/board.dart create mode 100644 test/game/components/board_test.dart diff --git a/lib/game/components/baseboard.dart b/lib/game/components/baseboard.dart index 9153d4f3..60e51593 100644 --- a/lib/game/components/baseboard.dart +++ b/lib/game/components/baseboard.dart @@ -8,27 +8,11 @@ import 'package:pinball/game/game.dart'; /// {@endtemplate} class Baseboard extends BodyComponent { /// {@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, + _position = position; /// The width of the [Baseboard]. static const width = 10.0; diff --git a/lib/game/components/board.dart b/lib/game/components/board.dart new file mode 100644 index 00000000..80db1711 --- /dev/null +++ b/lib/game/components/board.dart @@ -0,0 +1,76 @@ +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): Add [SlingShot] once provided. +// 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, + position: _position, + ); + final baseboard = Baseboard( + side: _side, + position: _position + + Vector2( + (Flipper.width * direction) - direction, + Flipper.height, + ), + ); + + await addAll([flipper, baseboard]); + } +} diff --git a/lib/game/components/components.dart b/lib/game/components/components.dart index d5c479c7..f733cfcd 100644 --- a/lib/game/components/components.dart +++ b/lib/game/components/components.dart @@ -1,5 +1,6 @@ export 'ball.dart'; export 'baseboard.dart'; +export 'board.dart'; export 'board_side.dart'; export 'bonus_word.dart'; export 'flipper.dart'; diff --git a/lib/game/components/flipper.dart b/lib/game/components/flipper.dart index 9035daaf..ff4754ea 100644 --- a/lib/game/components/flipper.dart +++ b/lib/game/components/flipper.dart @@ -8,42 +8,6 @@ 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. /// @@ -58,8 +22,7 @@ class Flipper extends BodyComponent with KeyboardHandler { }) : _position = position, _keys = keys; - /// A left positioned [Flipper]. - Flipper.left({ + Flipper._left({ required Vector2 position, }) : this._( position: position, @@ -70,8 +33,7 @@ class Flipper extends BodyComponent with KeyboardHandler { ], ); - /// A right positioned [Flipper]. - Flipper.right({ + Flipper._right({ required Vector2 position, }) : this._( position: position, @@ -82,6 +44,22 @@ 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, + required Vector2 position, + }) { + switch (side) { + case BoardSide.left: + return Flipper._left(position: position); + case BoardSide.right: + return Flipper._right(position: position); + } + } + /// Asset location of the sprite that renders with the [Flipper]. /// /// Sprite is preloaded by [PinballGameAssetsX]. diff --git a/lib/game/pinball_game.dart b/lib/game/pinball_game.dart index b67a8dad..112f0522 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,25 +77,6 @@ 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)); } @@ -115,31 +107,6 @@ class PinballGame extends Forge2DGame ), ); } - - 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, - ), - ), - ); - await add(leftBaseboard); - - final rightBaseboard = Baseboard.right( - position: screenToWorld( - Vector2( - camera.viewport.effectiveSize.x / 2 + (spaceBetweenBaseboards / 2), - baseboardY, - ), - ), - ); - await add(rightBaseboard); - } } class DebugPinballGame extends PinballGame with TapDetector { diff --git a/test/game/components/baseboard_test.dart b/test/game/components/baseboard_test.dart index bc9f68af..8f1874b1 100644 --- a/test/game/components/baseboard_test.dart +++ b/test/game/components/baseboard_test.dart @@ -14,8 +14,14 @@ 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( + position: Vector2.zero(), + side: BoardSide.left, + ); + final rightBaseboard = Baseboard( + position: Vector2.zero(), + side: BoardSide.right, + ); await game.ensureAddAll([leftBaseboard, rightBaseboard]); expect(game.contains(leftBaseboard), isTrue); @@ -28,7 +34,10 @@ void main() { 'positions correctly', (game) async { final position = Vector2.all(10); - final baseboard = Baseboard.left(position: position); + final baseboard = Baseboard( + position: position, + side: BoardSide.left, + ); await game.ensureAdd(baseboard); game.contains(baseboard); @@ -39,7 +48,10 @@ void main() { flameTester.test( 'is static', (game) async { - final baseboard = Baseboard.left(position: Vector2.zero()); + final baseboard = Baseboard( + position: Vector2.zero(), + side: BoardSide.left, + ); await game.ensureAdd(baseboard); expect(baseboard.body.bodyType, equals(BodyType.static)); @@ -49,8 +61,14 @@ 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( + position: Vector2.zero(), + side: BoardSide.left, + ); + final rightBaseboard = Baseboard( + position: Vector2.zero(), + side: BoardSide.right, + ); await game.ensureAddAll([leftBaseboard, rightBaseboard]); expect(leftBaseboard.body.angle, isNegative); @@ -63,7 +81,10 @@ void main() { flameTester.test( 'has three', (game) async { - final baseboard = Baseboard.left(position: Vector2.zero()); + final baseboard = Baseboard( + position: Vector2.zero(), + 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..eaa916c3 --- /dev/null +++ b/test/game/components/board_test.dart @@ -0,0 +1,68 @@ +// 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 leftFlippers = bottomGroup.findNestedChildren( + condition: (flipper) => flipper.side.isRight, + ); + expect(leftFlippers.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 leftFlippers = bottomGroup.findNestedChildren(); + expect(leftFlippers.length, equals(2)); + }, + ); + }); + }); +} diff --git a/test/game/components/flipper_test.dart b/test/game/components/flipper_test.dart index 0e281d07..eae238c9 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,20 @@ 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, + position: Vector2.zero(), + ); + final rightFlipper = Flipper.fromSide( + side: BoardSide.right, + position: Vector2.zero(), + ); await game.ready(); await game.ensureAddAll([leftFlipper, rightFlipper]); @@ -129,10 +38,17 @@ void main() { group('constructor', () { test('sets BoardSide', () { - final leftFlipper = Flipper.left(position: Vector2.zero()); + final leftFlipper = Flipper.fromSide( + side: BoardSide.left, + position: Vector2.zero(), + ); + expect(leftFlipper.side, equals(leftFlipper.side)); - final rightFlipper = Flipper.right(position: Vector2.zero()); + final rightFlipper = Flipper.fromSide( + side: BoardSide.right, + position: Vector2.zero(), + ); expect(rightFlipper.side, equals(rightFlipper.side)); }); }); @@ -142,7 +58,10 @@ void main() { 'positions correctly', (game) async { final position = Vector2.all(10); - final flipper = Flipper.left(position: position); + final flipper = Flipper.fromSide( + side: BoardSide.left, + position: position, + ); await game.ensureAdd(flipper); game.contains(flipper); @@ -153,7 +72,10 @@ void main() { flameTester.test( 'is dynamic', (game) async { - final flipper = Flipper.left(position: Vector2.zero()); + final flipper = Flipper.fromSide( + side: BoardSide.left, + position: Vector2.zero(), + ); await game.ensureAdd(flipper); expect(flipper.body.bodyType, equals(BodyType.dynamic)); @@ -163,7 +85,10 @@ void main() { flameTester.test( 'ignores gravity', (game) async { - final flipper = Flipper.left(position: Vector2.zero()); + final flipper = Flipper.fromSide( + side: BoardSide.left, + position: Vector2.zero(), + ); await game.ensureAdd(flipper); expect(flipper.body.gravityScale, isZero); @@ -173,7 +98,10 @@ void main() { flameTester.test( 'has greater mass than Ball', (game) async { - final flipper = Flipper.left(position: Vector2.zero()); + final flipper = Flipper.fromSide( + side: BoardSide.left, + position: Vector2.zero(), + ); final ball = Ball(position: Vector2.zero()); await game.ready(); @@ -191,7 +119,10 @@ void main() { flameTester.test( 'has three', (game) async { - final flipper = Flipper.left(position: Vector2.zero()); + final flipper = Flipper.fromSide( + side: BoardSide.left, + position: Vector2.zero(), + ); await game.ensureAdd(flipper); expect(flipper.body.fixtures.length, equals(3)); @@ -201,7 +132,10 @@ void main() { flameTester.test( 'has density', (game) async { - final flipper = Flipper.left(position: Vector2.zero()); + final flipper = Flipper.fromSide( + side: BoardSide.left, + position: Vector2.zero(), + ); await game.ensureAdd(flipper); final fixtures = flipper.body.fixtures; @@ -229,7 +163,10 @@ void main() { late Flipper flipper; setUp(() { - flipper = Flipper.left(position: Vector2.zero()); + flipper = Flipper.fromSide( + side: BoardSide.left, + position: Vector2.zero(), + ); }); testRawKeyDownEvents(leftKeys, (event) { @@ -293,7 +230,10 @@ void main() { late Flipper flipper; setUp(() { - flipper = Flipper.right(position: Vector2.zero()); + flipper = Flipper.fromSide( + side: BoardSide.right, + position: Vector2.zero(), + ); }); testRawKeyDownEvents(rightKeys, (event) { @@ -360,7 +300,10 @@ 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, + position: Vector2.zero(), + ); await game.ensureAdd(flipper); final flipperAnchor = FlipperAnchor(flipper: flipper); @@ -373,7 +316,10 @@ 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, + position: Vector2.zero(), + ); await game.ensureAdd(flipper); final flipperAnchor = FlipperAnchor(flipper: flipper); @@ -389,7 +335,10 @@ void main() { flameTester.test( 'limits enabled', (game) async { - final flipper = Flipper.left(position: Vector2.zero()); + final flipper = Flipper.fromSide( + side: BoardSide.left, + position: Vector2.zero(), + ); await game.ensureAdd(flipper); final flipperAnchor = FlipperAnchor(flipper: flipper); @@ -408,7 +357,10 @@ void main() { flameTester.test( 'when Flipper is left', (game) async { - final flipper = Flipper.left(position: Vector2.zero()); + final flipper = Flipper.fromSide( + side: BoardSide.left, + position: Vector2.zero(), + ); await game.ensureAdd(flipper); final flipperAnchor = FlipperAnchor(flipper: flipper); @@ -426,7 +378,10 @@ void main() { flameTester.test( 'when Flipper is right', (game) async { - final flipper = Flipper.right(position: Vector2.zero()); + final flipper = Flipper.fromSide( + side: BoardSide.right, + position: Vector2.zero(), + ); await game.ensureAdd(flipper); final flipperAnchor = FlipperAnchor(flipper: flipper); @@ -449,7 +404,10 @@ void main() { flameTester.test( 'when Flipper is left', (game) async { - final flipper = Flipper.left(position: Vector2.zero()); + final flipper = Flipper.fromSide( + side: BoardSide.left, + position: Vector2.zero(), + ); await game.ensureAdd(flipper); final flipperAnchor = FlipperAnchor(flipper: flipper); @@ -473,7 +431,10 @@ void main() { flameTester.test( 'when Flipper is right', (game) async { - final flipper = Flipper.right(position: Vector2.zero()); + final flipper = Flipper.fromSide( + side: BoardSide.right, + position: Vector2.zero(), + ); await game.ensureAdd(flipper); final flipperAnchor = FlipperAnchor(flipper: flipper); 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; + } +} From 1d30278ee4440823b2f872f5a3c31bc8dad4b435 Mon Sep 17 00:00:00 2001 From: Alejandro Santiago Date: Wed, 16 Mar 2022 11:12:15 +0000 Subject: [PATCH 2/5] feat: group Slingshots (#48) * feat: removed Wall friction * feat: included SlingShot * feat: adjusted SlingShot size * feat: included tests * refactor: fixed tests --- lib/game/components/board.dart | 11 +++++++++-- lib/game/components/sling_shot.dart | 13 +++++++------ lib/game/components/wall.dart | 2 +- test/game/components/board_test.dart | 20 ++++++++++++++++---- test/game/components/wall_test.dart | 4 ++-- 5 files changed, 35 insertions(+), 15 deletions(-) diff --git a/lib/game/components/board.dart b/lib/game/components/board.dart index 80db1711..674c3e43 100644 --- a/lib/game/components/board.dart +++ b/lib/game/components/board.dart @@ -6,7 +6,6 @@ import 'package:pinball/game/game.dart'; /// /// The bottom [Component]s are the [Flipper]s and the [Baseboard]s. /// {@endtemplate} -// TODO(alestiago): Add [SlingShot] once provided. // TODO(alestiago): Consider renaming once entire Board is defined. class BottomGroup extends Component { /// {@macro bottom_group} @@ -70,7 +69,15 @@ class _BottomGroupSide extends Component { Flipper.height, ), ); + final slingShot = SlingShot( + side: _side, + position: _position + + Vector2( + (Flipper.width) * direction, + Flipper.height + SlingShot.size.y, + ), + ); - await addAll([flipper, baseboard]); + await addAll([flipper, baseboard, slingShot]); } } diff --git a/lib/game/components/sling_shot.dart b/lib/game/components/sling_shot.dart index dacf1ee5..0791cf4f 100644 --- a/lib/game/components/sling_shot.dart +++ b/lib/game/components/sling_shot.dart @@ -33,18 +33,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 +79,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); 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/test/game/components/board_test.dart b/test/game/components/board_test.dart index eaa916c3..34a628a8 100644 --- a/test/game/components/board_test.dart +++ b/test/game/components/board_test.dart @@ -45,10 +45,10 @@ void main() { await game.ready(); await game.ensureAdd(bottomGroup); - final leftFlippers = bottomGroup.findNestedChildren( + final rightFlippers = bottomGroup.findNestedChildren( condition: (flipper) => flipper.side.isRight, ); - expect(leftFlippers.length, equals(1)); + expect(rightFlippers.length, equals(1)); }, ); @@ -59,8 +59,20 @@ void main() { await game.ready(); await game.ensureAdd(bottomGroup); - final leftFlippers = bottomGroup.findNestedChildren(); - expect(leftFlippers.length, equals(2)); + 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/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)); }, ); }); From 401244a8c0a30b0efb09398fc7da17ccbabaaf68 Mon Sep 17 00:00:00 2001 From: Alejandro Santiago Date: Wed, 16 Mar 2022 11:32:16 +0000 Subject: [PATCH 3/5] feat: defined InitialPosition mixin (#37) * feat: defined InitialPosition mixin * docs: included doc comments * refactor: cleaned test file * fix: solved ball_test loading * feat: included generics --- lib/game/components/components.dart | 1 + lib/game/components/initial_position.dart | 17 +++++ .../components/initial_position_test.dart | 66 +++++++++++++++++++ 3 files changed, 84 insertions(+) create mode 100644 lib/game/components/initial_position.dart create mode 100644 test/game/components/initial_position_test.dart diff --git a/lib/game/components/components.dart b/lib/game/components/components.dart index f733cfcd..86fa3845 100644 --- a/lib/game/components/components.dart +++ b/lib/game/components/components.dart @@ -4,6 +4,7 @@ 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/initial_position.dart b/lib/game/components/initial_position.dart new file mode 100644 index 00000000..aa4acb46 --- /dev/null +++ b/lib/game/components/initial_position.dart @@ -0,0 +1,17 @@ +import 'package:flame_forge2d/flame_forge2d.dart'; + +/// Forces a given [BodyComponent] to position their [body] to an +/// [initialPosition]. +mixin InitialPosition on BodyComponent { + /// The initial position of the [body]. + late final Vector2 initialPosition; + + @override + void onMount() { + super.onMount(); + assert( + body.position == initialPosition, + 'Body position is not equal to initial position.', + ); + } +} diff --git a/test/game/components/initial_position_test.dart b/test/game/components/initial_position_test.dart new file mode 100644 index 00000000..393d780a --- /dev/null +++ b/test/game/components/initial_position_test.dart @@ -0,0 +1,66 @@ +// 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)); + }); + + test('can only be set once', () { + final component = TestBodyComponent()..initialPosition = Vector2(1, 2); + expect( + () => component.initialPosition = Vector2(3, 4), + throwsA(isA()), + ); + }); + + flameTester.test( + 'returns normally ' + 'when the body sets the position to initial position', + (game) async { + final component = TestPositionedBodyComponent() + ..initialPosition = Vector2.zero(); + + await expectLater( + () async => game.ensureAdd(component), + returnsNormally, + ); + }, + ); + + flameTester.test( + 'throws AssertionError ' + 'when not setting initialPosition to body', + (game) async { + final component = TestBodyComponent()..initialPosition = Vector2.zero(); + await game.ensureAdd(component); + + await expectLater( + () async => game.ensureAdd(component), + throwsAssertionError, + ); + }, + ); + }); +} From 0dc8851e3b882173c836ff1f14408623b792f6de Mon Sep 17 00:00:00 2001 From: Alejandro Santiago Date: Wed, 16 Mar 2022 15:11:43 +0000 Subject: [PATCH 4/5] feat: implemented `InitialPosition` on `BodyComponent`s (#50) * feat: made Baseboard use InitialPosition * feat: made Plunger use InitialPosition * feat: made SlingShot use InitialPosition * fix: fixed plunger not initialised * feat: made RoundBumper use InitialPosition * feat: made Pathway use InitialPosition * feat: made JointAnchor use InitialPosition * feat: made BonusWord use InitialPosition * feat: changed override to onLoad * feat: made Flipper use InitialPosition * feat: made Ball use InitialPosition * feat: made InitialPosition position the body * feat: included default value * feat: removed unnecessary positioning * refactor: removed old test * refactor: removed unnecessary tests * refactor: improved assertion message Co-authored-by: Erick Co-authored-by: Erick --- lib/game/components/ball.dart | 10 +-- lib/game/components/baseboard.dart | 10 +-- lib/game/components/board.dart | 22 +++---- lib/game/components/bonus_word.dart | 11 +--- lib/game/components/flipper.dart | 46 +++++-------- lib/game/components/initial_position.dart | 26 ++++++-- lib/game/components/joint_anchor.dart | 17 ++--- lib/game/components/pathway.dart | 35 ++++------ lib/game/components/plunger.dart | 25 +++---- lib/game/components/round_bumper.dart | 13 +--- lib/game/components/sling_shot.dart | 12 +--- lib/game/pinball_game.dart | 30 +++++---- test/game/components/ball_test.dart | 26 ++------ test/game/components/baseboard_test.dart | 23 +------ test/game/components/bonus_word_test.dart | 21 ------ test/game/components/flipper_test.dart | 35 +--------- .../components/initial_position_test.dart | 48 ++++++-------- test/game/components/joint_anchor_test.dart | 19 +----- test/game/components/pathway_test.dart | 65 +------------------ test/game/components/plunger_test.dart | 24 ------- test/game/components/round_bumper_test.dart | 22 ------- test/game/components/sling_shot_test.dart | 25 ------- 22 files changed, 135 insertions(+), 430 deletions(-) diff --git a/lib/game/components/ball.dart b/lib/game/components/ball.dart index 738ceec6..d90cca78 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); @@ -45,7 +40,6 @@ class Ball extends BodyComponent { final bodyDef = BodyDef() ..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 60e51593..77f07024 100644 --- a/lib/game/components/baseboard.dart +++ b/lib/game/components/baseboard.dart @@ -6,13 +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 BoardSide side, - required Vector2 position, - }) : _side = side, - _position = position; + }) : _side = side; /// The width of the [Baseboard]. static const width = 10.0; @@ -20,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; @@ -63,7 +58,6 @@ class Baseboard extends BodyComponent { const angle = math.pi / 7; final bodyDef = BodyDef() - ..position = _position ..type = BodyType.static ..angle = _side.isLeft ? -angle : angle; diff --git a/lib/game/components/board.dart b/lib/game/components/board.dart index 674c3e43..b9ea85ea 100644 --- a/lib/game/components/board.dart +++ b/lib/game/components/board.dart @@ -59,24 +59,20 @@ class _BottomGroupSide extends Component { final flipper = Flipper.fromSide( side: _side, - position: _position, - ); - final baseboard = Baseboard( - side: _side, - position: _position + + )..initialPosition = _position; + final baseboard = Baseboard(side: _side) + ..initialPosition = _position + Vector2( (Flipper.width * direction) - direction, Flipper.height, - ), - ); + ); final slingShot = SlingShot( side: _side, - position: _position + - Vector2( - (Flipper.width) * direction, - Flipper.height + SlingShot.size.y, - ), - ); + )..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..e1078e30 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; @@ -134,7 +130,6 @@ class BonusLetter extends BodyComponent final bodyDef = BodyDef() ..userData = this - ..position = _position ..type = BodyType.static; return world.createBody(bodyDef)..createFixture(fixtureDef); diff --git a/lib/game/components/flipper.dart b/lib/game/components/flipper.dart index ff4754ea..03875d36 100644 --- a/lib/game/components/flipper.dart +++ b/lib/game/components/flipper.dart @@ -13,19 +13,15 @@ import 'package:pinball/game/game.dart'; /// /// [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; + }) : _keys = keys; - Flipper._left({ - required Vector2 position, - }) : this._( - position: position, + Flipper._left() + : this._( side: BoardSide.left, keys: [ LogicalKeyboardKey.arrowLeft, @@ -33,10 +29,8 @@ class Flipper extends BodyComponent with KeyboardHandler { ], ); - Flipper._right({ - required Vector2 position, - }) : this._( - position: position, + Flipper._right() + : this._( side: BoardSide.right, keys: [ LogicalKeyboardKey.arrowRight, @@ -50,13 +44,12 @@ class Flipper extends BodyComponent with KeyboardHandler { /// horizontally, also have different [LogicalKeyboardKey]s that control them. factory Flipper.fromSide({ required BoardSide side, - required Vector2 position, }) { switch (side) { case BoardSide.left: - return Flipper._left(position: position); + return Flipper._left(); case BoardSide.right: - return Flipper._right(position: position); + return Flipper._right(); } } @@ -82,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. @@ -200,9 +190,7 @@ class Flipper extends BodyComponent with KeyboardHandler { Body createBody() { final bodyDef = BodyDef() ..gravityScale = 0 - ..type = BodyType.dynamic - ..position = _position; - + ..type = BodyType.dynamic; final body = world.createBody(bodyDef); _createFixtureDefs().forEach(body.createFixture); @@ -238,14 +226,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 index aa4acb46..dc6958d9 100644 --- a/lib/game/components/initial_position.dart +++ b/lib/game/components/initial_position.dart @@ -2,16 +2,28 @@ 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 { - /// The initial position of the [body]. - late final Vector2 initialPosition; + final Vector2 _initialPosition = Vector2.zero(); - @override - void onMount() { - super.onMount(); + set initialPosition(Vector2 value) { assert( - body.position == initialPosition, - 'Body position is not equal to initial position.', + !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(); + body.position.setFrom(initialPosition); } } diff --git a/lib/game/components/joint_anchor.dart b/lib/game/components/joint_anchor.dart index 05e62b73..36ad3ab0 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,12 @@ 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; - - return world.createBody(bodyDef); + return world.createBody(BodyDef()); } } diff --git a/lib/game/components/pathway.dart b/lib/game/components/pathway.dart index d41ce3da..bd8caf5d 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()..type = BodyType.static; 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..68ccbeb0 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; @@ -30,7 +26,6 @@ class Plunger extends BodyComponent with KeyboardHandler { final bodyDef = BodyDef() ..userData = this - ..position = _position ..type = BodyType.dynamic ..gravityScale = 0; @@ -45,9 +40,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 +97,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..af8285af 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()..type = BodyType.static; return world.createBody(bodyDef)..createFixture(fixtureDef); } diff --git a/lib/game/components/sling_shot.dart b/lib/game/components/sling_shot.dart index 0791cf4f..0002ed0c 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, @@ -88,8 +83,7 @@ class SlingShot extends BodyComponent { @override Body createBody() { - final bodyDef = BodyDef()..position = _position; - final body = world.createBody(bodyDef); + final body = world.createBody(BodyDef()); _createFixtureDefs().forEach(body.createFixture); return body; diff --git a/lib/game/pinball_game.dart b/lib/game/pinball_game.dart index 112f0522..61ef17e0 100644 --- a/lib/game/pinball_game.dart +++ b/lib/game/pinball_game.dart @@ -78,7 +78,11 @@ class PinballGame extends Forge2DGame } 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() { @@ -93,19 +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, + ); + plunger.initialPosition = screenToWorld( + Vector2( + camera.viewport.effectiveSize.x / 1.035, + camera.viewport.effectiveSize.y - plunger.compressionDistance, ), ); + + await add(plunger); } } @@ -114,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 8f1874b1..75cc62cc 100644 --- a/test/game/components/baseboard_test.dart +++ b/test/game/components/baseboard_test.dart @@ -15,13 +15,12 @@ void main() { (game) async { await game.ready(); final leftBaseboard = Baseboard( - position: Vector2.zero(), side: BoardSide.left, ); final rightBaseboard = Baseboard( - position: Vector2.zero(), side: BoardSide.right, ); + await game.ensureAddAll([leftBaseboard, rightBaseboard]); expect(game.contains(leftBaseboard), isTrue); @@ -30,28 +29,13 @@ void main() { ); group('body', () { - flameTester.test( - 'positions correctly', - (game) async { - final position = Vector2.all(10); - final baseboard = Baseboard( - position: position, - side: BoardSide.left, - ); - await game.ensureAdd(baseboard); - game.contains(baseboard); - - expect(baseboard.body.position, position); - }, - ); - flameTester.test( 'is static', (game) async { final baseboard = Baseboard( - position: Vector2.zero(), side: BoardSide.left, ); + await game.ensureAdd(baseboard); expect(baseboard.body.bodyType, equals(BodyType.static)); @@ -62,11 +46,9 @@ void main() { 'is at an angle', (game) async { final leftBaseboard = Baseboard( - position: Vector2.zero(), side: BoardSide.left, ); final rightBaseboard = Baseboard( - position: Vector2.zero(), side: BoardSide.right, ); await game.ensureAddAll([leftBaseboard, rightBaseboard]); @@ -82,7 +64,6 @@ void main() { 'has three', (game) async { final baseboard = Baseboard( - position: Vector2.zero(), side: BoardSide.left, ); await game.ensureAdd(baseboard); 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 eae238c9..e6e9ba23 100644 --- a/test/game/components/flipper_test.dart +++ b/test/game/components/flipper_test.dart @@ -22,11 +22,9 @@ void main() { (game) async { final leftFlipper = Flipper.fromSide( side: BoardSide.left, - position: Vector2.zero(), ); final rightFlipper = Flipper.fromSide( side: BoardSide.right, - position: Vector2.zero(), ); await game.ready(); await game.ensureAddAll([leftFlipper, rightFlipper]); @@ -40,41 +38,23 @@ void main() { test('sets BoardSide', () { final leftFlipper = Flipper.fromSide( side: BoardSide.left, - position: Vector2.zero(), ); expect(leftFlipper.side, equals(leftFlipper.side)); final rightFlipper = Flipper.fromSide( side: BoardSide.right, - position: Vector2.zero(), ); expect(rightFlipper.side, equals(rightFlipper.side)); }); }); group('body', () { - flameTester.test( - 'positions correctly', - (game) async { - final position = Vector2.all(10); - final flipper = Flipper.fromSide( - side: BoardSide.left, - position: position, - ); - await game.ensureAdd(flipper); - game.contains(flipper); - - expect(flipper.body.position, position); - }, - ); - flameTester.test( 'is dynamic', (game) async { final flipper = Flipper.fromSide( side: BoardSide.left, - position: Vector2.zero(), ); await game.ensureAdd(flipper); @@ -87,7 +67,6 @@ void main() { (game) async { final flipper = Flipper.fromSide( side: BoardSide.left, - position: Vector2.zero(), ); await game.ensureAdd(flipper); @@ -100,9 +79,8 @@ void main() { (game) async { final flipper = Flipper.fromSide( side: BoardSide.left, - position: Vector2.zero(), ); - final ball = Ball(position: Vector2.zero()); + final ball = Ball(); await game.ready(); await game.ensureAddAll([flipper, ball]); @@ -121,7 +99,6 @@ void main() { (game) async { final flipper = Flipper.fromSide( side: BoardSide.left, - position: Vector2.zero(), ); await game.ensureAdd(flipper); @@ -134,7 +111,6 @@ void main() { (game) async { final flipper = Flipper.fromSide( side: BoardSide.left, - position: Vector2.zero(), ); await game.ensureAdd(flipper); @@ -165,7 +141,6 @@ void main() { setUp(() { flipper = Flipper.fromSide( side: BoardSide.left, - position: Vector2.zero(), ); }); @@ -232,7 +207,6 @@ void main() { setUp(() { flipper = Flipper.fromSide( side: BoardSide.right, - position: Vector2.zero(), ); }); @@ -302,7 +276,6 @@ void main() { (game) async { final flipper = Flipper.fromSide( side: BoardSide.left, - position: Vector2.zero(), ); await game.ensureAdd(flipper); @@ -318,7 +291,6 @@ void main() { (game) async { final flipper = Flipper.fromSide( side: BoardSide.right, - position: Vector2.zero(), ); await game.ensureAdd(flipper); @@ -337,7 +309,6 @@ void main() { (game) async { final flipper = Flipper.fromSide( side: BoardSide.left, - position: Vector2.zero(), ); await game.ensureAdd(flipper); @@ -359,7 +330,6 @@ void main() { (game) async { final flipper = Flipper.fromSide( side: BoardSide.left, - position: Vector2.zero(), ); await game.ensureAdd(flipper); @@ -380,7 +350,6 @@ void main() { (game) async { final flipper = Flipper.fromSide( side: BoardSide.right, - position: Vector2.zero(), ); await game.ensureAdd(flipper); @@ -406,7 +375,6 @@ void main() { (game) async { final flipper = Flipper.fromSide( side: BoardSide.left, - position: Vector2.zero(), ); await game.ensureAdd(flipper); @@ -433,7 +401,6 @@ void main() { (game) async { final flipper = Flipper.fromSide( side: BoardSide.right, - position: Vector2.zero(), ); await game.ensureAdd(flipper); diff --git a/test/game/components/initial_position_test.dart b/test/game/components/initial_position_test.dart index 393d780a..814b1e86 100644 --- a/test/game/components/initial_position_test.dart +++ b/test/game/components/initial_position_test.dart @@ -12,13 +12,6 @@ class TestBodyComponent extends BodyComponent with InitialPosition { } } -class TestPositionedBodyComponent extends BodyComponent with InitialPosition { - @override - Body createBody() { - return world.createBody(BodyDef()..position = initialPosition); - } -} - void main() { final flameTester = FlameTester(Forge2DGame.new); group('InitialPosition', () { @@ -27,37 +20,36 @@ void main() { expect(component.initialPosition, Vector2(1, 2)); }); - test('can only be set once', () { - final component = TestBodyComponent()..initialPosition = Vector2(1, 2); - expect( - () => component.initialPosition = Vector2(3, 4), - throwsA(isA()), - ); - }); - flameTester.test( - 'returns normally ' - 'when the body sets the position to initial position', + 'positions correctly', (game) async { - final component = TestPositionedBodyComponent() - ..initialPosition = Vector2.zero(); + final position = Vector2.all(10); + final component = TestBodyComponent()..initialPosition = position; + await game.ensureAdd(component); + expect(component.body.position, equals(position)); + }, + ); - await expectLater( - () async => game.ensureAdd(component), - returnsNormally, - ); + 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( - 'throws AssertionError ' - 'when not setting initialPosition to body', + 'setting throws AssertionError ' + 'when component has loaded', (game) async { - final component = TestBodyComponent()..initialPosition = Vector2.zero(); + final component = TestBodyComponent(); await game.ensureAdd(component); - await expectLater( - () async => 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); From dc3de740d3e10d5f39629650d44c1954cbe4c788 Mon Sep 17 00:00:00 2001 From: Alejandro Santiago Date: Wed, 16 Mar 2022 17:37:35 +0000 Subject: [PATCH 5/5] fix: used `initialPosition` in `BodyDef` (#52) * fix: using BodyDef * feat: included test * refactor: removed TODO --- lib/game/components/ball.dart | 1 + lib/game/components/baseboard.dart | 2 +- lib/game/components/bonus_word.dart | 1 + lib/game/components/flipper.dart | 1 + lib/game/components/initial_position.dart | 7 +++++- lib/game/components/joint_anchor.dart | 3 ++- lib/game/components/pathway.dart | 2 +- lib/game/components/plunger.dart | 1 + lib/game/components/round_bumper.dart | 2 +- lib/game/components/sling_shot.dart | 3 ++- .../components/initial_position_test.dart | 25 ++++++++++++++++++- 11 files changed, 41 insertions(+), 7 deletions(-) diff --git a/lib/game/components/ball.dart b/lib/game/components/ball.dart index d90cca78..36ed8929 100644 --- a/lib/game/components/ball.dart +++ b/lib/game/components/ball.dart @@ -39,6 +39,7 @@ class Ball extends BodyComponent with InitialPosition { final fixtureDef = FixtureDef(shape)..density = 1; final bodyDef = BodyDef() + ..position = initialPosition ..userData = this ..type = BodyType.dynamic; diff --git a/lib/game/components/baseboard.dart b/lib/game/components/baseboard.dart index 77f07024..62c9210c 100644 --- a/lib/game/components/baseboard.dart +++ b/lib/game/components/baseboard.dart @@ -58,7 +58,7 @@ class Baseboard extends BodyComponent with InitialPosition { const angle = math.pi / 7; final bodyDef = BodyDef() - ..type = BodyType.static + ..position = initialPosition ..angle = _side.isLeft ? -angle : angle; final body = world.createBody(bodyDef); diff --git a/lib/game/components/bonus_word.dart b/lib/game/components/bonus_word.dart index e1078e30..03f64a11 100644 --- a/lib/game/components/bonus_word.dart +++ b/lib/game/components/bonus_word.dart @@ -129,6 +129,7 @@ class BonusLetter extends BodyComponent final fixtureDef = FixtureDef(shape)..isSensor = true; final bodyDef = BodyDef() + ..position = initialPosition ..userData = this ..type = BodyType.static; diff --git a/lib/game/components/flipper.dart b/lib/game/components/flipper.dart index 03875d36..8b029b1e 100644 --- a/lib/game/components/flipper.dart +++ b/lib/game/components/flipper.dart @@ -189,6 +189,7 @@ class Flipper extends BodyComponent with KeyboardHandler, InitialPosition { @override Body createBody() { final bodyDef = BodyDef() + ..position = initialPosition ..gravityScale = 0 ..type = BodyType.dynamic; final body = world.createBody(bodyDef); diff --git a/lib/game/components/initial_position.dart b/lib/game/components/initial_position.dart index dc6958d9..d79f8d64 100644 --- a/lib/game/components/initial_position.dart +++ b/lib/game/components/initial_position.dart @@ -24,6 +24,11 @@ mixin InitialPosition on BodyComponent { @override Future onLoad() async { await super.onLoad(); - body.position.setFrom(initialPosition); + // 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 36ad3ab0..e945bd72 100644 --- a/lib/game/components/joint_anchor.dart +++ b/lib/game/components/joint_anchor.dart @@ -22,6 +22,7 @@ class JointAnchor extends BodyComponent with InitialPosition { @override Body createBody() { - return world.createBody(BodyDef()); + final bodyDef = BodyDef()..position = initialPosition; + return world.createBody(bodyDef); } } diff --git a/lib/game/components/pathway.dart b/lib/game/components/pathway.dart index bd8caf5d..e4a6f1f5 100644 --- a/lib/game/components/pathway.dart +++ b/lib/game/components/pathway.dart @@ -148,7 +148,7 @@ class Pathway extends BodyComponent with InitialPosition { @override Body createBody() { - final bodyDef = BodyDef()..type = BodyType.static; + 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 68ccbeb0..6f319bd3 100644 --- a/lib/game/components/plunger.dart +++ b/lib/game/components/plunger.dart @@ -25,6 +25,7 @@ class Plunger extends BodyComponent with KeyboardHandler, InitialPosition { final fixtureDef = FixtureDef(shape)..density = 5; final bodyDef = BodyDef() + ..position = initialPosition ..userData = this ..type = BodyType.dynamic ..gravityScale = 0; diff --git a/lib/game/components/round_bumper.dart b/lib/game/components/round_bumper.dart index af8285af..2f43a35b 100644 --- a/lib/game/components/round_bumper.dart +++ b/lib/game/components/round_bumper.dart @@ -27,7 +27,7 @@ class RoundBumper extends BodyComponent with ScorePoints, InitialPosition { final fixtureDef = FixtureDef(shape)..restitution = 1; - final bodyDef = BodyDef()..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 0002ed0c..61d28c29 100644 --- a/lib/game/components/sling_shot.dart +++ b/lib/game/components/sling_shot.dart @@ -83,7 +83,8 @@ class SlingShot extends BodyComponent with InitialPosition { @override Body createBody() { - final body = world.createBody(BodyDef()); + final bodyDef = BodyDef()..position = initialPosition; + final body = world.createBody(bodyDef); _createFixtureDefs().forEach(body.createFixture); return body; diff --git a/test/game/components/initial_position_test.dart b/test/game/components/initial_position_test.dart index 814b1e86..ece083cc 100644 --- a/test/game/components/initial_position_test.dart +++ b/test/game/components/initial_position_test.dart @@ -12,6 +12,13 @@ class TestBodyComponent extends BodyComponent with InitialPosition { } } +class TestPositionedBodyComponent extends BodyComponent with InitialPosition { + @override + Body createBody() { + return world.createBody(BodyDef()..position = initialPosition); + } +} + void main() { final flameTester = FlameTester(Forge2DGame.new); group('InitialPosition', () { @@ -20,11 +27,27 @@ void main() { 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 = TestBodyComponent()..initialPosition = position; + final component = TestPositionedBodyComponent() + ..initialPosition = position; await game.ensureAdd(component); expect(component.body.position, equals(position)); },