refactor: implemented `FlipperMovingBehavior` (#444)

pull/446/head
Alejandro Santiago 2 years ago committed by GitHub
parent 0ac9cb3140
commit 973375a9b3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -31,7 +31,6 @@ class GameBlocStatusListener extends Component
.descendants()
.whereType<Plunger>()
.forEach(_addPlungerBehaviors);
gameRef.overlays.remove(PinballGame.playButtonOverlay);
gameRef.overlays.remove(PinballGame.replayButtonOverlay);
break;
@ -43,7 +42,6 @@ class GameBlocStatusListener extends Component
.state
.characterTheme,
);
gameRef
.descendants()
.whereType<Flipper>()
@ -97,8 +95,8 @@ class GameBlocStatusListener extends Component
}
void _addFlipperBehaviors(Flipper flipper) => flipper
..add(FlipperKeyControllingBehavior())
..moveDown();
.firstChild<FlameBlocProvider<FlipperCubit, FlipperState>>()!
.add(FlipperKeyControllingBehavior());
void _removeFlipperBehaviors(Flipper flipper) => flipper
.descendants()

@ -169,13 +169,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<Flipper>().where((flipper) {
return flipper.side == focusedBoardSide[pointerId];
});
flippers.first.moveUp();
tappedLeftSide ? BoardSide.left : BoardSide.right;
final flippers = descendants()
.whereType<Flipper>()
.where((flipper) => flipper.side == focusedBoardSide[pointerId]);
for (final flipper in flippers) {
flipper
.descendants()
.whereType<FlameBlocProvider<FlipperCubit, FlipperState>>()
.forEach((provider) => provider.bloc.moveUp());
}
}
}
@ -196,11 +201,15 @@ class PinballGame extends PinballForge2DGame
void _moveFlippersDown(int pointerId) {
if (focusedBoardSide[pointerId] != null) {
final flippers = descendants().whereType<Flipper>().where((flipper) {
return flipper.side == focusedBoardSide[pointerId];
});
flippers.first.moveDown();
focusedBoardSide.remove(pointerId);
final flippers = descendants()
.whereType<Flipper>()
.where((flipper) => flipper.side == focusedBoardSide[pointerId]);
for (final flipper in flippers) {
flipper
.descendants()
.whereType<FlameBlocProvider<FlipperCubit, FlipperState>>()
.forEach((provider) => provider.bloc.moveDown());
}
}
}
}
@ -230,9 +239,7 @@ class DebugPinballGame extends PinballGame with FPSCounter, PanDetector {
@override
Future<void> onLoad() async {
await super.onLoad();
await add(PreviewLine());
await add(_DebugInformation());
await addAll([PreviewLine(), _DebugInformation()]);
}
@override
@ -247,14 +254,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) {

@ -1,2 +1,3 @@
export 'flipper_jointing_behavior.dart';
export 'flipper_key_controlling_behavior.dart';
export 'flipper_moving_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<Flipper> {
with KeyboardHandler, FlameBlocReader<FlipperCubit, FlipperState> {
/// 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<void> 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;

@ -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<FlipperCubit, FlipperState>,
FlameBlocReader<FlipperCubit, FlipperState> {
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<void> onLoad() async {
await super.onLoad();
_flipper = parent!.parent! as Flipper;
_moveDown();
}
}

@ -0,0 +1,11 @@
import 'package:bloc/bloc.dart';
part 'flipper_state.dart';
class FlipperCubit extends Cubit<FlipperState> {
FlipperCubit() : super(FlipperState.movingDown);
void moveUp() => emit(FlipperState.movingUp);
void moveDown() => emit(FlipperState.movingDown);
}

@ -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;
}

@ -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<FlipperCubit, FlipperState>(
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<FixtureDef> _createFixtureDefs() {
final direction = side.direction;

@ -5,11 +5,7 @@ part 'plunger_state.dart';
class PlungerCubit extends Cubit<PlungerState> {
PlungerCubit() : super(PlungerState.releasing);
void pulled() {
emit(PlungerState.pulling);
}
void pulled() => emit(PlungerState.pulling);
void released() {
emit(PlungerState.releasing);
}
void released() => emit(PlungerState.releasing);
}

@ -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(

@ -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<void> pump(
FlipperKeyControllingBehavior behavior, {
required BoardSide side,
FlipperCubit? flipperBloc,
}) async {
final flipper = Flipper.test(side: side);
await ensureAdd(flipper);
await flipper.ensureAdd(
FlameBlocProvider<FlipperCubit, FlipperState>.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<FlipperState>(
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<void>.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<void>.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<void>.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<void>.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<void>.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<void>.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<void>.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<void>.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);
},
);
});

@ -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<void> pump(
FlipperMovingBehavior behavior, {
FlipperCubit? flipperBloc,
}) async {
final flipper = Flipper.test(side: BoardSide.left);
await ensureAdd(flipper);
await flipper.ensureAdd(
FlameBlocProvider<FlipperCubit, FlipperState>.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<FlipperMovingBehavior>(),
);
});
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 vertical velocity to flipper when moving down',
(game) async {
final bloc = _MockFlipperCubit();
final streamController = StreamController<FlipperState>();
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<void>.delayed(Duration.zero);
final flipper = behavior.ancestors().whereType<Flipper>().single;
expect(flipper.body.linearVelocity.x, 0);
expect(flipper.body.linearVelocity.y, strength);
},
);
flameTester.test(
'applies vertical 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<Flipper>().single;
expect(flipper.body.linearVelocity.x, 0);
expect(flipper.body.linearVelocity.y, -strength);
},
);
});
}

