diff --git a/lib/game/components/board.dart b/lib/game/components/board.dart index 49595f10..a312daee 100644 --- a/lib/game/components/board.dart +++ b/lib/game/components/board.dart @@ -83,8 +83,8 @@ class _BottomGroupSide extends Component { final kicker = Kicker( side: _side, )..initialPosition = Vector2( - (22.0 * direction) + centerXAdjustment, - -26, + (22.4 * direction) + centerXAdjustment, + -25, ); await addAll([flipper, baseboard, kicker]); diff --git a/packages/pinball_components/assets/images/kicker/left.png b/packages/pinball_components/assets/images/kicker/left.png new file mode 100644 index 00000000..42bd5030 Binary files /dev/null and b/packages/pinball_components/assets/images/kicker/left.png differ diff --git a/packages/pinball_components/assets/images/kicker/right.png b/packages/pinball_components/assets/images/kicker/right.png new file mode 100644 index 00000000..0a746f3c Binary files /dev/null and b/packages/pinball_components/assets/images/kicker/right.png differ diff --git a/packages/pinball_components/lib/gen/assets.gen.dart b/packages/pinball_components/lib/gen/assets.gen.dart index 48ddbaff..154930bc 100644 --- a/packages/pinball_components/lib/gen/assets.gen.dart +++ b/packages/pinball_components/lib/gen/assets.gen.dart @@ -3,12 +3,16 @@ /// FlutterGen /// ***************************************************** +// ignore_for_file: directives_ordering,unnecessary_import + import 'package:flutter/widgets.dart'; class $AssetsImagesGen { const $AssetsImagesGen(); + /// File path: assets/images/ball.png AssetGenImage get ball => const AssetGenImage('assets/images/ball.png'); + $AssetsImagesBaseboardGen get baseboard => const $AssetsImagesBaseboardGen(); $AssetsImagesBoundaryGen get boundary => const $AssetsImagesBoundaryGen(); $AssetsImagesChromeDinoGen get chromeDino => @@ -17,8 +21,12 @@ class $AssetsImagesGen { const $AssetsImagesDashBumperGen(); $AssetsImagesDinoGen get dino => const $AssetsImagesDinoGen(); $AssetsImagesFlipperGen get flipper => const $AssetsImagesFlipperGen(); + + /// File path: assets/images/flutter_sign_post.png AssetGenImage get flutterSignPost => const AssetGenImage('assets/images/flutter_sign_post.png'); + + $AssetsImagesKickerGen get kicker => const $AssetsImagesKickerGen(); $AssetsImagesLaunchRampGen get launchRamp => const $AssetsImagesLaunchRampGen(); $AssetsImagesSpaceshipGen get spaceship => const $AssetsImagesSpaceshipGen(); @@ -27,8 +35,11 @@ class $AssetsImagesGen { class $AssetsImagesBaseboardGen { const $AssetsImagesBaseboardGen(); + /// File path: assets/images/baseboard/left.png AssetGenImage get left => const AssetGenImage('assets/images/baseboard/left.png'); + + /// File path: assets/images/baseboard/right.png AssetGenImage get right => const AssetGenImage('assets/images/baseboard/right.png'); } @@ -36,8 +47,11 @@ class $AssetsImagesBaseboardGen { 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'); } @@ -45,8 +59,11 @@ class $AssetsImagesBoundaryGen { class $AssetsImagesChromeDinoGen { const $AssetsImagesChromeDinoGen(); + /// File path: assets/images/chrome_dino/head.png AssetGenImage get head => const AssetGenImage('assets/images/chrome_dino/head.png'); + + /// File path: assets/images/chrome_dino/mouth.png AssetGenImage get mouth => const AssetGenImage('assets/images/chrome_dino/mouth.png'); } @@ -63,8 +80,11 @@ class $AssetsImagesDashBumperGen { class $AssetsImagesDinoGen { const $AssetsImagesDinoGen(); + /// File path: assets/images/dino/dino-land-bottom.png AssetGenImage get dinoLandBottom => const AssetGenImage('assets/images/dino/dino-land-bottom.png'); + + /// File path: assets/images/dino/dino-land-top.png AssetGenImage get dinoLandTop => const AssetGenImage('assets/images/dino/dino-land-top.png'); } @@ -72,17 +92,35 @@ class $AssetsImagesDinoGen { class $AssetsImagesFlipperGen { const $AssetsImagesFlipperGen(); + /// File path: assets/images/flipper/left.png AssetGenImage get left => const AssetGenImage('assets/images/flipper/left.png'); + + /// File path: assets/images/flipper/right.png AssetGenImage get right => const AssetGenImage('assets/images/flipper/right.png'); } +class $AssetsImagesKickerGen { + const $AssetsImagesKickerGen(); + + /// File path: assets/images/kicker/left.png + AssetGenImage get left => + const AssetGenImage('assets/images/kicker/left.png'); + + /// File path: assets/images/kicker/right.png + AssetGenImage get right => + const AssetGenImage('assets/images/kicker/right.png'); +} + class $AssetsImagesLaunchRampGen { const $AssetsImagesLaunchRampGen(); + /// File path: assets/images/launch_ramp/foreground-railing.png AssetGenImage get foregroundRailing => const AssetGenImage('assets/images/launch_ramp/foreground-railing.png'); + + /// File path: assets/images/launch_ramp/ramp.png AssetGenImage get ramp => const AssetGenImage('assets/images/launch_ramp/ramp.png'); } @@ -90,12 +128,16 @@ class $AssetsImagesLaunchRampGen { class $AssetsImagesSpaceshipGen { const $AssetsImagesSpaceshipGen(); + /// File path: assets/images/spaceship/bridge.png AssetGenImage get bridge => const AssetGenImage('assets/images/spaceship/bridge.png'); + $AssetsImagesSpaceshipRailGen get rail => const $AssetsImagesSpaceshipRailGen(); $AssetsImagesSpaceshipRampGen get ramp => const $AssetsImagesSpaceshipRampGen(); + + /// File path: assets/images/spaceship/saucer.png AssetGenImage get saucer => const AssetGenImage('assets/images/spaceship/saucer.png'); } @@ -103,8 +145,11 @@ class $AssetsImagesSpaceshipGen { class $AssetsImagesDashBumperAGen { const $AssetsImagesDashBumperAGen(); + /// File path: assets/images/dash_bumper/a/active.png AssetGenImage get active => const AssetGenImage('assets/images/dash_bumper/a/active.png'); + + /// File path: assets/images/dash_bumper/a/inactive.png AssetGenImage get inactive => const AssetGenImage('assets/images/dash_bumper/a/inactive.png'); } @@ -112,8 +157,11 @@ class $AssetsImagesDashBumperAGen { class $AssetsImagesDashBumperBGen { const $AssetsImagesDashBumperBGen(); + /// File path: assets/images/dash_bumper/b/active.png AssetGenImage get active => const AssetGenImage('assets/images/dash_bumper/b/active.png'); + + /// File path: assets/images/dash_bumper/b/inactive.png AssetGenImage get inactive => const AssetGenImage('assets/images/dash_bumper/b/inactive.png'); } @@ -121,8 +169,11 @@ class $AssetsImagesDashBumperBGen { class $AssetsImagesDashBumperMainGen { const $AssetsImagesDashBumperMainGen(); + /// File path: assets/images/dash_bumper/main/active.png AssetGenImage get active => const AssetGenImage('assets/images/dash_bumper/main/active.png'); + + /// File path: assets/images/dash_bumper/main/inactive.png AssetGenImage get inactive => const AssetGenImage('assets/images/dash_bumper/main/inactive.png'); } @@ -130,8 +181,11 @@ class $AssetsImagesDashBumperMainGen { class $AssetsImagesSpaceshipRailGen { const $AssetsImagesSpaceshipRailGen(); + /// File path: assets/images/spaceship/rail/foreground.png AssetGenImage get foreground => const AssetGenImage('assets/images/spaceship/rail/foreground.png'); + + /// File path: assets/images/spaceship/rail/main.png AssetGenImage get main => const AssetGenImage('assets/images/spaceship/rail/main.png'); } @@ -139,10 +193,15 @@ class $AssetsImagesSpaceshipRailGen { class $AssetsImagesSpaceshipRampGen { const $AssetsImagesSpaceshipRampGen(); + /// File path: assets/images/spaceship/ramp/main.png AssetGenImage get main => const AssetGenImage('assets/images/spaceship/ramp/main.png'); + + /// File path: assets/images/spaceship/ramp/railing-background.png AssetGenImage get railingBackground => const AssetGenImage( 'assets/images/spaceship/ramp/railing-background.png'); + + /// File path: assets/images/spaceship/ramp/railing-foreground.png AssetGenImage get railingForeground => const AssetGenImage( 'assets/images/spaceship/ramp/railing-foreground.png'); } diff --git a/packages/pinball_components/lib/src/components/kicker.dart b/packages/pinball_components/lib/src/components/kicker.dart index d9eb7932..442f4200 100644 --- a/packages/pinball_components/lib/src/components/kicker.dart +++ b/packages/pinball_components/lib/src/components/kicker.dart @@ -1,10 +1,10 @@ import 'dart:math' as math; -import 'package:flame/extensions.dart'; +import 'package:flame/components.dart'; import 'package:flame_forge2d/flame_forge2d.dart'; -import 'package:flutter/material.dart'; import 'package:geometry/geometry.dart' as geometry show centroid; -import 'package:pinball_components/pinball_components.dart'; +import 'package:pinball_components/gen/assets.gen.dart'; +import 'package:pinball_components/pinball_components.dart' hide Assets; /// {@template kicker} /// Triangular [BodyType.static] body that propels the [Ball] towards the @@ -16,12 +16,7 @@ class Kicker extends BodyComponent with InitialPosition { /// {@macro kicker} Kicker({ required BoardSide side, - }) : _side = side { - // TODO(alestiago): Use sprite instead of color when provided. - paint = Paint() - ..color = const Color(0xFF00FF00) - ..style = PaintingStyle.fill; - } + }) : _side = side; /// Whether the [Kicker] is on the left or right side of the board. /// @@ -31,24 +26,22 @@ class Kicker extends BodyComponent with InitialPosition { final BoardSide _side; /// The size of the [Kicker] body. - // TODO(alestiago): Use size from PositionedBodyComponent instead, - // once a sprite is given. - static final Vector2 size = Vector2(4, 10); + static final Vector2 size = Vector2(4.4, 15); List _createFixtureDefs() { final fixturesDefs = []; final direction = _side.direction; const quarterPi = math.pi / 4; - final upperCircle = CircleShape()..radius = 1.45; + final upperCircle = CircleShape()..radius = 1.6; upperCircle.position.setValues(0, -upperCircle.radius / 2); final upperCircleFixtureDef = FixtureDef(upperCircle)..friction = 0; fixturesDefs.add(upperCircleFixtureDef); - final lowerCircle = CircleShape()..radius = 1.45; + final lowerCircle = CircleShape()..radius = 1.6; lowerCircle.position.setValues( size.x * -direction, - -size.y, + -size.y - 0.8, ); final lowerCircleFixtureDef = FixtureDef(lowerCircle)..friction = 0; fixturesDefs.add(lowerCircleFixtureDef); @@ -60,8 +53,7 @@ class Kicker extends BodyComponent with InitialPosition { upperCircle.radius * direction, 0, ), - // TODO(alestiago): Use values from design. - Vector2(2.0 * direction, -size.y + 2), + Vector2(2.5 * direction, -size.y + 2), ); final wallFacingLineFixtureDef = FixtureDef(wallFacingEdge)..friction = 0; fixturesDefs.add(wallFacingLineFixtureDef); @@ -125,6 +117,27 @@ class Kicker extends BodyComponent with InitialPosition { return body; } + + @override + Future onLoad() async { + await super.onLoad(); + renderBody = false; + + final sprite = await gameRef.loadSprite( + (_side.isLeft) + ? Assets.images.kicker.left.keyName + : Assets.images.kicker.right.keyName, + ); + + await add( + SpriteComponent( + sprite: sprite, + size: Vector2(8.7, 19), + anchor: Anchor.center, + position: Vector2(0.7 * -_side.direction, -2.2), + ), + ); + } } // TODO(alestiago): Evaluate if there's value on generalising this to diff --git a/packages/pinball_components/pubspec.yaml b/packages/pinball_components/pubspec.yaml index 0e5eb37a..c7302d0d 100644 --- a/packages/pinball_components/pubspec.yaml +++ b/packages/pinball_components/pubspec.yaml @@ -38,6 +38,7 @@ flutter: - assets/images/spaceship/rail/ - assets/images/spaceship/ramp/ - assets/images/chrome_dino/ + - assets/images/kicker/ flutter_gen: line_length: 80 diff --git a/packages/pinball_components/sandbox/lib/main.dart b/packages/pinball_components/sandbox/lib/main.dart index 59066fca..1801fa52 100644 --- a/packages/pinball_components/sandbox/lib/main.dart +++ b/packages/pinball_components/sandbox/lib/main.dart @@ -6,6 +6,7 @@ // https://opensource.org/licenses/MIT. import 'package:dashbook/dashbook.dart'; import 'package:flutter/material.dart'; +import 'package:sandbox/stories/kicker/stories.dart'; import 'package:sandbox/stories/stories.dart'; void main() { @@ -19,5 +20,6 @@ void main() { addBaseboardStories(dashbook); addChromeDinoStories(dashbook); addDashNestBumperStories(dashbook); + addKickerStories(dashbook); runApp(dashbook); } diff --git a/packages/pinball_components/sandbox/lib/stories/kicker/kicker_game.dart b/packages/pinball_components/sandbox/lib/stories/kicker/kicker_game.dart new file mode 100644 index 00000000..21c0cfb8 --- /dev/null +++ b/packages/pinball_components/sandbox/lib/stories/kicker/kicker_game.dart @@ -0,0 +1,39 @@ +import 'package:flame/extensions.dart'; +import 'package:pinball_components/pinball_components.dart'; +import 'package:sandbox/common/common.dart'; +import 'package:sandbox/stories/ball/basic_ball_game.dart'; + +class KickerGame extends BasicBallGame { + KickerGame({ + required this.trace, + }) : super(color: const Color(0xFFFF0000)); + + static const info = ''' + Shows how Kickers are rendered. + + - Activate the "trace" parameter to overlay the body. + - Tap anywhere on the screen to spawn a ball into the game. +'''; + + final bool trace; + + @override + Future onLoad() async { + await super.onLoad(); + + final center = screenToWorld(camera.viewport.canvasSize! / 2); + + final leftKicker = Kicker(side: BoardSide.left) + ..initialPosition = Vector2(center.x - (Kicker.size.x * 2), center.y); + await add(leftKicker); + + final rightKicker = Kicker(side: BoardSide.right) + ..initialPosition = Vector2(center.x + (Kicker.size.x * 2), center.y); + await add(rightKicker); + + if (trace) { + leftKicker.trace(); + rightKicker.trace(); + } + } +} diff --git a/packages/pinball_components/sandbox/lib/stories/kicker/stories.dart b/packages/pinball_components/sandbox/lib/stories/kicker/stories.dart new file mode 100644 index 00000000..f4a6bf91 --- /dev/null +++ b/packages/pinball_components/sandbox/lib/stories/kicker/stories.dart @@ -0,0 +1,17 @@ +import 'package:dashbook/dashbook.dart'; +import 'package:flame/game.dart'; +import 'package:sandbox/common/common.dart'; +import 'package:sandbox/stories/kicker/kicker_game.dart'; + +void addKickerStories(Dashbook dashbook) { + dashbook.storiesOf('Kickers').add( + 'Basic', + (context) => GameWidget( + game: KickerGame( + trace: context.boolProperty('Trace', true), + ), + ), + codeLink: buildSourceLink('kicker_game/basic.dart'), + info: KickerGame.info, + ); +} diff --git a/packages/pinball_components/test/src/components/golden/kickers.png b/packages/pinball_components/test/src/components/golden/kickers.png new file mode 100644 index 00000000..23176923 Binary files /dev/null and b/packages/pinball_components/test/src/components/golden/kickers.png differ diff --git a/packages/pinball_components/test/src/components/kicker_test.dart b/packages/pinball_components/test/src/components/kicker_test.dart index 4af7b8b1..55802703 100644 --- a/packages/pinball_components/test/src/components/kicker_test.dart +++ b/packages/pinball_components/test/src/components/kicker_test.dart @@ -5,10 +5,34 @@ 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('Kicker', () { - // TODO(alestiago): Include golden tests for left and right. - final flameTester = FlameTester(Forge2DGame.new); + final flameTester = FlameTester(TestGame.new); + + flameTester.testGameWidget( + 'renders correctly', + setUp: (game, tester) async { + final leftKicker = Kicker( + side: BoardSide.left, + )..initialPosition = Vector2(-20, 0); + final rightKicker = Kicker( + side: BoardSide.right, + )..initialPosition = Vector2(20, 0); + + await game.addAll([leftKicker, rightKicker]); + await game.ready(); + game.camera.followVector2(Vector2.zero()); + }, + // TODO(ruimiguel): enable test when workflows are fixed. + //verify: (game, tester) async { + // await expectLater( + // find.byGame(), + // matchesGoldenFile('golden/kickers.png'), + // ); + //}, + ); flameTester.test( 'loads correctly',