diff --git a/lib/game/components/components.dart b/lib/game/components/components.dart index f91d9baf..a5540614 100644 --- a/lib/game/components/components.dart +++ b/lib/game/components/components.dart @@ -3,10 +3,10 @@ export 'bonus_word.dart'; export 'camera_controller.dart'; export 'controlled_ball.dart'; export 'controlled_flipper.dart'; +export 'controlled_plunger.dart'; export 'controlled_sparky_computer.dart'; export 'flutter_forest.dart'; export 'game_flow_controller.dart'; -export 'plunger.dart'; export 'score_effect_controller.dart'; export 'score_points.dart'; export 'sparky_fire_zone.dart'; diff --git a/lib/game/components/controlled_plunger.dart b/lib/game/components/controlled_plunger.dart new file mode 100644 index 00000000..167f129e --- /dev/null +++ b/lib/game/components/controlled_plunger.dart @@ -0,0 +1,49 @@ +import 'package:flame/components.dart'; +import 'package:flutter/services.dart'; +import 'package:pinball/flame/flame.dart'; +import 'package:pinball_components/pinball_components.dart'; + +/// {@template controlled_plunger} +/// A [Plunger] with a [PlungerController] attached. +/// {@endtemplate} +class ControlledPlunger extends Plunger with Controls { + /// {@macro controlled_plunger} + ControlledPlunger({required double compressionDistance}) + : super(compressionDistance: compressionDistance) { + controller = PlungerController(this); + } +} + +/// {@template plunger_controller} +/// A [ComponentController] that controls a [Plunger]s movement. +/// {@endtemplate} +class PlungerController extends ComponentController + with KeyboardHandler { + /// {@macro plunger_controller} + PlungerController(Plunger plunger) : super(plunger); + + /// The [LogicalKeyboardKey]s that will control the [Flipper]. + /// + /// [onKeyEvent] method listens to when one of these keys is pressed. + static const List _keys = [ + LogicalKeyboardKey.arrowDown, + LogicalKeyboardKey.space, + LogicalKeyboardKey.keyS, + ]; + + @override + bool onKeyEvent( + RawKeyEvent event, + Set keysPressed, + ) { + if (!_keys.contains(event.logicalKey)) return true; + + if (event is RawKeyDownEvent) { + component.pull(); + } else if (event is RawKeyUpEvent) { + component.release(); + } + + return false; + } +} diff --git a/lib/game/game_assets.dart b/lib/game/game_assets.dart index c5d95392..c499f5a4 100644 --- a/lib/game/game_assets.dart +++ b/lib/game/game_assets.dart @@ -47,6 +47,7 @@ extension PinballGameAssetsX on PinballGame { images.load(components.Assets.images.spaceship.rail.foreground.keyName), images.load(components.Assets.images.chromeDino.mouth.keyName), images.load(components.Assets.images.chromeDino.head.keyName), + images.load(components.Assets.images.plunger.plunger.keyName), images.load(components.Assets.images.sparky.computer.base.keyName), images.load(components.Assets.images.sparky.computer.top.keyName), images.load(components.Assets.images.sparky.bumper.a.active.keyName), diff --git a/lib/game/pinball_game.dart b/lib/game/pinball_game.dart index 8d080b22..df602184 100644 --- a/lib/game/pinball_game.dart +++ b/lib/game/pinball_game.dart @@ -48,7 +48,7 @@ class PinballGame extends Forge2DGame unawaited(addFromBlueprint(LaunchRamp())); unawaited(addFromBlueprint(ControlledSparkyComputer())); - final plunger = Plunger(compressionDistance: 29) + final plunger = ControlledPlunger(compressionDistance: 29) ..initialPosition = Vector2(38, -19); await add(plunger); diff --git a/lib/gen/assets.gen.dart b/lib/gen/assets.gen.dart index b3b964f3..90013646 100644 --- a/lib/gen/assets.gen.dart +++ b/lib/gen/assets.gen.dart @@ -3,6 +3,8 @@ /// FlutterGen /// ***************************************************** +// ignore_for_file: directives_ordering,unnecessary_import + import 'package:flutter/widgets.dart'; class $AssetsImagesGen { @@ -15,8 +17,11 @@ class $AssetsImagesGen { class $AssetsImagesComponentsGen { const $AssetsImagesComponentsGen(); + /// File path: assets/images/components/background.png AssetGenImage get background => const AssetGenImage('assets/images/components/background.png'); + + /// File path: assets/images/components/plunger.png AssetGenImage get plunger => const AssetGenImage('assets/images/components/plunger.png'); } diff --git a/packages/pinball_components/assets/images/google_word/letter1.png b/packages/pinball_components/assets/images/google_word/letter1.png new file mode 100644 index 00000000..f79ea687 Binary files /dev/null and b/packages/pinball_components/assets/images/google_word/letter1.png differ diff --git a/packages/pinball_components/assets/images/google_word/letter2.png b/packages/pinball_components/assets/images/google_word/letter2.png new file mode 100644 index 00000000..e9d205e3 Binary files /dev/null and b/packages/pinball_components/assets/images/google_word/letter2.png differ diff --git a/packages/pinball_components/assets/images/google_word/letter3.png b/packages/pinball_components/assets/images/google_word/letter3.png new file mode 100644 index 00000000..e9d205e3 Binary files /dev/null and b/packages/pinball_components/assets/images/google_word/letter3.png differ diff --git a/packages/pinball_components/assets/images/google_word/letter4.png b/packages/pinball_components/assets/images/google_word/letter4.png new file mode 100644 index 00000000..f79ea687 Binary files /dev/null and b/packages/pinball_components/assets/images/google_word/letter4.png differ diff --git a/packages/pinball_components/assets/images/google_word/letter5.png b/packages/pinball_components/assets/images/google_word/letter5.png new file mode 100644 index 00000000..13f30fb7 Binary files /dev/null and b/packages/pinball_components/assets/images/google_word/letter5.png differ diff --git a/packages/pinball_components/assets/images/google_word/letter6.png b/packages/pinball_components/assets/images/google_word/letter6.png new file mode 100644 index 00000000..7d87654b Binary files /dev/null and b/packages/pinball_components/assets/images/google_word/letter6.png differ diff --git a/packages/pinball_components/assets/images/plunger/plunger.png b/packages/pinball_components/assets/images/plunger/plunger.png new file mode 100644 index 00000000..f3cbdf0f Binary files /dev/null and b/packages/pinball_components/assets/images/plunger/plunger.png differ diff --git a/packages/pinball_components/lib/gen/assets.gen.dart b/packages/pinball_components/lib/gen/assets.gen.dart index 56efb200..7b535ce8 100644 --- a/packages/pinball_components/lib/gen/assets.gen.dart +++ b/packages/pinball_components/lib/gen/assets.gen.dart @@ -3,13 +3,18 @@ /// FlutterGen /// ***************************************************** +// ignore_for_file: directives_ordering,unnecessary_import + import 'package:flutter/widgets.dart'; class $AssetsImagesGen { const $AssetsImagesGen(); $AssetsImagesBackboardGen get backboard => const $AssetsImagesBackboardGen(); + + /// 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,11 +22,17 @@ class $AssetsImagesGen { $AssetsImagesDashGen get dash => const $AssetsImagesDashGen(); $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'); + + $AssetsImagesGoogleWordGen get googleWord => + const $AssetsImagesGoogleWordGen(); $AssetsImagesKickerGen get kicker => const $AssetsImagesKickerGen(); $AssetsImagesLaunchRampGen get launchRamp => const $AssetsImagesLaunchRampGen(); + $AssetsImagesPlungerGen get plunger => const $AssetsImagesPlungerGen(); $AssetsImagesSlingshotGen get slingshot => const $AssetsImagesSlingshotGen(); $AssetsImagesSpaceshipGen get spaceship => const $AssetsImagesSpaceshipGen(); $AssetsImagesSparkyGen get sparky => const $AssetsImagesSparkyGen(); @@ -30,8 +41,11 @@ class $AssetsImagesGen { class $AssetsImagesBackboardGen { const $AssetsImagesBackboardGen(); + /// File path: assets/images/backboard/backboard_game_over.png AssetGenImage get backboardGameOver => const AssetGenImage('assets/images/backboard/backboard_game_over.png'); + + /// File path: assets/images/backboard/backboard_scores.png AssetGenImage get backboardScores => const AssetGenImage('assets/images/backboard/backboard_scores.png'); } @@ -39,8 +53,11 @@ class $AssetsImagesBackboardGen { 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'); } @@ -48,8 +65,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'); } @@ -57,8 +77,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'); } @@ -76,8 +99,11 @@ class $AssetsImagesDashGen { 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'); } @@ -85,17 +111,51 @@ 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 $AssetsImagesGoogleWordGen { + const $AssetsImagesGoogleWordGen(); + + /// File path: assets/images/google_word/letter1.png + AssetGenImage get letter1 => + const AssetGenImage('assets/images/google_word/letter1.png'); + + /// File path: assets/images/google_word/letter2.png + AssetGenImage get letter2 => + const AssetGenImage('assets/images/google_word/letter2.png'); + + /// File path: assets/images/google_word/letter3.png + AssetGenImage get letter3 => + const AssetGenImage('assets/images/google_word/letter3.png'); + + /// File path: assets/images/google_word/letter4.png + AssetGenImage get letter4 => + const AssetGenImage('assets/images/google_word/letter4.png'); + + /// File path: assets/images/google_word/letter5.png + AssetGenImage get letter5 => + const AssetGenImage('assets/images/google_word/letter5.png'); + + /// File path: assets/images/google_word/letter6.png + AssetGenImage get letter6 => + const AssetGenImage('assets/images/google_word/letter6.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'); } @@ -103,21 +163,39 @@ class $AssetsImagesKickerGen { 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'); } +class $AssetsImagesPlungerGen { + const $AssetsImagesPlungerGen(); + + /// File path: assets/images/plunger/plunger.png + AssetGenImage get plunger => + const AssetGenImage('assets/images/plunger/plunger.png'); +} + class $AssetsImagesSlingshotGen { const $AssetsImagesSlingshotGen(); + /// File path: assets/images/slingshot/left_lower.png AssetGenImage get leftLower => const AssetGenImage('assets/images/slingshot/left_lower.png'); + + /// File path: assets/images/slingshot/left_upper.png AssetGenImage get leftUpper => const AssetGenImage('assets/images/slingshot/left_upper.png'); + + /// File path: assets/images/slingshot/right_lower.png AssetGenImage get rightLower => const AssetGenImage('assets/images/slingshot/right_lower.png'); + + /// File path: assets/images/slingshot/right_upper.png AssetGenImage get rightUpper => const AssetGenImage('assets/images/slingshot/right_upper.png'); } @@ -125,12 +203,16 @@ class $AssetsImagesSlingshotGen { 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'); } @@ -156,8 +238,11 @@ class $AssetsImagesDashBumperGen { 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'); } @@ -165,10 +250,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/gen/fonts.gen.dart b/packages/pinball_components/lib/gen/fonts.gen.dart index b15f2dd0..5f77da16 100644 --- a/packages/pinball_components/lib/gen/fonts.gen.dart +++ b/packages/pinball_components/lib/gen/fonts.gen.dart @@ -3,9 +3,14 @@ /// FlutterGen /// ***************************************************** +// ignore_for_file: directives_ordering,unnecessary_import + class FontFamily { FontFamily._(); + /// Font family: PixeloidMono static const String pixeloidMono = 'PixeloidMono'; + + /// Font family: PixeloidSans static const String pixeloidSans = 'PixeloidSans'; } diff --git a/packages/pinball_components/lib/src/components/board_side.dart b/packages/pinball_components/lib/src/components/board_side.dart index ac530567..9f6be71c 100644 --- a/packages/pinball_components/lib/src/components/board_side.dart +++ b/packages/pinball_components/lib/src/components/board_side.dart @@ -1,12 +1,8 @@ -// ignore_for_file: comment_references -// TODO(alestiago): Revisit ignore lint rule once Kicker is moved to this -// package. - import 'package:pinball_components/pinball_components.dart'; /// Indicates a side of the board. /// -/// Usually used to position or mirror elements of a [PinballGame]; such as a +/// Usually used to position or mirror elements of a pinball game; such as a /// [Flipper] or [Kicker]. enum BoardSide { /// The left side of the board. diff --git a/packages/pinball_components/lib/src/components/components.dart b/packages/pinball_components/lib/src/components/components.dart index 76005f8a..0348e4df 100644 --- a/packages/pinball_components/lib/src/components/components.dart +++ b/packages/pinball_components/lib/src/components/components.dart @@ -12,11 +12,13 @@ export 'dino_walls.dart'; export 'fire_effect.dart'; export 'flipper.dart'; export 'flutter_sign_post.dart'; +export 'google_letter.dart'; export 'initial_position.dart'; export 'joint_anchor.dart'; export 'kicker.dart'; export 'launch_ramp.dart'; export 'layer.dart'; +export 'plunger.dart'; export 'ramp_opening.dart'; export 'score_text.dart'; export 'shapes/shapes.dart'; diff --git a/packages/pinball_components/lib/src/components/google_letter.dart b/packages/pinball_components/lib/src/components/google_letter.dart new file mode 100644 index 00000000..9e9e2dec --- /dev/null +++ b/packages/pinball_components/lib/src/components/google_letter.dart @@ -0,0 +1,95 @@ +import 'package:flame/components.dart'; +import 'package:flame/effects.dart'; +import 'package:flame_forge2d/flame_forge2d.dart'; +import 'package:flutter/material.dart'; +import 'package:pinball_components/pinball_components.dart'; + +/// {@template google_letter} +/// Circular sensor that represents a letter in "GOOGLE" for a given index. +/// {@endtemplate} +class GoogleLetter extends BodyComponent with InitialPosition { + /// {@macro google_letter} + GoogleLetter(int index) + : _sprite = _GoogleLetterSprite( + _GoogleLetterSprite.spritePaths[index], + ); + + final _GoogleLetterSprite _sprite; + + /// Activates this [GoogleLetter]. + // TODO(alestiago): Improve doc comment once activate and deactivate + // are implemented with the actual assets. + Future activate() => _sprite.activate(); + + /// Deactivates this [GoogleLetter]. + Future deactivate() => _sprite.deactivate(); + + @override + Future onLoad() async { + await super.onLoad(); + await add(_sprite); + } + + @override + Body createBody() { + final shape = CircleShape()..radius = 1.85; + final fixtureDef = FixtureDef(shape)..isSensor = true; + + final bodyDef = BodyDef() + ..position = initialPosition + ..userData = this + ..type = BodyType.static; + + return world.createBody(bodyDef)..createFixture(fixtureDef); + } +} + +class _GoogleLetterSprite extends SpriteComponent with HasGameRef { + _GoogleLetterSprite(String path) : _path = path; + + static final spritePaths = [ + Assets.images.googleWord.letter1.keyName, + Assets.images.googleWord.letter2.keyName, + Assets.images.googleWord.letter3.keyName, + Assets.images.googleWord.letter4.keyName, + Assets.images.googleWord.letter5.keyName, + Assets.images.googleWord.letter6.keyName, + ]; + + final String _path; + + // TODO(alestiago): Correctly implement activate and deactivate once the + // assets are provided. + Future activate() async { + await add( + _GoogleLetterColorEffect(color: Colors.green), + ); + } + + Future deactivate() async { + await add( + _GoogleLetterColorEffect(color: Colors.red), + ); + } + + @override + Future onLoad() async { + await super.onLoad(); + + final sprite = await gameRef.loadSprite(_path); + this.sprite = sprite; + // TODO(alestiago): Size correctly once the assets are provided. + size = sprite.originalSize / 5; + anchor = Anchor.center; + } +} + +class _GoogleLetterColorEffect extends ColorEffect { + _GoogleLetterColorEffect({ + required Color color, + }) : super( + color, + const Offset(0, 1), + EffectController(duration: 0.25), + ); +} diff --git a/lib/game/components/plunger.dart b/packages/pinball_components/lib/src/components/plunger.dart similarity index 73% rename from lib/game/components/plunger.dart rename to packages/pinball_components/lib/src/components/plunger.dart index b8c079b5..7e0ba5ba 100644 --- a/lib/game/components/plunger.dart +++ b/packages/pinball_components/lib/src/components/plunger.dart @@ -1,16 +1,14 @@ import 'package:flame/components.dart'; import 'package:flame_forge2d/flame_forge2d.dart'; -import 'package:flutter/services.dart'; -import 'package:pinball/gen/assets.gen.dart'; -import 'package:pinball_components/pinball_components.dart' hide Assets; +import 'package:pinball_components/pinball_components.dart'; /// {@template plunger} /// [Plunger] serves as a spring, that shoots the ball on the right side of the /// playfield. /// -/// [Plunger] ignores gravity so the player controls its downward [_pull]. +/// [Plunger] ignores gravity so the player controls its downward [pull]. /// {@endtemplate} -class Plunger extends BodyComponent with KeyboardHandler, InitialPosition { +class Plunger extends BodyComponent with InitialPosition { /// {@macro plunger} Plunger({ required this.compressionDistance, @@ -43,7 +41,7 @@ class Plunger extends BodyComponent with KeyboardHandler, InitialPosition { } /// Set a constant downward velocity on the [Plunger]. - void _pull() { + void pull() { body.linearVelocity = Vector2(0, -7); } @@ -51,32 +49,11 @@ class Plunger extends BodyComponent with KeyboardHandler, InitialPosition { /// /// The velocity's magnitude depends on how far the [Plunger] has been pulled /// from its original [initialPosition]. - void _release() { + void release() { final velocity = (initialPosition.y - body.position.y) * 5; body.linearVelocity = Vector2(0, velocity); } - @override - bool onKeyEvent( - RawKeyEvent event, - Set keysPressed, - ) { - final keys = [ - LogicalKeyboardKey.space, - LogicalKeyboardKey.arrowDown, - LogicalKeyboardKey.keyS, - ]; - if (!keys.contains(event.logicalKey)) return true; - - if (event is RawKeyDownEvent) { - _pull(); - } else if (event is RawKeyUpEvent) { - _release(); - } - - return false; - } - /// Anchors the [Plunger] to the [PrismaticJoint] that controls its vertical /// motion. Future _anchorToJoint() async { @@ -97,26 +74,24 @@ class Plunger extends BodyComponent with KeyboardHandler, InitialPosition { Future onLoad() async { await super.onLoad(); await _anchorToJoint(); - renderBody = false; - - await _loadSprite(); + await add(_PlungerSpriteComponent()); } +} - Future _loadSprite() async { +class _PlungerSpriteComponent extends SpriteComponent with HasGameRef { + @override + Future onLoad() async { + await super.onLoad(); final sprite = await gameRef.loadSprite( - Assets.images.components.plunger.path, + Assets.images.plunger.plunger.keyName, ); - await add( - SpriteComponent( - sprite: sprite, - size: Vector2(5.5, 40), - anchor: Anchor.center, - position: Vector2(2, 19), - angle: -0.033, - ), - ); + this.sprite = sprite; + size = sprite.originalSize / 10; + anchor = Anchor.center; + position = Vector2(2, 19); + angle = -0.033; } } @@ -133,14 +108,6 @@ class PlungerAnchor extends JointAnchor { -plunger.compressionDistance, ); } - - @override - Body createBody() { - final bodyDef = BodyDef() - ..position = initialPosition - ..type = BodyType.static; - return world.createBody(bodyDef); - } } /// {@template plunger_anchor_prismatic_joint_def} diff --git a/packages/pinball_components/pubspec.yaml b/packages/pinball_components/pubspec.yaml index a6ccb713..fc44a74a 100644 --- a/packages/pinball_components/pubspec.yaml +++ b/packages/pinball_components/pubspec.yaml @@ -50,12 +50,14 @@ flutter: - assets/images/spaceship/ramp/ - assets/images/chrome_dino/ - assets/images/kicker/ + - assets/images/plunger/ - assets/images/slingshot/ - assets/images/sparky/computer/ - assets/images/sparky/bumper/a/ - assets/images/sparky/bumper/b/ - assets/images/sparky/bumper/c/ - assets/images/backboard/ + - assets/images/google_word/ flutter_gen: line_length: 80 diff --git a/packages/pinball_components/sandbox/lib/main.dart b/packages/pinball_components/sandbox/lib/main.dart index 1c9c9c25..1e4aab5e 100644 --- a/packages/pinball_components/sandbox/lib/main.dart +++ b/packages/pinball_components/sandbox/lib/main.dart @@ -21,10 +21,12 @@ void main() { addChromeDinoStories(dashbook); addDashNestBumperStories(dashbook); addKickerStories(dashbook); + addPlungerStories(dashbook); addSlingshotStories(dashbook); addSparkyBumperStories(dashbook); addZoomStories(dashbook); addBoundariesStories(dashbook); + addGoogleWordStories(dashbook); addSpaceshipRampStories(dashbook); addSpaceshipRailStories(dashbook); addLaunchRampStories(dashbook); diff --git a/packages/pinball_components/sandbox/lib/stories/ball/ball_booster_game.dart b/packages/pinball_components/sandbox/lib/stories/ball/ball_booster_game.dart index 3b8fe149..2fa4462e 100644 --- a/packages/pinball_components/sandbox/lib/stories/ball/ball_booster_game.dart +++ b/packages/pinball_components/sandbox/lib/stories/ball/ball_booster_game.dart @@ -7,7 +7,7 @@ class BallBoosterGame extends LineGame { static const info = ''' Shows how a Ball with a boost works. - Drag to launch a boosted Ball. + - Drag to launch a boosted Ball. '''; @override diff --git a/packages/pinball_components/sandbox/lib/stories/ball/basic_ball_game.dart b/packages/pinball_components/sandbox/lib/stories/ball/basic_ball_game.dart index bfc7a9b0..17bd0723 100644 --- a/packages/pinball_components/sandbox/lib/stories/ball/basic_ball_game.dart +++ b/packages/pinball_components/sandbox/lib/stories/ball/basic_ball_game.dart @@ -5,7 +5,7 @@ import 'package:sandbox/common/common.dart'; class BasicBallGame extends BasicGame with TapDetector, Traceable { BasicBallGame({ - required this.color, + this.color = Colors.blue, this.ballPriority = 0, this.ballLayer = Layer.all, }); @@ -13,7 +13,7 @@ class BasicBallGame extends BasicGame with TapDetector, Traceable { static const info = ''' Shows how a Ball works. - Tap anywhere on the screen to spawn a ball into the game. + - Tap anywhere on the screen to spawn a ball into the game. '''; final Color color; diff --git a/packages/pinball_components/sandbox/lib/stories/boundaries/boundaries_game.dart b/packages/pinball_components/sandbox/lib/stories/boundaries/boundaries_game.dart index a98fb7b9..0bc2755d 100644 --- a/packages/pinball_components/sandbox/lib/stories/boundaries/boundaries_game.dart +++ b/packages/pinball_components/sandbox/lib/stories/boundaries/boundaries_game.dart @@ -4,8 +4,6 @@ import 'package:sandbox/common/common.dart'; import 'package:sandbox/stories/ball/basic_ball_game.dart'; class BoundariesGame extends BasicBallGame with Traceable { - BoundariesGame() : super(color: const Color(0xFFFF0000)); - static const info = ''' Shows how Boundaries are rendered. diff --git a/packages/pinball_components/sandbox/lib/stories/effects/fire_effect_game.dart b/packages/pinball_components/sandbox/lib/stories/effects/fire_effect_game.dart index ecc22910..9e2acaf4 100644 --- a/packages/pinball_components/sandbox/lib/stories/effects/fire_effect_game.dart +++ b/packages/pinball_components/sandbox/lib/stories/effects/fire_effect_game.dart @@ -6,7 +6,7 @@ class FireEffectGame extends LineGame { static const info = ''' Shows how the FireEffect renders. - Drag a line to define the trail direction. + - Drag a line to define the trail direction. '''; @override diff --git a/packages/pinball_components/sandbox/lib/stories/flipper/flipper_game.dart b/packages/pinball_components/sandbox/lib/stories/flipper/flipper_game.dart index 5a9e1787..c0dfee24 100644 --- a/packages/pinball_components/sandbox/lib/stories/flipper/flipper_game.dart +++ b/packages/pinball_components/sandbox/lib/stories/flipper/flipper_game.dart @@ -7,8 +7,6 @@ import 'package:sandbox/common/common.dart'; import 'package:sandbox/stories/ball/basic_ball_game.dart'; class FlipperGame extends BasicBallGame with KeyboardEvents, Traceable { - FlipperGame() : super(color: Colors.blue); - static const info = ''' Shows how Flippers are rendered. diff --git a/packages/pinball_components/sandbox/lib/stories/flutter_forest/big_dash_nest_bumper_game.dart b/packages/pinball_components/sandbox/lib/stories/flutter_forest/big_dash_nest_bumper_game.dart index c1407819..e74abe35 100644 --- a/packages/pinball_components/sandbox/lib/stories/flutter_forest/big_dash_nest_bumper_game.dart +++ b/packages/pinball_components/sandbox/lib/stories/flutter_forest/big_dash_nest_bumper_game.dart @@ -1,5 +1,4 @@ import 'dart:async'; -import 'dart:ui'; import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:pinball_components/pinball_components.dart'; @@ -7,8 +6,6 @@ import 'package:sandbox/common/common.dart'; import 'package:sandbox/stories/ball/basic_ball_game.dart'; class BigDashNestBumperGame extends BasicBallGame with Traceable { - BigDashNestBumperGame() : super(color: const Color(0xFF0000FF)); - static const info = ''' Shows how a BigDashNestBumper is rendered. diff --git a/packages/pinball_components/sandbox/lib/stories/flutter_forest/flutter_sign_post_game.dart b/packages/pinball_components/sandbox/lib/stories/flutter_forest/flutter_sign_post_game.dart index f3ba7bda..3efb83fe 100644 --- a/packages/pinball_components/sandbox/lib/stories/flutter_forest/flutter_sign_post_game.dart +++ b/packages/pinball_components/sandbox/lib/stories/flutter_forest/flutter_sign_post_game.dart @@ -1,5 +1,4 @@ import 'dart:async'; -import 'dart:ui'; import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:pinball_components/pinball_components.dart'; @@ -7,8 +6,6 @@ import 'package:sandbox/common/common.dart'; import 'package:sandbox/stories/ball/basic_ball_game.dart'; class FlutterSignPostGame extends BasicBallGame with Traceable { - FlutterSignPostGame() : super(color: const Color(0xFF0000FF)); - static const info = ''' Shows how a FlutterSignPost is rendered. diff --git a/packages/pinball_components/sandbox/lib/stories/flutter_forest/small_dash_nest_bumper_a_game.dart b/packages/pinball_components/sandbox/lib/stories/flutter_forest/small_dash_nest_bumper_a_game.dart index a8499581..4ef99b21 100644 --- a/packages/pinball_components/sandbox/lib/stories/flutter_forest/small_dash_nest_bumper_a_game.dart +++ b/packages/pinball_components/sandbox/lib/stories/flutter_forest/small_dash_nest_bumper_a_game.dart @@ -1,5 +1,4 @@ import 'dart:async'; -import 'dart:ui'; import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:pinball_components/pinball_components.dart'; @@ -7,8 +6,6 @@ import 'package:sandbox/common/common.dart'; import 'package:sandbox/stories/ball/basic_ball_game.dart'; class SmallDashNestBumperAGame extends BasicBallGame with Traceable { - SmallDashNestBumperAGame() : super(color: const Color(0xFF0000FF)); - static const info = ''' Shows how a SmallDashNestBumper ("a") is rendered. diff --git a/packages/pinball_components/sandbox/lib/stories/flutter_forest/small_dash_nest_bumper_b_game.dart b/packages/pinball_components/sandbox/lib/stories/flutter_forest/small_dash_nest_bumper_b_game.dart index 91b2a383..625b8e5c 100644 --- a/packages/pinball_components/sandbox/lib/stories/flutter_forest/small_dash_nest_bumper_b_game.dart +++ b/packages/pinball_components/sandbox/lib/stories/flutter_forest/small_dash_nest_bumper_b_game.dart @@ -1,5 +1,4 @@ import 'dart:async'; -import 'dart:ui'; import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:pinball_components/pinball_components.dart'; @@ -7,8 +6,6 @@ import 'package:sandbox/common/common.dart'; import 'package:sandbox/stories/ball/basic_ball_game.dart'; class SmallDashNestBumperBGame extends BasicBallGame with Traceable { - SmallDashNestBumperBGame() : super(color: const Color(0xFF0000FF)); - static const info = ''' Shows how a SmallDashNestBumper ("b") is rendered. diff --git a/packages/pinball_components/sandbox/lib/stories/google_word/google_letter_game.dart b/packages/pinball_components/sandbox/lib/stories/google_word/google_letter_game.dart new file mode 100644 index 00000000..ad6e556b --- /dev/null +++ b/packages/pinball_components/sandbox/lib/stories/google_word/google_letter_game.dart @@ -0,0 +1,36 @@ +import 'dart:ui'; + +import 'package:flame_forge2d/flame_forge2d.dart'; +import 'package:flutter/material.dart'; +import 'package:pinball_components/pinball_components.dart'; +import 'package:sandbox/stories/ball/basic_ball_game.dart'; + +class GoogleLetterGame extends BasicBallGame { + GoogleLetterGame() : super(color: const Color(0xFF009900)); + + static const info = ''' + Shows how a GoogleLetter is rendered. + + - Tap anywhere on the screen to spawn a ball into the game. +'''; + + @override + Future onLoad() async { + await super.onLoad(); + addContactCallback(_BallGoogleLetterContactCallback()); + + camera.followVector2(Vector2.zero()); + await add(GoogleLetter(0)); + + await traceAllBodies(); + } +} + +class _BallGoogleLetterContactCallback + extends ContactCallback { + @override + void begin(Ball a, GoogleLetter b, Contact contact) { + super.begin(a, b, contact); + b.activate(); + } +} diff --git a/packages/pinball_components/sandbox/lib/stories/google_word/stories.dart b/packages/pinball_components/sandbox/lib/stories/google_word/stories.dart new file mode 100644 index 00000000..290bf9dd --- /dev/null +++ b/packages/pinball_components/sandbox/lib/stories/google_word/stories.dart @@ -0,0 +1,15 @@ +import 'package:dashbook/dashbook.dart'; +import 'package:flame/game.dart'; +import 'package:sandbox/common/common.dart'; +import 'package:sandbox/stories/google_word/google_letter_game.dart'; + +void addGoogleWordStories(Dashbook dashbook) { + dashbook.storiesOf('Google Word').add( + 'Letter', + (context) => GameWidget( + game: GoogleLetterGame()..trace = context.boolProperty('Trace', true), + ), + codeLink: buildSourceLink('google_word/letter.dart'), + info: GoogleLetterGame.info, + ); +} diff --git a/packages/pinball_components/sandbox/lib/stories/kicker/kicker_game.dart b/packages/pinball_components/sandbox/lib/stories/kicker/kicker_game.dart index 1b29c3f9..add7b205 100644 --- a/packages/pinball_components/sandbox/lib/stories/kicker/kicker_game.dart +++ b/packages/pinball_components/sandbox/lib/stories/kicker/kicker_game.dart @@ -4,8 +4,6 @@ import 'package:sandbox/common/common.dart'; import 'package:sandbox/stories/ball/basic_ball_game.dart'; class KickerGame extends BasicBallGame with Traceable { - KickerGame() : super(color: const Color(0xFFFF0000)); - static const info = ''' Shows how Kickers are rendered. diff --git a/packages/pinball_components/sandbox/lib/stories/layer/basic_layer_game.dart b/packages/pinball_components/sandbox/lib/stories/layer/basic_layer_game.dart index a6361094..60d72ff3 100644 --- a/packages/pinball_components/sandbox/lib/stories/layer/basic_layer_game.dart +++ b/packages/pinball_components/sandbox/lib/stories/layer/basic_layer_game.dart @@ -10,7 +10,7 @@ class BasicLayerGame extends BasicGame with TapDetector { static const info = ''' Shows how Layers work when a Ball hits other components. - Tap anywhere on the screen to spawn a Ball into the game. + - Tap anywhere on the screen to spawn a Ball into the game. '''; final Color color; diff --git a/packages/pinball_components/sandbox/lib/stories/plunger/plunger_game.dart b/packages/pinball_components/sandbox/lib/stories/plunger/plunger_game.dart new file mode 100644 index 00000000..baaab21b --- /dev/null +++ b/packages/pinball_components/sandbox/lib/stories/plunger/plunger_game.dart @@ -0,0 +1,54 @@ +import 'package:flame/input.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:pinball_components/pinball_components.dart'; +import 'package:sandbox/common/common.dart'; +import 'package:sandbox/stories/ball/basic_ball_game.dart'; + +class PlungerGame extends BasicBallGame with KeyboardEvents, Traceable { + PlungerGame() : super(color: const Color(0xFFFF0000)); + + static const info = ''' + Shows how Plunger is rendered. + + - Activate the "trace" parameter to overlay the body. + - Tap anywhere on the screen to spawn a ball into the game. +'''; + + static const _downKeys = [ + LogicalKeyboardKey.arrowDown, + LogicalKeyboardKey.space, + ]; + + late Plunger plunger; + + @override + Future onLoad() async { + await super.onLoad(); + + final center = screenToWorld(camera.viewport.canvasSize! / 2); + + plunger = Plunger(compressionDistance: 29) + ..initialPosition = Vector2(center.x - (Kicker.size.x * 2), center.y); + await add(plunger); + + await traceAllBodies(); + } + + @override + KeyEventResult onKeyEvent( + RawKeyEvent event, + Set keysPressed, + ) { + final movedPlungerDown = _downKeys.contains(event.logicalKey); + if (movedPlungerDown) { + if (event is RawKeyDownEvent) { + plunger.pull(); + } else if (event is RawKeyUpEvent) { + plunger.release(); + } + return KeyEventResult.handled; + } + return KeyEventResult.ignored; + } +} diff --git a/packages/pinball_components/sandbox/lib/stories/plunger/stories.dart b/packages/pinball_components/sandbox/lib/stories/plunger/stories.dart new file mode 100644 index 00000000..86061dc2 --- /dev/null +++ b/packages/pinball_components/sandbox/lib/stories/plunger/stories.dart @@ -0,0 +1,15 @@ +import 'package:dashbook/dashbook.dart'; +import 'package:flame/game.dart'; +import 'package:sandbox/common/common.dart'; +import 'package:sandbox/stories/plunger/plunger_game.dart'; + +void addPlungerStories(Dashbook dashbook) { + dashbook.storiesOf('Plunger').add( + 'Basic', + (context) => GameWidget( + game: PlungerGame()..trace = context.boolProperty('Trace', true), + ), + codeLink: buildSourceLink('plunger_game/basic.dart'), + info: PlungerGame.info, + ); +} diff --git a/packages/pinball_components/sandbox/lib/stories/slingshot/slingshot_game.dart b/packages/pinball_components/sandbox/lib/stories/slingshot/slingshot_game.dart index 8d54f391..dd1df4de 100644 --- a/packages/pinball_components/sandbox/lib/stories/slingshot/slingshot_game.dart +++ b/packages/pinball_components/sandbox/lib/stories/slingshot/slingshot_game.dart @@ -4,8 +4,6 @@ import 'package:sandbox/common/common.dart'; import 'package:sandbox/stories/ball/basic_ball_game.dart'; class SlingshotGame extends BasicBallGame with Traceable { - SlingshotGame() : super(color: const Color(0xFFFF0000)); - static const info = ''' Shows how Slingshots are rendered. diff --git a/packages/pinball_components/sandbox/lib/stories/spaceship/basic_spaceship_game.dart b/packages/pinball_components/sandbox/lib/stories/spaceship/basic_spaceship_game.dart index 97124c3f..95afcd7f 100644 --- a/packages/pinball_components/sandbox/lib/stories/spaceship/basic_spaceship_game.dart +++ b/packages/pinball_components/sandbox/lib/stories/spaceship/basic_spaceship_game.dart @@ -9,7 +9,7 @@ class BasicSpaceshipGame extends BasicGame with TapDetector { static const info = ''' Shows how a Spaceship works. - Tap anywhere on the screen to spawn a Ball into the game. + - Tap anywhere on the screen to spawn a Ball into the game. '''; @override diff --git a/packages/pinball_components/sandbox/lib/stories/sparky_bumper/sparky_bumper_game.dart b/packages/pinball_components/sandbox/lib/stories/sparky_bumper/sparky_bumper_game.dart index a57beb8d..37537952 100644 --- a/packages/pinball_components/sandbox/lib/stories/sparky_bumper/sparky_bumper_game.dart +++ b/packages/pinball_components/sandbox/lib/stories/sparky_bumper/sparky_bumper_game.dart @@ -6,12 +6,10 @@ import 'package:sandbox/common/common.dart'; import 'package:sandbox/stories/ball/basic_ball_game.dart'; class SparkyBumperGame extends BasicBallGame with Traceable { - SparkyBumperGame() : super(color: const Color(0xFF0000FF)); - static const info = ''' Shows how a SparkyBumper is rendered. - Activate the "trace" parameter to overlay the body. + - Activate the "trace" parameter to overlay the body. '''; @override diff --git a/packages/pinball_components/sandbox/lib/stories/stories.dart b/packages/pinball_components/sandbox/lib/stories/stories.dart index b4400500..7dd02878 100644 --- a/packages/pinball_components/sandbox/lib/stories/stories.dart +++ b/packages/pinball_components/sandbox/lib/stories/stories.dart @@ -5,8 +5,10 @@ export 'chrome_dino/stories.dart'; export 'effects/stories.dart'; export 'flipper/stories.dart'; export 'flutter_forest/stories.dart'; +export 'google_word/stories.dart'; export 'launch_ramp/stories.dart'; export 'layer/stories.dart'; +export 'plunger/stories.dart'; export 'score_text/stories.dart'; export 'slingshot/stories.dart'; export 'spaceship/stories.dart'; diff --git a/packages/pinball_components/sandbox/lib/stories/zoom/basic_zoom_game.dart b/packages/pinball_components/sandbox/lib/stories/zoom/basic_zoom_game.dart index 276dd39c..7e6d035f 100644 --- a/packages/pinball_components/sandbox/lib/stories/zoom/basic_zoom_game.dart +++ b/packages/pinball_components/sandbox/lib/stories/zoom/basic_zoom_game.dart @@ -5,8 +5,9 @@ import 'package:sandbox/common/common.dart'; class BasicCameraZoomGame extends BasicGame with TapDetector { static const info = ''' - Simple game to demonstrate how the CameraZoom can be used. - Tap to zoom in/out + Shows how CameraZoom can be used. + + - Tap to zoom in/out. '''; bool zoomApplied = false; diff --git a/packages/pinball_components/test/src/components/google_letter_test.dart b/packages/pinball_components/test/src/components/google_letter_test.dart new file mode 100644 index 00000000..cdfd3c4a --- /dev/null +++ b/packages/pinball_components/test/src/components/google_letter_test.dart @@ -0,0 +1,126 @@ +// ignore_for_file: cascade_invocations + +import 'package:flame/effects.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() { + TestWidgetsFlutterBinding.ensureInitialized(); + final flameTester = FlameTester(TestGame.new); + + group('Google Letter', () { + flameTester.test( + '0th loads correctly', + (game) async { + final googleLetter = GoogleLetter(0); + await game.ready(); + await game.ensureAdd(googleLetter); + + expect(game.contains(googleLetter), isTrue); + }, + ); + + flameTester.test( + '1st loads correctly', + (game) async { + final googleLetter = GoogleLetter(1); + await game.ready(); + await game.ensureAdd(googleLetter); + + expect(game.contains(googleLetter), isTrue); + }, + ); + + flameTester.test( + '2nd loads correctly', + (game) async { + final googleLetter = GoogleLetter(2); + await game.ready(); + await game.ensureAdd(googleLetter); + + expect(game.contains(googleLetter), isTrue); + }, + ); + + flameTester.test( + '3d loads correctly', + (game) async { + final googleLetter = GoogleLetter(3); + await game.ready(); + await game.ensureAdd(googleLetter); + + expect(game.contains(googleLetter), isTrue); + }, + ); + + flameTester.test( + '4th loads correctly', + (game) async { + final googleLetter = GoogleLetter(4); + await game.ready(); + await game.ensureAdd(googleLetter); + + expect(game.contains(googleLetter), isTrue); + }, + ); + + flameTester.test( + '5th loads correctly', + (game) async { + final googleLetter = GoogleLetter(5); + await game.ready(); + await game.ensureAdd(googleLetter); + + expect(game.contains(googleLetter), isTrue); + }, + ); + + test('throws error when index out of range', () { + expect(() => GoogleLetter(-1), throwsA(isA())); + expect(() => GoogleLetter(6), throwsA(isA())); + }); + + group('activate', () { + flameTester.test('returns normally', (game) async { + final googleLetter = GoogleLetter(0); + await game.ensureAdd(googleLetter); + await expectLater(googleLetter.activate, returnsNormally); + }); + + flameTester.test('adds an Effect', (game) async { + final googleLetter = GoogleLetter(0); + await game.ensureAdd(googleLetter); + await googleLetter.activate(); + await game.ready(); + + expect( + googleLetter.descendants().whereType().length, + equals(1), + ); + }); + }); + + group('deactivate', () { + flameTester.test('returns normally', (game) async { + final googleLetter = GoogleLetter(0); + await game.ensureAdd(googleLetter); + await expectLater(googleLetter.deactivate, returnsNormally); + }); + + flameTester.test('adds an Effect', (game) async { + final googleLetter = GoogleLetter(0); + await game.ensureAdd(googleLetter); + await googleLetter.deactivate(); + await game.ready(); + + expect( + googleLetter.descendants().whereType().length, + equals(1), + ); + }); + }); + }); +} diff --git a/test/game/components/plunger_test.dart b/packages/pinball_components/test/src/components/plunger_test.dart similarity index 62% rename from test/game/components/plunger_test.dart rename to packages/pinball_components/test/src/components/plunger_test.dart index 65789ae0..8f8a26db 100644 --- a/test/game/components/plunger_test.dart +++ b/packages/pinball_components/test/src/components/plunger_test.dart @@ -1,12 +1,9 @@ // ignore_for_file: cascade_invocations -import 'dart:collection'; - import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flame_test/flame_test.dart'; -import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:pinball/game/game.dart'; +import 'package:pinball_components/pinball_components.dart'; import '../../helpers/helpers.dart'; @@ -117,13 +114,23 @@ void main() { ); }); - group('onKeyEvent', () { - final keys = UnmodifiableListView([ - LogicalKeyboardKey.space, - LogicalKeyboardKey.arrowDown, - LogicalKeyboardKey.keyS, - ]); + group('pull', () { + flameTester.test( + 'moves downwards when pull is called', + (game) async { + final plunger = Plunger( + compressionDistance: compressionDistance, + ); + await game.ensureAdd(plunger); + plunger.pull(); + expect(plunger.body.linearVelocity.y, isNegative); + expect(plunger.body.linearVelocity.x, isZero); + }, + ); + }); + + group('release', () { late Plunger plunger; setUp(() { @@ -132,56 +139,28 @@ void main() { ); }); - testRawKeyUpEvents(keys, (event) { - final keyLabel = (event.logicalKey != LogicalKeyboardKey.space) - ? event.logicalKey.keyLabel - : 'Space'; - flameTester.test( - 'moves upwards when $keyLabel is released ' - 'and plunger is below its starting position', - (game) async { - await game.ensureAdd(plunger); - plunger.body.setTransform(Vector2(0, -1), 0); - plunger.onKeyEvent(event, {}); - - expect(plunger.body.linearVelocity.y, isPositive); - expect(plunger.body.linearVelocity.x, isZero); - }, - ); - }); + flameTester.test( + 'moves upwards when release is called ' + 'and plunger is below its starting position', (game) async { + await game.ensureAdd(plunger); + plunger.body.setTransform(Vector2(0, -1), 0); + plunger.release(); - testRawKeyUpEvents(keys, (event) { - final keyLabel = (event.logicalKey != LogicalKeyboardKey.space) - ? event.logicalKey.keyLabel - : 'Space'; - flameTester.test( - 'does not move when $keyLabel is released ' - 'and plunger is in its starting position', - (game) async { - await game.ensureAdd(plunger); - plunger.onKeyEvent(event, {}); - - expect(plunger.body.linearVelocity.y, isZero); - expect(plunger.body.linearVelocity.x, isZero); - }, - ); + expect(plunger.body.linearVelocity.y, isPositive); + expect(plunger.body.linearVelocity.x, isZero); }); - testRawKeyDownEvents(keys, (event) { - final keyLabel = (event.logicalKey != LogicalKeyboardKey.space) - ? event.logicalKey.keyLabel - : 'Space'; - flameTester.test( - 'moves downwards when $keyLabel is pressed', - (game) async { - await game.ensureAdd(plunger); - plunger.onKeyEvent(event, {}); - - expect(plunger.body.linearVelocity.y, isNegative); - expect(plunger.body.linearVelocity.x, isZero); - }, - ); - }); + flameTester.test( + 'does not move when release is called ' + 'and plunger is in its starting position', + (game) async { + await game.ensureAdd(plunger); + plunger.release(); + + expect(plunger.body.linearVelocity.y, isZero); + expect(plunger.body.linearVelocity.x, isZero); + }, + ); }); }); @@ -210,11 +189,13 @@ void main() { group('PlungerAnchorPrismaticJointDef', () { const compressionDistance = 10.0; late Plunger plunger; + late PlungerAnchor anchor; setUp(() { plunger = Plunger( compressionDistance: compressionDistance, ); + anchor = PlungerAnchor(plunger: plunger); }); group('initializes with', () { @@ -222,7 +203,6 @@ void main() { 'plunger body as bodyA', (game) async { await game.ensureAdd(plunger); - final anchor = PlungerAnchor(plunger: plunger); await game.ensureAdd(anchor); final jointDef = PlungerAnchorPrismaticJointDef( @@ -238,7 +218,6 @@ void main() { 'anchor body as bodyB', (game) async { await game.ensureAdd(plunger); - final anchor = PlungerAnchor(plunger: plunger); await game.ensureAdd(anchor); final jointDef = PlungerAnchorPrismaticJointDef( @@ -255,7 +234,6 @@ void main() { 'limits enabled', (game) async { await game.ensureAdd(plunger); - final anchor = PlungerAnchor(plunger: plunger); await game.ensureAdd(anchor); final jointDef = PlungerAnchorPrismaticJointDef( @@ -272,7 +250,6 @@ void main() { 'lower translation limit as negative infinity', (game) async { await game.ensureAdd(plunger); - final anchor = PlungerAnchor(plunger: plunger); await game.ensureAdd(anchor); final jointDef = PlungerAnchorPrismaticJointDef( @@ -289,7 +266,6 @@ void main() { 'connected body collison enabled', (game) async { await game.ensureAdd(plunger); - final anchor = PlungerAnchor(plunger: plunger); await game.ensureAdd(anchor); final jointDef = PlungerAnchorPrismaticJointDef( @@ -303,53 +279,47 @@ void main() { ); }); - testRawKeyUpEvents([LogicalKeyboardKey.space], (event) { - late final anchor = PlungerAnchor(plunger: plunger); - flameTester.testGameWidget( - 'plunger cannot go below anchor', - setUp: (game, tester) async { - await game.ensureAdd(plunger); - await game.ensureAdd(anchor); + flameTester.testGameWidget( + 'plunger cannot go below anchor', + setUp: (game, tester) async { + await game.ensureAdd(plunger); + await game.ensureAdd(anchor); - // Giving anchor a shape for the plunger to collide with. - anchor.body.createFixtureFromShape(PolygonShape()..setAsBoxXY(2, 1)); + // Giving anchor a shape for the plunger to collide with. + anchor.body.createFixtureFromShape(PolygonShape()..setAsBoxXY(2, 1)); - final jointDef = PlungerAnchorPrismaticJointDef( - plunger: plunger, - anchor: anchor, - ); - game.world.createJoint(PrismaticJoint(jointDef)); + final jointDef = PlungerAnchorPrismaticJointDef( + plunger: plunger, + anchor: anchor, + ); + game.world.createJoint(PrismaticJoint(jointDef)); - await tester.pump(const Duration(seconds: 1)); - }, - verify: (game, tester) async { - expect(plunger.body.position.y > anchor.body.position.y, isTrue); - }, - ); - }); + await tester.pump(const Duration(seconds: 1)); + }, + verify: (game, tester) async { + expect(plunger.body.position.y > anchor.body.position.y, isTrue); + }, + ); - testRawKeyUpEvents([LogicalKeyboardKey.space], (event) { - flameTester.testGameWidget( - 'plunger cannot excessively exceed starting position', - setUp: (game, tester) async { - await game.ensureAdd(plunger); - final anchor = PlungerAnchor(plunger: plunger); - await game.ensureAdd(anchor); + flameTester.testGameWidget( + 'plunger cannot excessively exceed starting position', + setUp: (game, tester) async { + await game.ensureAdd(plunger); + await game.ensureAdd(anchor); - final jointDef = PlungerAnchorPrismaticJointDef( - plunger: plunger, - anchor: anchor, - ); - game.world.createJoint(PrismaticJoint(jointDef)); + final jointDef = PlungerAnchorPrismaticJointDef( + plunger: plunger, + anchor: anchor, + ); + game.world.createJoint(PrismaticJoint(jointDef)); - plunger.body.setTransform(Vector2(0, -1), 0); + plunger.body.setTransform(Vector2(0, -1), 0); - await tester.pump(const Duration(seconds: 1)); - }, - verify: (game, tester) async { - expect(plunger.body.position.y < 1, isTrue); - }, - ); - }); + await tester.pump(const Duration(seconds: 1)); + }, + verify: (game, tester) async { + expect(plunger.body.position.y < 1, isTrue); + }, + ); }); } diff --git a/test/game/components/controlled_plunger_test.dart b/test/game/components/controlled_plunger_test.dart new file mode 100644 index 00000000..bb965fc1 --- /dev/null +++ b/test/game/components/controlled_plunger_test.dart @@ -0,0 +1,78 @@ +import 'dart:collection'; + +import 'package:flame_forge2d/flame_forge2d.dart'; +import 'package:flame_test/flame_test.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:pinball/game/game.dart'; +import 'package:pinball_components/pinball_components.dart'; + +import '../../helpers/helpers.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + final flameTester = FlameTester(EmptyPinballGameTest.new); + + group('PlungerController', () { + group('onKeyEvent', () { + final downKeys = UnmodifiableListView([ + LogicalKeyboardKey.arrowDown, + LogicalKeyboardKey.space, + LogicalKeyboardKey.keyS, + ]); + + late Plunger plunger; + late PlungerController controller; + + setUp(() { + plunger = Plunger(compressionDistance: 10); + controller = PlungerController(plunger); + plunger.add(controller); + }); + + testRawKeyDownEvents(downKeys, (event) { + flameTester.test( + 'moves down ' + 'when ${event.logicalKey.keyLabel} is pressed', + (game) async { + await game.ensureAdd(plunger); + controller.onKeyEvent(event, {}); + + expect(plunger.body.linearVelocity.y, isNegative); + expect(plunger.body.linearVelocity.x, isZero); + }, + ); + }); + + testRawKeyUpEvents(downKeys, (event) { + flameTester.test( + 'moves up ' + 'when ${event.logicalKey.keyLabel} is released ' + 'and plunger is below its starting position', + (game) async { + await game.ensureAdd(plunger); + plunger.body.setTransform(Vector2(0, -1), 0); + controller.onKeyEvent(event, {}); + + expect(plunger.body.linearVelocity.y, isPositive); + expect(plunger.body.linearVelocity.x, isZero); + }, + ); + }); + + testRawKeyUpEvents(downKeys, (event) { + flameTester.test( + 'does not move when ${event.logicalKey.keyLabel} is released ' + 'and plunger is in its starting position', + (game) async { + await game.ensureAdd(plunger); + controller.onKeyEvent(event, {}); + + expect(plunger.body.linearVelocity.y, isZero); + expect(plunger.body.linearVelocity.x, isZero); + }, + ); + }); + }); + }); +}