@ -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<FlipperCubit>());
});
blocTest<FlipperCubit, FlipperState>(
'moves',
build: FlipperCubit.new,
act: (cubit) => cubit
..moveUp()
..moveDown(),
expect: () => [
FlipperState.movingUp,
FlipperState.movingDown,
],
);
});
}

@ -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);
},
);
});
}

@ -86,6 +86,8 @@ class _MockPlungerCubit extends Mock implements PlungerCubit {}
class _MockGoogleWordCubit extends Mock implements GoogleWordCubit {}
class _MockFlipperCubit extends Mock implements FlipperCubit {}
class _MockAppLocalizations extends Mock implements AppLocalizations {
@override
String get score => '';
@ -199,7 +201,12 @@ void main() {
final behavior = FlipperKeyControllingBehavior();
await game.pump([component, backbox, flipper]);
await flipper.ensureAdd(behavior);
await flipper.ensureAdd(
FlameBlocProvider<FlipperCubit, FlipperState>(
create: _MockFlipperCubit.new,
children: [behavior],
),
);
expect(state.status, GameStatus.gameOver);
component.onNewState(state);
@ -366,12 +373,18 @@ void main() {
final flipper = Flipper.test(side: BoardSide.left);
await game.pump([component, backbox, flipper]);
await flipper.ensureAdd(
FlameBlocProvider<FlipperCubit, FlipperState>(
create: _MockFlipperCubit.new,
),
);
component.onNewState(state);
await game.ready();
expect(
flipper.children
flipper
.descendants()
.whereType<FlipperKeyControllingBehavior>()
.length,
equals(1),

@ -259,13 +259,19 @@ void main() {
when(() => tapDownEvent.eventPosition).thenReturn(eventPosition);
when(() => tapDownEvent.raw).thenReturn(raw);
final flippers = game.descendants().whereType<Flipper>().where(
(flipper) => flipper.side == BoardSide.left,
);
game.onTapDown(0, tapDownEvent);
await Future<void>.delayed(Duration.zero);
expect(flippers.first.body.linearVelocity.y, isNegative);
final flipperBloc = game
.descendants()
.whereType<Flipper>()
.where((flipper) => flipper.side == BoardSide.left)
.single
.descendants()
.whereType<FlameBlocProvider<FlipperCubit, FlipperState>>()
.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<Flipper>().where(
(flipper) => flipper.side == BoardSide.right,
);
game.onTapDown(0, tapDownEvent);
final flipperBloc = game
.descendants()
.whereType<Flipper>()
.where((flipper) => flipper.side == BoardSide.right)
.single
.descendants()
.whereType<FlameBlocProvider<FlipperCubit, FlipperState>>()
.first
.bloc;
expect(flippers.first.body.linearVelocity.y, isNegative);
await Future<void>.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<Flipper>().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<Flipper>()
.where((flipper) => flipper.side == BoardSide.left)
.single
.descendants()
.whereType<FlameBlocProvider<FlipperCubit, FlipperState>>()
.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<Flipper>().where(
(flipper) => flipper.side == BoardSide.left,
);
final flipperBloc = game
.descendants()
.whereType<Flipper>()
.where((flipper) => flipper.side == BoardSide.left)
.single
.descendants()
.whereType<FlameBlocProvider<FlipperCubit, FlipperState>>()
.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<Flipper>();
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<Flipper>();
final rightFlipper = flippers.elementAt(0);
final leftFlipper = flippers.elementAt(1);
final leftFlipperBloc = leftFlipper
.descendants()
.whereType<FlameBlocProvider<FlipperCubit, FlipperState>>()
.first
.bloc;
final rightFlipperBloc = rightFlipper
.descendants()
.whereType<FlameBlocProvider<FlipperCubit, FlipperState>>()
.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();

Loading…
Cancel
Save