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 22605904..9abfa01c 100644 --- a/lib/game/game_assets.dart +++ b/lib/game/game_assets.dart @@ -46,6 +46,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/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 b52ca694..ab64439f 100644 --- a/packages/pinball_components/lib/gen/assets.gen.dart +++ b/packages/pinball_components/lib/gen/assets.gen.dart @@ -33,6 +33,7 @@ class $AssetsImagesGen { $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(); @@ -171,6 +172,14 @@ class $AssetsImagesLaunchRampGen { 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(); 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/components.dart b/packages/pinball_components/lib/src/components/components.dart index 8dca7549..b5e9392b 100644 --- a/packages/pinball_components/lib/src/components/components.dart +++ b/packages/pinball_components/lib/src/components/components.dart @@ -17,6 +17,7 @@ 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/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 72263d1d..d63e2312 100644 --- a/packages/pinball_components/pubspec.yaml +++ b/packages/pinball_components/pubspec.yaml @@ -49,6 +49,7 @@ 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/ diff --git a/packages/pinball_components/sandbox/lib/main.dart b/packages/pinball_components/sandbox/lib/main.dart index 193a0d8f..1e4aab5e 100644 --- a/packages/pinball_components/sandbox/lib/main.dart +++ b/packages/pinball_components/sandbox/lib/main.dart @@ -21,6 +21,7 @@ void main() { addChromeDinoStories(dashbook); addDashNestBumperStories(dashbook); addKickerStories(dashbook); + addPlungerStories(dashbook); addSlingshotStories(dashbook); addSparkyBumperStories(dashbook); addZoomStories(dashbook); 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/stories.dart b/packages/pinball_components/sandbox/lib/stories/stories.dart index 0a795ec7..7dd02878 100644 --- a/packages/pinball_components/sandbox/lib/stories/stories.dart +++ b/packages/pinball_components/sandbox/lib/stories/stories.dart @@ -8,6 +8,7 @@ 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/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); + }, + ); + }); + }); + }); +}