diff --git a/lib/game/components/flutter_forest.dart b/lib/game/components/flutter_forest.dart index 2d7bdf33..966b0a6b 100644 --- a/lib/game/components/flutter_forest.dart +++ b/lib/game/components/flutter_forest.dart @@ -68,7 +68,7 @@ class _FlutterForestController extends ComponentController void onNewState(GameState state) { super.onNewState(state); - component.add( + gameRef.add( ControlledBall.bonus(theme: gameRef.theme) ..initialPosition = Vector2(17.2, 52.7), ); diff --git a/lib/game/game_assets.dart b/lib/game/game_assets.dart index 5592d667..5c977d65 100644 --- a/lib/game/game_assets.dart +++ b/lib/game/game_assets.dart @@ -27,6 +27,8 @@ extension PinballGameAssetsX on PinballGame { images.load(components.Assets.images.dashBumper.b.inactive.keyName), images.load(components.Assets.images.dashBumper.main.active.keyName), images.load(components.Assets.images.dashBumper.main.inactive.keyName), + images.load(components.Assets.images.boundary.bottom.keyName), + images.load(components.Assets.images.boundary.outer.keyName), images.load(components.Assets.images.spaceshipRamp.spaceshipRamp.keyName), images.load( components.Assets.images.spaceshipRamp.spaceshipRailingBg.keyName, diff --git a/lib/game/pinball_game.dart b/lib/game/pinball_game.dart index 35484bd5..6f4a0c81 100644 --- a/lib/game/pinball_game.dart +++ b/lib/game/pinball_game.dart @@ -33,6 +33,7 @@ class PinballGame extends Forge2DGame await _addGameBoundaries(); unawaited(add(Board())); + unawaited(addFromBlueprint(Boundaries())); unawaited(_addPlunger()); unawaited(_addBonusWord()); unawaited(_addRamps()); diff --git a/packages/pinball_components/assets/images/boundary/bottom.png b/packages/pinball_components/assets/images/boundary/bottom.png new file mode 100644 index 00000000..7caf3e2f Binary files /dev/null and b/packages/pinball_components/assets/images/boundary/bottom.png differ diff --git a/packages/pinball_components/assets/images/boundary/outer.png b/packages/pinball_components/assets/images/boundary/outer.png new file mode 100644 index 00000000..c0b81e96 Binary files /dev/null and b/packages/pinball_components/assets/images/boundary/outer.png differ diff --git a/packages/pinball_components/lib/gen/assets.gen.dart b/packages/pinball_components/lib/gen/assets.gen.dart index 42b164cc..99dd9668 100644 --- a/packages/pinball_components/lib/gen/assets.gen.dart +++ b/packages/pinball_components/lib/gen/assets.gen.dart @@ -14,6 +14,7 @@ class $AssetsImagesGen { AssetGenImage get ball => const AssetGenImage('assets/images/ball.png'); $AssetsImagesBaseboardGen get baseboard => const $AssetsImagesBaseboardGen(); + $AssetsImagesBoundaryGen get boundary => const $AssetsImagesBoundaryGen(); $AssetsImagesChromeDinoGen get chromeDino => const $AssetsImagesChromeDinoGen(); $AssetsImagesDashBumperGen get dashBumper => @@ -52,6 +53,18 @@ class $AssetsImagesBaseboardGen { const AssetGenImage('assets/images/baseboard/right.png'); } +class $AssetsImagesBoundaryGen { + const $AssetsImagesBoundaryGen(); + + /// File path: assets/images/boundary/bottom.png + AssetGenImage get bottom => + const AssetGenImage('assets/images/boundary/bottom.png'); + + /// File path: assets/images/boundary/outer.png + AssetGenImage get outer => + const AssetGenImage('assets/images/boundary/outer.png'); +} + class $AssetsImagesChromeDinoGen { const $AssetsImagesChromeDinoGen(); diff --git a/packages/pinball_components/lib/src/components/boundaries.dart b/packages/pinball_components/lib/src/components/boundaries.dart new file mode 100644 index 00000000..79b3f909 --- /dev/null +++ b/packages/pinball_components/lib/src/components/boundaries.dart @@ -0,0 +1,156 @@ +// ignore_for_file: avoid_renaming_method_parameters + +import 'package:flame/components.dart'; +import 'package:flame_forge2d/flame_forge2d.dart'; +import 'package:pinball_components/pinball_components.dart'; + +/// {@template boundaries} +/// A [Blueprint] which creates the [_BottomBoundary] and [_OuterBoundary]. +///{@endtemplate boundaries} +class Boundaries extends Forge2DBlueprint { + @override + void build(_) { + final bottomBoundary = _BottomBoundary(); + final outerBoundary = _OuterBoundary(); + + addAll([bottomBoundary, outerBoundary]); + } +} + +/// {@template bottom_boundary} +/// Curved boundary at the bottom of the board where the [Ball] exits the field +/// of play. +/// {@endtemplate bottom_boundary} +class _BottomBoundary extends BodyComponent with InitialPosition { + /// {@macro bottom_boundary} + _BottomBoundary() : super(priority: 2); + + List _createFixtureDefs() { + final fixturesDefs = []; + + final bottomLeftCurve = BezierCurveShape( + controlPoints: [ + Vector2(-43.6, -44.4), + Vector2(-31, -43.4), + Vector2(-18.7, -52.1), + ], + ); + final bottomLeftCurveFixtureDef = FixtureDef(bottomLeftCurve); + fixturesDefs.add(bottomLeftCurveFixtureDef); + + final bottomRightCurve = BezierCurveShape( + controlPoints: [ + Vector2(31.8, -44.1), + Vector2(21.95, -47), + Vector2(12.3, -51.4), + ], + ); + final bottomRightCurveFixtureDef = FixtureDef(bottomRightCurve); + fixturesDefs.add(bottomRightCurveFixtureDef); + + return fixturesDefs; + } + + @override + Body createBody() { + final bodyDef = BodyDef()..position = initialPosition; + final body = world.createBody(bodyDef); + _createFixtureDefs().forEach(body.createFixture); + + return body; + } + + @override + Future onLoad() async { + await super.onLoad(); + await _loadSprite(); + renderBody = false; + } + + Future _loadSprite() async { + final sprite = await gameRef.loadSprite( + Assets.images.boundary.bottom.keyName, + ); + + await add( + SpriteComponent( + sprite: sprite, + size: sprite.originalSize / 10, + anchor: Anchor.center, + position: Vector2(-5.4, 57.4), + ), + ); + } +} + +/// {@template outer_boundary} +/// Boundary enclosing the top and left side of the board. The right side of the +/// board is closed by the barrier the [LaunchRamp] creates. +/// {@endtemplate outer_boundary} +class _OuterBoundary extends BodyComponent with InitialPosition { + /// {@macro outer_boundary} + _OuterBoundary() : super(priority: -1); + + List _createFixtureDefs() { + final fixturesDefs = []; + + final topWall = EdgeShape() + ..set( + Vector2(3.6, 70.2), + Vector2(-14.1, 70.2), + ); + final topWallFixtureDef = FixtureDef(topWall); + fixturesDefs.add(topWallFixtureDef); + + final topLeftCurve = BezierCurveShape( + controlPoints: [ + Vector2(-32.3, 57.2), + Vector2(-31.5, 69.9), + Vector2(-14.1, 70.2), + ], + ); + final topLeftCurveFixtureDef = FixtureDef(topLeftCurve); + fixturesDefs.add(topLeftCurveFixtureDef); + + final leftWall = EdgeShape() + ..set( + Vector2(-32.3, 57.2), + Vector2(-44.1, -44.4), + ); + final leftWallFixtureDef = FixtureDef(leftWall); + fixturesDefs.add(leftWallFixtureDef); + + return fixturesDefs; + } + + @override + Body createBody() { + final bodyDef = BodyDef()..position = initialPosition; + final body = world.createBody(bodyDef); + _createFixtureDefs().forEach(body.createFixture); + + return body; + } + + @override + Future onLoad() async { + await super.onLoad(); + await _loadSprite(); + renderBody = false; + } + + Future _loadSprite() async { + final sprite = await gameRef.loadSprite( + Assets.images.boundary.outer.keyName, + ); + + await add( + SpriteComponent( + sprite: sprite, + size: sprite.originalSize / 10, + anchor: Anchor.center, + position: Vector2(-0.2, -1.4), + ), + ); + } +} diff --git a/packages/pinball_components/lib/src/components/components.dart b/packages/pinball_components/lib/src/components/components.dart index 4fb633a5..bf578ea7 100644 --- a/packages/pinball_components/lib/src/components/components.dart +++ b/packages/pinball_components/lib/src/components/components.dart @@ -2,6 +2,7 @@ export 'ball.dart'; export 'baseboard.dart'; export 'board_dimensions.dart'; export 'board_side.dart'; +export 'boundaries.dart'; export 'chrome_dino.dart'; export 'dash_nest_bumper.dart'; export 'dino_walls.dart'; diff --git a/packages/pinball_components/pubspec.yaml b/packages/pinball_components/pubspec.yaml index 13e850cd..f513b054 100644 --- a/packages/pinball_components/pubspec.yaml +++ b/packages/pinball_components/pubspec.yaml @@ -27,6 +27,7 @@ flutter: assets: - assets/images/ - assets/images/baseboard/ + - assets/images/boundary/ - assets/images/dino/ - assets/images/flipper/ - assets/images/launch_ramp/ diff --git a/packages/pinball_components/test/src/components/boundaries_test.dart b/packages/pinball_components/test/src/components/boundaries_test.dart new file mode 100644 index 00000000..2c6fe1da --- /dev/null +++ b/packages/pinball_components/test/src/components/boundaries_test.dart @@ -0,0 +1,31 @@ +// 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_components/pinball_components.dart'; + +import '../../helpers/helpers.dart'; + +void main() { + group('Boundaries', () { + final tester = FlameTester(TestGame.new); + + tester.testGameWidget( + 'render correctly', + setUp: (game, tester) async { + await game.addFromBlueprint(Boundaries()); + await game.ready(); + game.camera.followVector2(Vector2.zero()); + game.camera.zoom = 3.9; + }, + // TODO(allisonryan0002): enable test when workflows are fixed. + // verify: (game, tester) async { + // await expectLater( + // find.byGame(), + // matchesGoldenFile('golden/boundaries.png'), + // ); + // }, + ); + }); +} diff --git a/packages/pinball_components/test/src/components/golden/boundaries.png b/packages/pinball_components/test/src/components/golden/boundaries.png new file mode 100644 index 00000000..d1ef9ce1 Binary files /dev/null and b/packages/pinball_components/test/src/components/golden/boundaries.png differ