From f6f171b7c7f660cbd8f4c12a61c2752ebef3084a Mon Sep 17 00:00:00 2001 From: Allison Ryan <77211884+allisonryan0002@users.noreply.github.com> Date: Fri, 11 Mar 2022 14:26:59 -0600 Subject: [PATCH] feat: add baseboards (#34) * feat: add baseboards * docs: baseboard * fix: test extension * chore: simplify rectangle * refactor: PR suggestions --- lib/game/components/baseboard.dart | 91 ++++++++++++++++++++++++ lib/game/components/components.dart | 1 + lib/game/pinball_game.dart | 26 +++++++ test/game/components/baseboard_test.dart | 76 ++++++++++++++++++++ test/game/pinball_game_test.dart | 9 +++ 5 files changed, 203 insertions(+) create mode 100644 lib/game/components/baseboard.dart create mode 100644 test/game/components/baseboard_test.dart diff --git a/lib/game/components/baseboard.dart b/lib/game/components/baseboard.dart new file mode 100644 index 00000000..9153d4f3 --- /dev/null +++ b/lib/game/components/baseboard.dart @@ -0,0 +1,91 @@ +import 'dart:math' as math; + +import 'package:flame_forge2d/flame_forge2d.dart'; +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 { + /// {@macro baseboard} + Baseboard._({ + required Vector2 position, + 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, + ); + + /// The width of the [Baseboard]. + static const width = 10.0; + + /// 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; + + List _createFixtureDefs() { + final fixtures = []; + + final circleShape1 = CircleShape()..radius = Baseboard.height / 2; + circleShape1.position.setValues( + -(Baseboard.width / 2) + circleShape1.radius, + 0, + ); + final circle1FixtureDef = FixtureDef(circleShape1); + fixtures.add(circle1FixtureDef); + + final circleShape2 = CircleShape()..radius = Baseboard.height / 2; + circleShape2.position.setValues( + (Baseboard.width / 2) - circleShape2.radius, + 0, + ); + final circle2FixtureDef = FixtureDef(circleShape2); + fixtures.add(circle2FixtureDef); + + final rectangle = PolygonShape() + ..setAsBoxXY( + (Baseboard.width - Baseboard.height) / 2, + Baseboard.height / 2, + ); + final rectangleFixtureDef = FixtureDef(rectangle); + fixtures.add(rectangleFixtureDef); + + return fixtures; + } + + @override + Body createBody() { + // TODO(allisonryan0002): share sweeping angle with flipper when components + // are grouped. + const angle = math.pi / 7; + + final bodyDef = BodyDef() + ..position = _position + ..type = BodyType.static + ..angle = _side.isLeft ? -angle : angle; + + final body = world.createBody(bodyDef); + _createFixtureDefs().forEach(body.createFixture); + + return body; + } +} diff --git a/lib/game/components/components.dart b/lib/game/components/components.dart index 89f60343..bcf9a92d 100644 --- a/lib/game/components/components.dart +++ b/lib/game/components/components.dart @@ -1,5 +1,6 @@ export 'anchor.dart'; export 'ball.dart'; +export 'baseboard.dart'; export 'board_side.dart'; export 'flipper.dart'; export 'pathway.dart'; diff --git a/lib/game/pinball_game.dart b/lib/game/pinball_game.dart index fbb68e63..c6b23dae 100644 --- a/lib/game/pinball_game.dart +++ b/lib/game/pinball_game.dart @@ -63,6 +63,7 @@ class PinballGame extends Forge2DGame ), ), ); + unawaited(_addBaseboards()); } void spawnBall() { @@ -103,6 +104,31 @@ 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 new file mode 100644 index 00000000..51002cef --- /dev/null +++ b/test/game/components/baseboard_test.dart @@ -0,0 +1,76 @@ +// 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() { + group('Baseboard', () { + TestWidgetsFlutterBinding.ensureInitialized(); + final flameTester = FlameTester(PinballGameTest.create); + + flameTester.test( + 'loads correctly', + (game) async { + await game.ready(); + final leftBaseboard = Baseboard.left(position: Vector2.zero()); + final rightBaseboard = Baseboard.right(position: Vector2.zero()); + await game.ensureAddAll([leftBaseboard, rightBaseboard]); + + expect(game.contains(leftBaseboard), isTrue); + expect(game.contains(rightBaseboard), isTrue); + }, + ); + + 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()); + await game.ensureAdd(baseboard); + + expect(baseboard.body.bodyType, equals(BodyType.static)); + }, + ); + + flameTester.test( + 'is at an angle', + (game) async { + final leftBaseboard = Baseboard.left(position: Vector2.zero()); + final rightBaseboard = Baseboard.right(position: Vector2.zero()); + await game.ensureAddAll([leftBaseboard, rightBaseboard]); + + expect(leftBaseboard.body.angle, isNegative); + expect(rightBaseboard.body.angle, isPositive); + }, + ); + }); + + group('fixtures', () { + flameTester.test( + 'has three', + (game) async { + final baseboard = Baseboard.left(position: Vector2.zero()); + await game.ensureAdd(baseboard); + + expect(baseboard.body.fixtures.length, equals(3)); + }, + ); + }); + }); +} diff --git a/test/game/pinball_game_test.dart b/test/game/pinball_game_test.dart index faa55d11..a992a471 100644 --- a/test/game/pinball_game_test.dart +++ b/test/game/pinball_game_test.dart @@ -74,6 +74,15 @@ void main() { returnsNormally, ); }); + + flameTester.test( + 'has two Baseboards', + (game) async { + await game.ready(); + final baseboards = game.children.whereType().toList(); + expect(baseboards.length, 2); + }, + ); }); debugModeFlameTester.test('adds a ball on tap up', (game) async {