From 13cc5e1d6fa4931aeb48678e5e9fc584f7969c60 Mon Sep 17 00:00:00 2001 From: alestiago Date: Mon, 9 May 2022 19:37:34 +0100 Subject: [PATCH] refactor: implemented FlipperMovingBehavior --- .../components/game_bloc_status_listener.dart | 6 +- lib/game/pinball_game.dart | 43 ++-- .../flipper/behaviors/behaviors.dart | 1 + .../flipper_key_controlling_behavior.dart | 12 +- .../behaviors/flipper_moving_behavior.dart | 40 ++++ .../flipper/cubit/flipper_cubit.dart | 11 + .../flipper/cubit/flipper_state.dart | 11 + .../lib/src/components/flipper/flipper.dart | 23 +- .../plunger/cubit/plunger_cubit.dart | 8 +- .../flipper_jointing_behavior_test.dart | 5 +- ...flipper_key_controlling_behavior_test.dart | 210 ++++++++++++------ .../flipper_moving_behavior_test.dart | 101 +++++++++ .../flipper/cubit/flipper_cubit_test.dart | 23 ++ .../src/components/flipper/flipper_test.dart | 26 --- test/game/pinball_game_test.dart | 102 +++++---- 15 files changed, 428 insertions(+), 194 deletions(-) create mode 100644 packages/pinball_components/lib/src/components/flipper/behaviors/flipper_moving_behavior.dart create mode 100644 packages/pinball_components/lib/src/components/flipper/cubit/flipper_cubit.dart create mode 100644 packages/pinball_components/lib/src/components/flipper/cubit/flipper_state.dart create mode 100644 packages/pinball_components/test/src/components/flipper/behaviors/flipper_moving_behavior_test.dart create mode 100644 packages/pinball_components/test/src/components/flipper/cubit/flipper_cubit_test.dart diff --git a/lib/game/components/game_bloc_status_listener.dart b/lib/game/components/game_bloc_status_listener.dart index 359db070..560e5aa7 100644 --- a/lib/game/components/game_bloc_status_listener.dart +++ b/lib/game/components/game_bloc_status_listener.dart @@ -30,7 +30,6 @@ class GameBlocStatusListener extends Component .descendants() .whereType() .forEach(_addPlungerBehaviors); - gameRef.overlays.remove(PinballGame.playButtonOverlay); break; case GameStatus.gameOver: @@ -41,7 +40,6 @@ class GameBlocStatusListener extends Component .state .characterTheme, ); - gameRef .descendants() .whereType() @@ -86,8 +84,8 @@ class GameBlocStatusListener extends Component } void _addFlipperBehaviors(Flipper flipper) => flipper - ..add(FlipperKeyControllingBehavior()) - ..moveDown(); + .firstChild>()! + .add(FlipperKeyControllingBehavior()); void _removeFlipperBehaviors(Flipper flipper) => flipper .descendants() diff --git a/lib/game/pinball_game.dart b/lib/game/pinball_game.dart index 2250a8fa..84d2a6ae 100644 --- a/lib/game/pinball_game.dart +++ b/lib/game/pinball_game.dart @@ -166,13 +166,18 @@ class PinballGame extends PinballForge2DGame .bloc .pulled(); } else { - final leftSide = info.eventPosition.widget.x < canvasSize.x / 2; + final tappedLeftSide = info.eventPosition.widget.x < canvasSize.x / 2; focusedBoardSide[pointerId] = - leftSide ? BoardSide.left : BoardSide.right; - final flippers = descendants().whereType().where((flipper) { - return flipper.side == focusedBoardSide[pointerId]; - }); - flippers.first.moveUp(); + tappedLeftSide ? BoardSide.left : BoardSide.right; + final flippers = descendants() + .whereType() + .where((flipper) => flipper.side == focusedBoardSide[pointerId]); + for (final flipper in flippers) { + flipper + .descendants() + .whereType>() + .forEach((provider) => provider.bloc.moveUp()); + } } } @@ -193,11 +198,15 @@ class PinballGame extends PinballForge2DGame void _moveFlippersDown(int pointerId) { if (focusedBoardSide[pointerId] != null) { - final flippers = descendants().whereType().where((flipper) { - return flipper.side == focusedBoardSide[pointerId]; - }); - flippers.first.moveDown(); - focusedBoardSide.remove(pointerId); + final flippers = descendants() + .whereType() + .where((flipper) => flipper.side == focusedBoardSide[pointerId]); + for (final flipper in flippers) { + flipper + .descendants() + .whereType>() + .forEach((provider) => provider.bloc.moveDown()); + } } } } @@ -227,9 +236,7 @@ class DebugPinballGame extends PinballGame with FPSCounter, PanDetector { @override Future onLoad() async { await super.onLoad(); - await add(PreviewLine()); - - await add(_DebugInformation()); + await addAll([PreviewLine(), _DebugInformation()]); } @override @@ -244,14 +251,10 @@ class DebugPinballGame extends PinballGame with FPSCounter, PanDetector { } @override - void onPanStart(DragStartInfo info) { - lineStart = info.eventPosition.game; - } + void onPanStart(DragStartInfo info) => lineStart = info.eventPosition.game; @override - void onPanUpdate(DragUpdateInfo info) { - lineEnd = info.eventPosition.game; - } + void onPanUpdate(DragUpdateInfo info) => lineEnd = info.eventPosition.game; @override void onPanEnd(DragEndInfo info) { diff --git a/packages/pinball_components/lib/src/components/flipper/behaviors/behaviors.dart b/packages/pinball_components/lib/src/components/flipper/behaviors/behaviors.dart index ef3630e7..d3743ae9 100644 --- a/packages/pinball_components/lib/src/components/flipper/behaviors/behaviors.dart +++ b/packages/pinball_components/lib/src/components/flipper/behaviors/behaviors.dart @@ -1,2 +1,3 @@ export 'flipper_jointing_behavior.dart'; export 'flipper_key_controlling_behavior.dart'; +export 'flipper_moving_behavior.dart'; diff --git a/packages/pinball_components/lib/src/components/flipper/behaviors/flipper_key_controlling_behavior.dart b/packages/pinball_components/lib/src/components/flipper/behaviors/flipper_key_controlling_behavior.dart index ca4fcece..b002420a 100644 --- a/packages/pinball_components/lib/src/components/flipper/behaviors/flipper_key_controlling_behavior.dart +++ b/packages/pinball_components/lib/src/components/flipper/behaviors/flipper_key_controlling_behavior.dart @@ -1,11 +1,11 @@ import 'package:flame/components.dart'; +import 'package:flame_bloc/flame_bloc.dart'; import 'package:flutter/services.dart'; import 'package:pinball_components/pinball_components.dart'; -import 'package:pinball_flame/pinball_flame.dart'; /// Allows controlling the [Flipper]'s movement with keyboard input. class FlipperKeyControllingBehavior extends Component - with KeyboardHandler, ParentIsA { + with KeyboardHandler, FlameBlocReader { /// The [LogicalKeyboardKey]s that will control the [Flipper]. /// /// [onKeyEvent] method listens to when one of these keys is pressed. @@ -14,8 +14,8 @@ class FlipperKeyControllingBehavior extends Component @override Future onLoad() async { await super.onLoad(); - - switch (parent.side) { + final flipper = parent!.parent! as Flipper; + switch (flipper.side) { case BoardSide.left: _keys = [ LogicalKeyboardKey.arrowLeft, @@ -39,9 +39,9 @@ class FlipperKeyControllingBehavior extends Component if (!_keys.contains(event.logicalKey)) return true; if (event is RawKeyDownEvent) { - parent.moveUp(); + bloc.moveUp(); } else if (event is RawKeyUpEvent) { - parent.moveDown(); + bloc.moveDown(); } return false; diff --git a/packages/pinball_components/lib/src/components/flipper/behaviors/flipper_moving_behavior.dart b/packages/pinball_components/lib/src/components/flipper/behaviors/flipper_moving_behavior.dart new file mode 100644 index 00000000..13989192 --- /dev/null +++ b/packages/pinball_components/lib/src/components/flipper/behaviors/flipper_moving_behavior.dart @@ -0,0 +1,40 @@ +import 'package:flame/components.dart'; +import 'package:flame_bloc/flame_bloc.dart'; +import 'package:pinball_components/pinball_components.dart'; + +class FlipperMovingBehavior extends Component + with + FlameBlocListenable, + FlameBlocReader { + FlipperMovingBehavior({ + required double strength, + }) : assert(strength >= 0, "Strength can't be negative"), + _strength = strength; + + final double _strength; + + late final Flipper _flipper; + + void _moveUp() => _flipper.body.linearVelocity = Vector2(0, -_strength); + + void _moveDown() => _flipper.body.linearVelocity = Vector2(0, _strength); + + @override + void onNewState(FlipperState state) { + super.onNewState(state); + if (bloc.state.isMovingDown) _moveDown(); + } + + @override + void update(double dt) { + super.update(dt); + if (bloc.state.isMovingUp) _moveUp(); + } + + @override + Future onLoad() async { + await super.onLoad(); + _flipper = parent!.parent! as Flipper; + _moveDown(); + } +} diff --git a/packages/pinball_components/lib/src/components/flipper/cubit/flipper_cubit.dart b/packages/pinball_components/lib/src/components/flipper/cubit/flipper_cubit.dart new file mode 100644 index 00000000..21ddb2d4 --- /dev/null +++ b/packages/pinball_components/lib/src/components/flipper/cubit/flipper_cubit.dart @@ -0,0 +1,11 @@ +import 'package:bloc/bloc.dart'; + +part 'flipper_state.dart'; + +class FlipperCubit extends Cubit { + FlipperCubit() : super(FlipperState.movingDown); + + void moveUp() => emit(FlipperState.movingUp); + + void moveDown() => emit(FlipperState.movingDown); +} diff --git a/packages/pinball_components/lib/src/components/flipper/cubit/flipper_state.dart b/packages/pinball_components/lib/src/components/flipper/cubit/flipper_state.dart new file mode 100644 index 00000000..e14a495e --- /dev/null +++ b/packages/pinball_components/lib/src/components/flipper/cubit/flipper_state.dart @@ -0,0 +1,11 @@ +part of 'flipper_cubit.dart'; + +enum FlipperState { + movingDown, + movingUp, +} + +extension FlipperStateX on FlipperState { + bool get isMovingDown => this == FlipperState.movingDown; + bool get isMovingUp => this == FlipperState.movingUp; +} diff --git a/packages/pinball_components/lib/src/components/flipper/flipper.dart b/packages/pinball_components/lib/src/components/flipper/flipper.dart index 280c157f..265e7924 100644 --- a/packages/pinball_components/lib/src/components/flipper/flipper.dart +++ b/packages/pinball_components/lib/src/components/flipper/flipper.dart @@ -1,11 +1,13 @@ import 'dart:async'; import 'package:flame/components.dart'; +import 'package:flame_bloc/flame_bloc.dart'; import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flutter/foundation.dart'; import 'package:pinball_components/pinball_components.dart'; export 'behaviors/behaviors.dart'; +export 'cubit/flipper_cubit.dart'; /// {@template flipper} /// A bat, typically found in pairs at the bottom of the board. @@ -21,6 +23,10 @@ class Flipper extends BodyComponent with KeyboardHandler, InitialPosition { children: [ _FlipperSpriteComponent(side: side), FlipperJointingBehavior(), + FlameBlocProvider( + create: FlipperCubit.new, + children: [FlipperMovingBehavior(strength: 90)], + ), ], ); @@ -33,29 +39,12 @@ class Flipper extends BodyComponent with KeyboardHandler, InitialPosition { /// The size of the [Flipper]. static final size = Vector2(13.5, 4.3); - /// The speed required to move the [Flipper] to its highest position. - /// - /// The higher the value, the faster the [Flipper] will move. - static const double _speed = 90; - /// Whether the [Flipper] is on the left or right side of the board. /// /// A [Flipper] with [BoardSide.left] has a counter-clockwise arc motion, /// whereas a [Flipper] with [BoardSide.right] has a clockwise arc motion. final BoardSide side; - /// Applies downward linear velocity to the [Flipper], moving it to its - /// resting position. - void moveDown() { - body.linearVelocity = Vector2(0, _speed); - } - - /// Applies upward linear velocity to the [Flipper], moving it to its highest - /// position. - void moveUp() { - body.linearVelocity = Vector2(0, -_speed); - } - List _createFixtureDefs() { final direction = side.direction; diff --git a/packages/pinball_components/lib/src/components/plunger/cubit/plunger_cubit.dart b/packages/pinball_components/lib/src/components/plunger/cubit/plunger_cubit.dart index 601257d2..ce845197 100644 --- a/packages/pinball_components/lib/src/components/plunger/cubit/plunger_cubit.dart +++ b/packages/pinball_components/lib/src/components/plunger/cubit/plunger_cubit.dart @@ -5,11 +5,7 @@ part 'plunger_state.dart'; class PlungerCubit extends Cubit { PlungerCubit() : super(PlungerState.releasing); - void pulled() { - emit(PlungerState.pulling); - } + void pulled() => emit(PlungerState.pulling); - void released() { - emit(PlungerState.releasing); - } + void released() => emit(PlungerState.releasing); } diff --git a/packages/pinball_components/test/src/components/flipper/behaviors/flipper_jointing_behavior_test.dart b/packages/pinball_components/test/src/components/flipper/behaviors/flipper_jointing_behavior_test.dart index 70c93439..3128d286 100644 --- a/packages/pinball_components/test/src/components/flipper/behaviors/flipper_jointing_behavior_test.dart +++ b/packages/pinball_components/test/src/components/flipper/behaviors/flipper_jointing_behavior_test.dart @@ -1,15 +1,14 @@ // ignore_for_file: cascade_invocations +import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flame_test/flame_test.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:pinball_components/src/components/components.dart'; -import '../../../../helpers/helpers.dart'; - void main() { TestWidgetsFlutterBinding.ensureInitialized(); group('FlipperJointingBehavior', () { - final flameTester = FlameTester(TestGame.new); + final flameTester = FlameTester(Forge2DGame.new); test('can be instantiated', () { expect( diff --git a/packages/pinball_components/test/src/components/flipper/behaviors/flipper_key_controlling_behavior_test.dart b/packages/pinball_components/test/src/components/flipper/behaviors/flipper_key_controlling_behavior_test.dart index 6a6ac91c..307264f0 100644 --- a/packages/pinball_components/test/src/components/flipper/behaviors/flipper_key_controlling_behavior_test.dart +++ b/packages/pinball_components/test/src/components/flipper/behaviors/flipper_key_controlling_behavior_test.dart @@ -1,5 +1,7 @@ // ignore_for_file: cascade_invocations +import 'package:bloc_test/bloc_test.dart'; +import 'package:flame_bloc/flame_bloc.dart'; import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flame_test/flame_test.dart'; import 'package:flutter/foundation.dart'; @@ -9,6 +11,25 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:mocktail/mocktail.dart'; import 'package:pinball_components/pinball_components.dart'; +class _TestGame extends Forge2DGame { + Future pump( + FlipperKeyControllingBehavior behavior, { + required BoardSide side, + FlipperCubit? flipperBloc, + }) async { + final flipper = Flipper.test(side: side); + await ensureAdd(flipper); + await flipper.ensureAdd( + FlameBlocProvider.value( + value: flipperBloc ?? FlipperCubit(), + children: [behavior], + ), + ); + } +} + +class _MockFlipperCubit extends Mock implements FlipperCubit {} + class _MockRawKeyDownEvent extends Mock implements RawKeyDownEvent { @override String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) { @@ -26,26 +47,32 @@ class _MockRawKeyUpEvent extends Mock implements RawKeyUpEvent { void main() { TestWidgetsFlutterBinding.ensureInitialized(); group('FlipperKeyControllingBehavior', () { - final flameTester = FlameTester(Forge2DGame.new); + final flameTester = FlameTester(_TestGame.new); group( 'onKeyEvent', () { - late Flipper rightFlipper; - late Flipper leftFlipper; + late FlipperCubit flipperBloc; setUp(() { - rightFlipper = Flipper.test(side: BoardSide.right); - leftFlipper = Flipper.test(side: BoardSide.left); + flipperBloc = _MockFlipperCubit(); + whenListen( + flipperBloc, + const Stream.empty(), + initialState: FlipperState.movingDown, + ); }); group('on right Flipper', () { flameTester.test( 'moves upwards when right arrow is pressed', (game) async { - await game.ensureAdd(rightFlipper); final behavior = FlipperKeyControllingBehavior(); - await rightFlipper.ensureAdd(behavior); + await game.pump( + behavior, + flipperBloc: flipperBloc, + side: BoardSide.right, + ); final event = _MockRawKeyDownEvent(); when(() => event.logicalKey).thenReturn( @@ -54,17 +81,20 @@ void main() { behavior.onKeyEvent(event, {}); - expect(rightFlipper.body.linearVelocity.y, isNegative); - expect(rightFlipper.body.linearVelocity.x, isZero); + await Future.delayed(Duration.zero); + verify(flipperBloc.moveUp).called(1); }, ); flameTester.test( 'moves downwards when right arrow is released', (game) async { - await game.ensureAdd(rightFlipper); final behavior = FlipperKeyControllingBehavior(); - await rightFlipper.ensureAdd(behavior); + await game.pump( + behavior, + side: BoardSide.right, + flipperBloc: flipperBloc, + ); final event = _MockRawKeyUpEvent(); when(() => event.logicalKey).thenReturn( @@ -73,17 +103,20 @@ void main() { behavior.onKeyEvent(event, {}); - expect(rightFlipper.body.linearVelocity.y, isPositive); - expect(rightFlipper.body.linearVelocity.x, isZero); + await Future.delayed(Duration.zero); + verify(flipperBloc.moveDown).called(1); }, ); flameTester.test( 'moves upwards when D is pressed', (game) async { - await game.ensureAdd(rightFlipper); final behavior = FlipperKeyControllingBehavior(); - await rightFlipper.ensureAdd(behavior); + await game.pump( + behavior, + side: BoardSide.right, + flipperBloc: flipperBloc, + ); final event = _MockRawKeyDownEvent(); when(() => event.logicalKey).thenReturn( @@ -92,17 +125,20 @@ void main() { behavior.onKeyEvent(event, {}); - expect(rightFlipper.body.linearVelocity.y, isNegative); - expect(rightFlipper.body.linearVelocity.x, isZero); + await Future.delayed(Duration.zero); + verify(flipperBloc.moveUp).called(1); }, ); flameTester.test( 'moves downwards when D is released', (game) async { - await game.ensureAdd(rightFlipper); final behavior = FlipperKeyControllingBehavior(); - await rightFlipper.ensureAdd(behavior); + await game.pump( + behavior, + side: BoardSide.right, + flipperBloc: flipperBloc, + ); final event = _MockRawKeyUpEvent(); when(() => event.logicalKey).thenReturn( @@ -111,8 +147,8 @@ void main() { behavior.onKeyEvent(event, {}); - expect(rightFlipper.body.linearVelocity.y, isPositive); - expect(rightFlipper.body.linearVelocity.x, isZero); + await Future.delayed(Duration.zero); + verify(flipperBloc.moveDown).called(1); }, ); @@ -120,9 +156,12 @@ void main() { flameTester.test( 'left arrow is pressed', (game) async { - await game.ensureAdd(rightFlipper); final behavior = FlipperKeyControllingBehavior(); - await rightFlipper.ensureAdd(behavior); + await game.pump( + behavior, + side: BoardSide.right, + flipperBloc: flipperBloc, + ); final event = _MockRawKeyDownEvent(); when(() => event.logicalKey).thenReturn( @@ -131,17 +170,20 @@ void main() { behavior.onKeyEvent(event, {}); - expect(rightFlipper.body.linearVelocity.y, isZero); - expect(rightFlipper.body.linearVelocity.x, isZero); + verifyNever(flipperBloc.moveDown); + verifyNever(flipperBloc.moveUp); }, ); flameTester.test( 'left arrow is released', (game) async { - await game.ensureAdd(rightFlipper); final behavior = FlipperKeyControllingBehavior(); - await rightFlipper.ensureAdd(behavior); + await game.pump( + behavior, + side: BoardSide.right, + flipperBloc: flipperBloc, + ); final event = _MockRawKeyUpEvent(); when(() => event.logicalKey).thenReturn( @@ -150,17 +192,20 @@ void main() { behavior.onKeyEvent(event, {}); - expect(rightFlipper.body.linearVelocity.y, isZero); - expect(rightFlipper.body.linearVelocity.x, isZero); + verifyNever(flipperBloc.moveDown); + verifyNever(flipperBloc.moveUp); }, ); flameTester.test( 'A is pressed', (game) async { - await game.ensureAdd(rightFlipper); final behavior = FlipperKeyControllingBehavior(); - await rightFlipper.ensureAdd(behavior); + await game.pump( + behavior, + side: BoardSide.right, + flipperBloc: flipperBloc, + ); final event = _MockRawKeyDownEvent(); when(() => event.logicalKey).thenReturn( @@ -169,17 +214,20 @@ void main() { behavior.onKeyEvent(event, {}); - expect(rightFlipper.body.linearVelocity.y, isZero); - expect(rightFlipper.body.linearVelocity.x, isZero); + verifyNever(flipperBloc.moveDown); + verifyNever(flipperBloc.moveUp); }, ); flameTester.test( 'A is released', (game) async { - await game.ensureAdd(rightFlipper); final behavior = FlipperKeyControllingBehavior(); - await rightFlipper.ensureAdd(behavior); + await game.pump( + behavior, + side: BoardSide.right, + flipperBloc: flipperBloc, + ); final event = _MockRawKeyUpEvent(); when(() => event.logicalKey).thenReturn( @@ -188,8 +236,8 @@ void main() { behavior.onKeyEvent(event, {}); - expect(rightFlipper.body.linearVelocity.y, isZero); - expect(rightFlipper.body.linearVelocity.x, isZero); + verifyNever(flipperBloc.moveDown); + verifyNever(flipperBloc.moveUp); }, ); }); @@ -199,9 +247,12 @@ void main() { flameTester.test( 'moves upwards when left arrow is pressed', (game) async { - await game.ensureAdd(leftFlipper); final behavior = FlipperKeyControllingBehavior(); - await leftFlipper.ensureAdd(behavior); + await game.pump( + behavior, + side: BoardSide.left, + flipperBloc: flipperBloc, + ); final event = _MockRawKeyDownEvent(); when(() => event.logicalKey).thenReturn( @@ -210,17 +261,20 @@ void main() { behavior.onKeyEvent(event, {}); - expect(leftFlipper.body.linearVelocity.y, isNegative); - expect(leftFlipper.body.linearVelocity.x, isZero); + await Future.delayed(Duration.zero); + verify(flipperBloc.moveUp).called(1); }, ); flameTester.test( 'moves downwards when left arrow is released', (game) async { - await game.ensureAdd(leftFlipper); final behavior = FlipperKeyControllingBehavior(); - await leftFlipper.ensureAdd(behavior); + await game.pump( + behavior, + side: BoardSide.left, + flipperBloc: flipperBloc, + ); final event = _MockRawKeyUpEvent(); when(() => event.logicalKey).thenReturn( @@ -229,17 +283,20 @@ void main() { behavior.onKeyEvent(event, {}); - expect(leftFlipper.body.linearVelocity.y, isPositive); - expect(leftFlipper.body.linearVelocity.x, isZero); + await Future.delayed(Duration.zero); + verify(flipperBloc.moveDown).called(1); }, ); flameTester.test( 'moves upwards when A is pressed', (game) async { - await game.ensureAdd(leftFlipper); final behavior = FlipperKeyControllingBehavior(); - await leftFlipper.ensureAdd(behavior); + await game.pump( + behavior, + side: BoardSide.left, + flipperBloc: flipperBloc, + ); final event = _MockRawKeyDownEvent(); when(() => event.logicalKey).thenReturn( @@ -248,17 +305,20 @@ void main() { behavior.onKeyEvent(event, {}); - expect(leftFlipper.body.linearVelocity.y, isNegative); - expect(leftFlipper.body.linearVelocity.x, isZero); + await Future.delayed(Duration.zero); + verify(flipperBloc.moveUp).called(1); }, ); flameTester.test( 'moves downwards when A is released', (game) async { - await game.ensureAdd(leftFlipper); final behavior = FlipperKeyControllingBehavior(); - await leftFlipper.ensureAdd(behavior); + await game.pump( + behavior, + side: BoardSide.left, + flipperBloc: flipperBloc, + ); final event = _MockRawKeyUpEvent(); when(() => event.logicalKey).thenReturn( @@ -267,8 +327,8 @@ void main() { behavior.onKeyEvent(event, {}); - expect(leftFlipper.body.linearVelocity.y, isPositive); - expect(leftFlipper.body.linearVelocity.x, isZero); + await Future.delayed(Duration.zero); + verify(flipperBloc.moveDown).called(1); }, ); @@ -276,9 +336,12 @@ void main() { flameTester.test( 'right arrow is pressed', (game) async { - await game.ensureAdd(leftFlipper); final behavior = FlipperKeyControllingBehavior(); - await leftFlipper.ensureAdd(behavior); + await game.pump( + behavior, + side: BoardSide.left, + flipperBloc: flipperBloc, + ); final event = _MockRawKeyDownEvent(); when(() => event.logicalKey).thenReturn( @@ -287,17 +350,20 @@ void main() { behavior.onKeyEvent(event, {}); - expect(leftFlipper.body.linearVelocity.y, isZero); - expect(leftFlipper.body.linearVelocity.x, isZero); + verifyNever(flipperBloc.moveDown); + verifyNever(flipperBloc.moveUp); }, ); flameTester.test( 'right arrow is released', (game) async { - await game.ensureAdd(leftFlipper); final behavior = FlipperKeyControllingBehavior(); - await leftFlipper.ensureAdd(behavior); + await game.pump( + behavior, + side: BoardSide.left, + flipperBloc: flipperBloc, + ); final event = _MockRawKeyUpEvent(); when(() => event.logicalKey).thenReturn( @@ -306,17 +372,20 @@ void main() { behavior.onKeyEvent(event, {}); - expect(leftFlipper.body.linearVelocity.y, isZero); - expect(leftFlipper.body.linearVelocity.x, isZero); + verifyNever(flipperBloc.moveDown); + verifyNever(flipperBloc.moveUp); }, ); flameTester.test( 'D is pressed', (game) async { - await game.ensureAdd(leftFlipper); final behavior = FlipperKeyControllingBehavior(); - await leftFlipper.ensureAdd(behavior); + await game.pump( + behavior, + side: BoardSide.left, + flipperBloc: flipperBloc, + ); final event = _MockRawKeyDownEvent(); when(() => event.logicalKey).thenReturn( @@ -325,17 +394,20 @@ void main() { behavior.onKeyEvent(event, {}); - expect(leftFlipper.body.linearVelocity.y, isZero); - expect(leftFlipper.body.linearVelocity.x, isZero); + verifyNever(flipperBloc.moveDown); + verifyNever(flipperBloc.moveUp); }, ); flameTester.test( 'D is released', (game) async { - await game.ensureAdd(leftFlipper); final behavior = FlipperKeyControllingBehavior(); - await leftFlipper.ensureAdd(behavior); + await game.pump( + behavior, + side: BoardSide.left, + flipperBloc: flipperBloc, + ); final event = _MockRawKeyUpEvent(); when(() => event.logicalKey).thenReturn( @@ -344,8 +416,8 @@ void main() { behavior.onKeyEvent(event, {}); - expect(leftFlipper.body.linearVelocity.y, isZero); - expect(leftFlipper.body.linearVelocity.x, isZero); + verifyNever(flipperBloc.moveDown); + verifyNever(flipperBloc.moveUp); }, ); }); diff --git a/packages/pinball_components/test/src/components/flipper/behaviors/flipper_moving_behavior_test.dart b/packages/pinball_components/test/src/components/flipper/behaviors/flipper_moving_behavior_test.dart new file mode 100644 index 00000000..3fed2956 --- /dev/null +++ b/packages/pinball_components/test/src/components/flipper/behaviors/flipper_moving_behavior_test.dart @@ -0,0 +1,101 @@ +// ignore_for_file: avoid_dynamic_calls, cascade_invocations + +import 'dart:async'; + +import 'package:bloc_test/bloc_test.dart'; +import 'package:flame_bloc/flame_bloc.dart'; +import 'package:flame_forge2d/flame_forge2d.dart'; +import 'package:flame_test/flame_test.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mocktail/mocktail.dart'; +import 'package:pinball_components/pinball_components.dart'; + +class _TestGame extends Forge2DGame { + Future pump( + FlipperMovingBehavior behavior, { + FlipperCubit? flipperBloc, + }) async { + final flipper = Flipper.test(side: BoardSide.left); + await ensureAdd(flipper); + await flipper.ensureAdd( + FlameBlocProvider.value( + value: flipperBloc ?? FlipperCubit(), + children: [behavior], + ), + ); + } +} + +class _MockFlipperCubit extends Mock implements FlipperCubit {} + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + final flameTester = FlameTester(_TestGame.new); + + group('FlipperMovingBehavior', () { + test('can be instantiated', () { + expect( + FlipperMovingBehavior(strength: 0), + isA(), + ); + }); + + test('throws assertion error when strength is negative', () { + expect( + () => FlipperMovingBehavior(strength: -1), + throwsAssertionError, + ); + }); + + flameTester.test('can be loaded', (game) async { + final behavior = FlipperMovingBehavior(strength: 0); + await game.pump(behavior); + expect(game.descendants(), contains(behavior)); + }); + + flameTester.test( + 'applies horizontal velocity to flipper when moving down', + (game) async { + final bloc = _MockFlipperCubit(); + final streamController = StreamController(); + whenListen( + bloc, + streamController.stream, + initialState: FlipperState.movingUp, + ); + + const strength = 10.0; + final behavior = FlipperMovingBehavior(strength: strength); + await game.pump(behavior, flipperBloc: bloc); + + streamController.add(FlipperState.movingDown); + await Future.delayed(Duration.zero); + + final flipper = behavior.ancestors().whereType().single; + expect(flipper.body.linearVelocity.x, 0); + expect(flipper.body.linearVelocity.y, strength); + }, + ); + + flameTester.test( + 'applies horizontal velocity to flipper when moving up', + (game) async { + final bloc = _MockFlipperCubit(); + whenListen( + bloc, + Stream.value(FlipperState.movingUp), + initialState: FlipperState.movingUp, + ); + + const strength = 10.0; + final behavior = FlipperMovingBehavior(strength: strength); + await game.pump(behavior, flipperBloc: bloc); + game.update(0); + + final flipper = behavior.ancestors().whereType().single; + expect(flipper.body.linearVelocity.x, 0); + expect(flipper.body.linearVelocity.y, -strength); + }, + ); + }); +} diff --git a/packages/pinball_components/test/src/components/flipper/cubit/flipper_cubit_test.dart b/packages/pinball_components/test/src/components/flipper/cubit/flipper_cubit_test.dart new file mode 100644 index 00000000..6cd9c591 --- /dev/null +++ b/packages/pinball_components/test/src/components/flipper/cubit/flipper_cubit_test.dart @@ -0,0 +1,23 @@ +import 'package:bloc_test/bloc_test.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:pinball_components/pinball_components.dart'; + +void main() { + group('FlipperCubit', () { + test('can be instantiated', () { + expect(FlipperCubit(), isA()); + }); + + blocTest( + 'moves', + build: FlipperCubit.new, + act: (cubit) => cubit + ..moveUp() + ..moveDown(), + expect: () => [ + FlipperState.movingUp, + FlipperState.movingDown, + ], + ); + }); +} diff --git a/packages/pinball_components/test/src/components/flipper/flipper_test.dart b/packages/pinball_components/test/src/components/flipper/flipper_test.dart index 4569f3ec..0aba00bc 100644 --- a/packages/pinball_components/test/src/components/flipper/flipper_test.dart +++ b/packages/pinball_components/test/src/components/flipper/flipper_test.dart @@ -128,31 +128,5 @@ void main() { }, ); }); - - flameTester.test( - 'moveDown applies downward velocity', - (game) async { - final flipper = Flipper(side: BoardSide.left); - await game.ensureAdd(flipper); - - expect(flipper.body.linearVelocity, equals(Vector2.zero())); - flipper.moveDown(); - - expect(flipper.body.linearVelocity.y, isPositive); - }, - ); - - flameTester.test( - 'moveUp applies upward velocity', - (game) async { - final flipper = Flipper(side: BoardSide.left); - await game.ensureAdd(flipper); - - expect(flipper.body.linearVelocity, equals(Vector2.zero())); - flipper.moveUp(); - - expect(flipper.body.linearVelocity.y, isNegative); - }, - ); }); } diff --git a/test/game/pinball_game_test.dart b/test/game/pinball_game_test.dart index a95f329b..b60ebbad 100644 --- a/test/game/pinball_game_test.dart +++ b/test/game/pinball_game_test.dart @@ -259,13 +259,19 @@ void main() { when(() => tapDownEvent.eventPosition).thenReturn(eventPosition); when(() => tapDownEvent.raw).thenReturn(raw); - final flippers = game.descendants().whereType().where( - (flipper) => flipper.side == BoardSide.left, - ); - game.onTapDown(0, tapDownEvent); + await Future.delayed(Duration.zero); - expect(flippers.first.body.linearVelocity.y, isNegative); + final flipperBloc = game + .descendants() + .whereType() + .where((flipper) => flipper.side == BoardSide.left) + .single + .descendants() + .whereType>() + .first + .bloc; + expect(flipperBloc.state, FlipperState.movingUp); }); flameTester.test('tap down moves right flipper up', (game) async { @@ -282,13 +288,19 @@ void main() { when(() => tapDownEvent.eventPosition).thenReturn(eventPosition); when(() => tapDownEvent.raw).thenReturn(raw); - final flippers = game.descendants().whereType().where( - (flipper) => flipper.side == BoardSide.right, - ); - game.onTapDown(0, tapDownEvent); + final flipperBloc = game + .descendants() + .whereType() + .where((flipper) => flipper.side == BoardSide.right) + .single + .descendants() + .whereType>() + .first + .bloc; - expect(flippers.first.body.linearVelocity.y, isNegative); + await Future.delayed(Duration.zero); + expect(flipperBloc.state, FlipperState.movingUp); }); flameTester.test('tap up moves flipper down', (game) async { @@ -298,28 +310,22 @@ void main() { when(() => eventPosition.game).thenReturn(Vector2.zero()); when(() => eventPosition.widget).thenReturn(Vector2.zero()); - final raw = _MockTapDownDetails(); - when(() => raw.kind).thenReturn(PointerDeviceKind.touch); - - final tapDownEvent = _MockTapDownInfo(); - when(() => tapDownEvent.eventPosition).thenReturn(eventPosition); - when(() => tapDownEvent.raw).thenReturn(raw); - - final flippers = game.descendants().whereType().where( - (flipper) => flipper.side == BoardSide.left, - ); - - game.onTapDown(0, tapDownEvent); - - expect(flippers.first.body.linearVelocity.y, isNegative); - final tapUpEvent = _MockTapUpInfo(); when(() => tapUpEvent.eventPosition).thenReturn(eventPosition); game.onTapUp(0, tapUpEvent); await game.ready(); - expect(flippers.first.body.linearVelocity.y, isPositive); + final flipperBloc = game + .descendants() + .whereType() + .where((flipper) => flipper.side == BoardSide.left) + .single + .descendants() + .whereType>() + .first + .bloc; + expect(flipperBloc.state, FlipperState.movingDown); }); flameTester.test('tap cancel moves flipper down', (game) async { @@ -336,17 +342,19 @@ void main() { when(() => tapDownEvent.eventPosition).thenReturn(eventPosition); when(() => tapDownEvent.raw).thenReturn(raw); - final flippers = game.descendants().whereType().where( - (flipper) => flipper.side == BoardSide.left, - ); + final flipperBloc = game + .descendants() + .whereType() + .where((flipper) => flipper.side == BoardSide.left) + .single + .descendants() + .whereType>() + .first + .bloc; game.onTapDown(0, tapDownEvent); - - expect(flippers.first.body.linearVelocity.y, isNegative); - game.onTapCancel(0); - - expect(flippers.first.body.linearVelocity.y, isPositive); + expect(flipperBloc.state, FlipperState.movingDown); }); flameTester.test( @@ -375,17 +383,25 @@ void main() { .thenReturn(rightEventPosition); when(() => rightTapDownEvent.raw).thenReturn(raw); - final flippers = game.descendants().whereType(); - final rightFlipper = flippers.elementAt(0); - final leftFlipper = flippers.elementAt(1); - game.onTapDown(0, leftTapDownEvent); game.onTapDown(1, rightTapDownEvent); - expect(leftFlipper.body.linearVelocity.y, isNegative); - expect(leftFlipper.side, equals(BoardSide.left)); - expect(rightFlipper.body.linearVelocity.y, isNegative); - expect(rightFlipper.side, equals(BoardSide.right)); + final flippers = game.descendants().whereType(); + final rightFlipper = flippers.elementAt(0); + final leftFlipper = flippers.elementAt(1); + final leftFlipperBloc = leftFlipper + .descendants() + .whereType>() + .first + .bloc; + final rightFlipperBloc = rightFlipper + .descendants() + .whereType>() + .first + .bloc; + + expect(leftFlipperBloc.state, equals(FlipperState.movingUp)); + expect(rightFlipperBloc.state, equals(FlipperState.movingUp)); expect( game.focusedBoardSide, @@ -396,7 +412,7 @@ void main() { }); group('plunger control', () { - flameTester.test('tap down emits plunging', (game) async { + flameTester.test('plunger control tap down emits plunging', (game) async { await game.ready(); final eventPosition = _MockEventPosition();