From f38f18d60af448161f0c6f3a8b4b2e2e58a5c920 Mon Sep 17 00:00:00 2001 From: Alejandro Santiago Date: Wed, 4 May 2022 11:15:17 +0100 Subject: [PATCH] refactor: moved noise logic to `BumperNoisyBehavior` (#326) --- lib/game/behaviors/behaviors.dart | 2 + lib/game/behaviors/bumper_noisy_behavior.dart | 14 +++++ .../scoring_behavior.dart | 22 +------ .../android_acres/android_acres.dart | 11 ++-- lib/game/components/bottom_group.dart | 1 + lib/game/components/components.dart | 1 - .../components/dino_desert/dino_desert.dart | 1 + .../flutter_forest/flutter_forest.dart | 14 +++-- .../components/google_word/google_word.dart | 2 +- lib/game/components/sparky_scorch.dart | 12 ++-- .../lib/src/components/ball/ball.dart | 8 +-- .../behaviors/bumper_noisy_behavior_test.dart | 50 +++++++++++++++ .../scoring_behavior_test.dart | 63 +------------------ .../android_acres/android_acres_test.dart | 22 ++++++- .../dino_desert/dino_desert_test.dart | 1 + .../flutter_forest/flutter_forest_test.dart | 16 +++++ test/game/components/sparky_scorch_test.dart | 15 +++++ test/game/pinball_game_test.dart | 28 +++++---- 18 files changed, 165 insertions(+), 118 deletions(-) create mode 100644 lib/game/behaviors/behaviors.dart create mode 100644 lib/game/behaviors/bumper_noisy_behavior.dart rename lib/game/{components => behaviors}/scoring_behavior.dart (61%) create mode 100644 test/game/behaviors/bumper_noisy_behavior_test.dart rename test/game/{components => behaviors}/scoring_behavior_test.dart (65%) diff --git a/lib/game/behaviors/behaviors.dart b/lib/game/behaviors/behaviors.dart new file mode 100644 index 00000000..ae51fc09 --- /dev/null +++ b/lib/game/behaviors/behaviors.dart @@ -0,0 +1,2 @@ +export 'bumper_noisy_behavior.dart'; +export 'scoring_behavior.dart'; diff --git a/lib/game/behaviors/bumper_noisy_behavior.dart b/lib/game/behaviors/bumper_noisy_behavior.dart new file mode 100644 index 00000000..c837c8c5 --- /dev/null +++ b/lib/game/behaviors/bumper_noisy_behavior.dart @@ -0,0 +1,14 @@ +// ignore_for_file: public_member_api_docs + +import 'package:flame/components.dart'; +import 'package:flame_forge2d/flame_forge2d.dart'; +import 'package:pinball/game/pinball_game.dart'; +import 'package:pinball_flame/pinball_flame.dart'; + +class BumperNoisyBehavior extends ContactBehavior with HasGameRef { + @override + void beginContact(Object other, Contact contact) { + super.beginContact(other, contact); + gameRef.audio.bumper(); + } +} diff --git a/lib/game/components/scoring_behavior.dart b/lib/game/behaviors/scoring_behavior.dart similarity index 61% rename from lib/game/components/scoring_behavior.dart rename to lib/game/behaviors/scoring_behavior.dart index f741e213..119efe6f 100644 --- a/lib/game/components/scoring_behavior.dart +++ b/lib/game/behaviors/scoring_behavior.dart @@ -7,7 +7,7 @@ import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_flame/pinball_flame.dart'; /// {@template scoring_behavior} -/// Adds points to the score when the ball contacts the [parent]. +/// Adds points to the score when the [Ball] contacts the [parent]. /// {@endtemplate} class ScoringBehavior extends ContactBehavior with HasGameRef { /// {@macro scoring_behavior} @@ -31,23 +31,3 @@ class ScoringBehavior extends ContactBehavior with HasGameRef { ); } } - -/// {@template bumper_scoring_behavior} -/// A specific [ScoringBehavior] used for Bumpers. -/// In addition to its parent logic, also plays the -/// SFX for bumpers -/// {@endtemplate} -class BumperScoringBehavior extends ScoringBehavior { - /// {@macro bumper_scoring_behavior} - BumperScoringBehavior({ - required Points points, - }) : super(points: points); - - @override - void beginContact(Object other, Contact contact) { - super.beginContact(other, contact); - if (other is! Ball) return; - - gameRef.audio.bumper(); - } -} diff --git a/lib/game/components/android_acres/android_acres.dart b/lib/game/components/android_acres/android_acres.dart index d29f38d7..032c5b22 100644 --- a/lib/game/components/android_acres/android_acres.dart +++ b/lib/game/components/android_acres/android_acres.dart @@ -2,8 +2,8 @@ import 'package:flame/components.dart'; import 'package:flutter/material.dart'; +import 'package:pinball/game/behaviors/behaviors.dart'; import 'package:pinball/game/components/android_acres/behaviors/behaviors.dart'; -import 'package:pinball/game/game.dart'; import 'package:pinball_components/pinball_components.dart'; /// {@template android_acres} @@ -25,17 +25,20 @@ class AndroidAcres extends Component { )..initialPosition = Vector2(-26, -28.25), AndroidBumper.a( children: [ - BumperScoringBehavior(points: Points.twentyThousand), + ScoringBehavior(points: Points.twentyThousand), + BumperNoisyBehavior(), ], )..initialPosition = Vector2(-25, 1.3), AndroidBumper.b( children: [ - BumperScoringBehavior(points: Points.twentyThousand), + ScoringBehavior(points: Points.twentyThousand), + BumperNoisyBehavior(), ], )..initialPosition = Vector2(-32.8, -9.2), AndroidBumper.cow( children: [ - BumperScoringBehavior(points: Points.twentyThousand), + ScoringBehavior(points: Points.twentyThousand), + BumperNoisyBehavior(), ], )..initialPosition = Vector2(-20.5, -13.8), AndroidSpaceshipBonusBehavior(), diff --git a/lib/game/components/bottom_group.dart b/lib/game/components/bottom_group.dart index c13f21be..8def273f 100644 --- a/lib/game/components/bottom_group.dart +++ b/lib/game/components/bottom_group.dart @@ -1,4 +1,5 @@ import 'package:flame/components.dart'; +import 'package:pinball/game/behaviors/behaviors.dart'; import 'package:pinball/game/game.dart'; import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_flame/pinball_flame.dart'; diff --git a/lib/game/components/components.dart b/lib/game/components/components.dart index b0b81239..19784226 100644 --- a/lib/game/components/components.dart +++ b/lib/game/components/components.dart @@ -12,5 +12,4 @@ export 'google_word/google_word.dart'; export 'launcher.dart'; export 'multiballs/multiballs.dart'; export 'multipliers/multipliers.dart'; -export 'scoring_behavior.dart'; export 'sparky_scorch.dart'; diff --git a/lib/game/components/dino_desert/dino_desert.dart b/lib/game/components/dino_desert/dino_desert.dart index 4d8cd7b6..e415c173 100644 --- a/lib/game/components/dino_desert/dino_desert.dart +++ b/lib/game/components/dino_desert/dino_desert.dart @@ -1,6 +1,7 @@ import 'package:flame/components.dart'; import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flutter/material.dart'; +import 'package:pinball/game/behaviors/behaviors.dart'; import 'package:pinball/game/components/dino_desert/behaviors/behaviors.dart'; import 'package:pinball/game/game.dart'; import 'package:pinball_components/pinball_components.dart'; diff --git a/lib/game/components/flutter_forest/flutter_forest.dart b/lib/game/components/flutter_forest/flutter_forest.dart index 0f24a3b6..f2b93d00 100644 --- a/lib/game/components/flutter_forest/flutter_forest.dart +++ b/lib/game/components/flutter_forest/flutter_forest.dart @@ -2,8 +2,8 @@ import 'package:flame/components.dart'; import 'package:flutter/material.dart'; +import 'package:pinball/game/behaviors/behaviors.dart'; import 'package:pinball/game/components/flutter_forest/behaviors/behaviors.dart'; -import 'package:pinball/game/game.dart'; import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_flame/pinball_flame.dart'; @@ -18,22 +18,26 @@ class FlutterForest extends Component with ZIndex { children: [ Signpost( children: [ - BumperScoringBehavior(points: Points.fiveThousand), + ScoringBehavior(points: Points.fiveThousand), + BumperNoisyBehavior(), ], )..initialPosition = Vector2(8.35, -58.3), DashNestBumper.main( children: [ - BumperScoringBehavior(points: Points.twoHundredThousand), + ScoringBehavior(points: Points.twoHundredThousand), + BumperNoisyBehavior(), ], )..initialPosition = Vector2(18.55, -59.35), DashNestBumper.a( children: [ - BumperScoringBehavior(points: Points.twentyThousand), + ScoringBehavior(points: Points.twentyThousand), + BumperNoisyBehavior(), ], )..initialPosition = Vector2(8.95, -51.95), DashNestBumper.b( children: [ - BumperScoringBehavior(points: Points.twentyThousand), + ScoringBehavior(points: Points.twentyThousand), + BumperNoisyBehavior(), ], )..initialPosition = Vector2(22.3, -46.75), DashAnimatronic()..position = Vector2(20, -66), diff --git a/lib/game/components/google_word/google_word.dart b/lib/game/components/google_word/google_word.dart index af1faea9..a2f6470a 100644 --- a/lib/game/components/google_word/google_word.dart +++ b/lib/game/components/google_word/google_word.dart @@ -1,7 +1,7 @@ import 'package:flame/components.dart'; import 'package:flutter/material.dart'; +import 'package:pinball/game/behaviors/scoring_behavior.dart'; import 'package:pinball/game/components/google_word/behaviors/behaviors.dart'; -import 'package:pinball/game/game.dart'; import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_flame/pinball_flame.dart'; diff --git a/lib/game/components/sparky_scorch.dart b/lib/game/components/sparky_scorch.dart index f98f71d7..7ce83c7a 100644 --- a/lib/game/components/sparky_scorch.dart +++ b/lib/game/components/sparky_scorch.dart @@ -2,7 +2,8 @@ import 'package:flame/components.dart'; import 'package:flame_forge2d/flame_forge2d.dart'; -import 'package:pinball/game/game.dart'; +import 'package:pinball/game/behaviors/behaviors.dart'; +import 'package:pinball/game/components/components.dart'; import 'package:pinball_components/pinball_components.dart'; /// {@template sparky_scorch} @@ -16,17 +17,20 @@ class SparkyScorch extends Component { children: [ SparkyBumper.a( children: [ - BumperScoringBehavior(points: Points.twentyThousand), + ScoringBehavior(points: Points.twentyThousand), + BumperNoisyBehavior(), ], )..initialPosition = Vector2(-22.9, -41.65), SparkyBumper.b( children: [ - BumperScoringBehavior(points: Points.twentyThousand), + ScoringBehavior(points: Points.twentyThousand), + BumperNoisyBehavior(), ], )..initialPosition = Vector2(-21.25, -57.9), SparkyBumper.c( children: [ - BumperScoringBehavior(points: Points.twentyThousand), + ScoringBehavior(points: Points.twentyThousand), + BumperNoisyBehavior(), ], )..initialPosition = Vector2(-3.3, -52.55), SparkyComputerSensor()..initialPosition = Vector2(-13, -49.9), diff --git a/packages/pinball_components/lib/src/components/ball/ball.dart b/packages/pinball_components/lib/src/components/ball/ball.dart index 456dd4f1..9234e69c 100644 --- a/packages/pinball_components/lib/src/components/ball/ball.dart +++ b/packages/pinball_components/lib/src/components/ball/ball.dart @@ -49,17 +49,13 @@ class Ball extends BodyComponent with Layered, InitialPosition, ZIndex { @override Body createBody() { final shape = CircleShape()..radius = size.x / 2; - final fixtureDef = FixtureDef( - shape, - density: 1, - ); final bodyDef = BodyDef( position: initialPosition, - userData: this, type: BodyType.dynamic, + userData: this, ); - return world.createBody(bodyDef)..createFixture(fixtureDef); + return world.createBody(bodyDef)..createFixtureFromShape(shape, 1); } /// Immediatly and completly [stop]s the ball. diff --git a/test/game/behaviors/bumper_noisy_behavior_test.dart b/test/game/behaviors/bumper_noisy_behavior_test.dart new file mode 100644 index 00000000..b288e4c6 --- /dev/null +++ b/test/game/behaviors/bumper_noisy_behavior_test.dart @@ -0,0 +1,50 @@ +// 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:mocktail/mocktail.dart'; +import 'package:pinball/game/behaviors/behaviors.dart'; +import 'package:pinball_audio/pinball_audio.dart'; + +import '../../helpers/helpers.dart'; + +class _TestBodyComponent extends BodyComponent { + @override + Body createBody() { + return world.createBody(BodyDef()); + } +} + +class _MockPinballAudio extends Mock implements PinballAudio {} + +class _MockContact extends Mock implements Contact {} + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + group('BumperNoisyBehavior', () {}); + + late PinballAudio audio; + final flameTester = FlameTester( + () => EmptyPinballTestGame(audio: audio), + ); + + setUp(() { + audio = _MockPinballAudio(); + }); + + flameTester.testGameWidget( + 'plays bumper sound', + setUp: (game, _) async { + final behavior = BumperNoisyBehavior(); + final parent = _TestBodyComponent(); + await game.ensureAdd(parent); + await parent.ensureAdd(behavior); + behavior.beginContact(Object(), _MockContact()); + }, + verify: (_, __) async { + verify(audio.bumper).called(1); + }, + ); +} diff --git a/test/game/components/scoring_behavior_test.dart b/test/game/behaviors/scoring_behavior_test.dart similarity index 65% rename from test/game/components/scoring_behavior_test.dart rename to test/game/behaviors/scoring_behavior_test.dart index 3e0f7fb4..07c2753a 100644 --- a/test/game/components/scoring_behavior_test.dart +++ b/test/game/behaviors/scoring_behavior_test.dart @@ -5,8 +5,8 @@ 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/game/behaviors/behaviors.dart'; import 'package:pinball/game/game.dart'; -import 'package:pinball_audio/pinball_audio.dart'; import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_flame/pinball_flame.dart'; @@ -17,8 +17,6 @@ class _TestBodyComponent extends BodyComponent { Body createBody() => world.createBody(BodyDef()); } -class _MockPinballAudio extends Mock implements PinballAudio {} - class _MockBall extends Mock implements Ball {} class _MockBody extends Mock implements Body {} @@ -39,12 +37,10 @@ void main() { group('ScoringBehavior', () { group('beginContact', () { late GameBloc bloc; - late PinballAudio audio; late Ball ball; late BodyComponent parent; setUp(() { - audio = _MockPinballAudio(); ball = _MockBall(); final ballBody = _MockBody(); when(() => ball.body).thenReturn(ballBody); @@ -54,9 +50,7 @@ void main() { }); final flameBlocTester = FlameBlocTester( - gameBuilder: () => EmptyPinballTestGame( - audio: audio, - ), + gameBuilder: EmptyPinballTestGame.new, blocBuilder: () { bloc = _MockGameBloc(); const state = GameState( @@ -116,57 +110,4 @@ void main() { ); }); }); - - group('BumperScoringBehavior', () { - group('beginContact', () { - late GameBloc bloc; - late PinballAudio audio; - late Ball ball; - late BodyComponent parent; - - setUp(() { - audio = _MockPinballAudio(); - ball = _MockBall(); - final ballBody = _MockBody(); - when(() => ball.body).thenReturn(ballBody); - when(() => ballBody.position).thenReturn(Vector2.all(4)); - - parent = _TestBodyComponent(); - }); - - final flameBlocTester = FlameBlocTester( - gameBuilder: () => EmptyPinballTestGame( - audio: audio, - ), - blocBuilder: () { - bloc = _MockGameBloc(); - const state = GameState( - score: 0, - multiplier: 1, - rounds: 3, - bonusHistory: [], - ); - whenListen(bloc, Stream.value(state), initialState: state); - return bloc; - }, - assets: assets, - ); - - flameBlocTester.testGameWidget( - 'plays bumper sound', - setUp: (game, tester) async { - final scoringBehavior = BumperScoringBehavior( - points: Points.oneMillion, - ); - await parent.add(scoringBehavior); - final canvas = ZCanvasComponent(children: [parent]); - await game.ensureAdd(canvas); - - scoringBehavior.beginContact(ball, _MockContact()); - - verify(audio.bumper).called(1); - }, - ); - }); - }); } diff --git a/test/game/components/android_acres/android_acres_test.dart b/test/game/components/android_acres/android_acres_test.dart index 73025551..8434d5f8 100644 --- a/test/game/components/android_acres/android_acres_test.dart +++ b/test/game/components/android_acres/android_acres_test.dart @@ -2,6 +2,7 @@ import 'package:flame_test/flame_test.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:pinball/game/behaviors/bumper_noisy_behavior.dart'; import 'package:pinball/game/components/android_acres/behaviors/behaviors.dart'; import 'package:pinball/game/game.dart'; import 'package:pinball_components/pinball_components.dart'; @@ -33,11 +34,12 @@ void main() { Assets.images.android.bumper.cow.lit.keyName, Assets.images.android.bumper.cow.dimmed.keyName, ]; - final flameTester = FlameTester( - () => EmptyPinballTestGame(assets: assets), - ); group('AndroidAcres', () { + final flameTester = FlameTester( + () => EmptyPinballTestGame(assets: assets), + ); + flameTester.test('loads correctly', (game) async { final component = AndroidAcres(); await game.ensureAdd(component); @@ -99,6 +101,20 @@ void main() { ); }, ); + + flameTester.test( + 'three AndroidBumpers with BumperNoisyBehavior', + (game) async { + await game.ensureAdd(AndroidAcres()); + final bumpers = game.descendants().whereType(); + for (final bumper in bumpers) { + expect( + bumper.firstChild(), + isNotNull, + ); + } + }, + ); }); flameTester.test('adds an AndroidSpaceshipBonusBehavior', (game) async { diff --git a/test/game/components/dino_desert/dino_desert_test.dart b/test/game/components/dino_desert/dino_desert_test.dart index 20c9ad38..d4c39dbe 100644 --- a/test/game/components/dino_desert/dino_desert_test.dart +++ b/test/game/components/dino_desert/dino_desert_test.dart @@ -2,6 +2,7 @@ import 'package:flame_test/flame_test.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:pinball/game/behaviors/behaviors.dart'; import 'package:pinball/game/components/dino_desert/behaviors/behaviors.dart'; import 'package:pinball/game/game.dart'; import 'package:pinball_components/pinball_components.dart'; diff --git a/test/game/components/flutter_forest/flutter_forest_test.dart b/test/game/components/flutter_forest/flutter_forest_test.dart index 5761a9eb..6dddcd7b 100644 --- a/test/game/components/flutter_forest/flutter_forest_test.dart +++ b/test/game/components/flutter_forest/flutter_forest_test.dart @@ -2,6 +2,7 @@ import 'package:flame_test/flame_test.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:pinball/game/behaviors/behaviors.dart'; import 'package:pinball/game/game.dart'; import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_flame/pinball_flame.dart'; @@ -73,6 +74,21 @@ void main() { ); }, ); + + flameTester.test( + 'three DashNestBumpers with BumperNoisyBehavior', + (game) async { + final flutterForest = FlutterForest(); + await game.ensureAdd(ZCanvasComponent(children: [flutterForest])); + final bumpers = game.descendants().whereType(); + for (final bumper in bumpers) { + expect( + bumper.firstChild(), + isNotNull, + ); + } + }, + ); }); }); } diff --git a/test/game/components/sparky_scorch_test.dart b/test/game/components/sparky_scorch_test.dart index 7d9c8c77..5df250dd 100644 --- a/test/game/components/sparky_scorch_test.dart +++ b/test/game/components/sparky_scorch_test.dart @@ -4,6 +4,7 @@ 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/game/behaviors/behaviors.dart'; import 'package:pinball/game/game.dart'; import 'package:pinball_components/pinball_components.dart'; @@ -74,6 +75,20 @@ void main() { ); }, ); + + flameTester.test( + 'three SparkyBumpers with BumperNoisyBehavior', + (game) async { + await game.ensureAdd(SparkyScorch()); + final bumpers = game.descendants().whereType(); + for (final bumper in bumpers) { + expect( + bumper.firstChild(), + isNotNull, + ); + } + }, + ); }); }); diff --git a/test/game/pinball_game_test.dart b/test/game/pinball_game_test.dart index dd87fec0..98aac670 100644 --- a/test/game/pinball_game_test.dart +++ b/test/game/pinball_game_test.dart @@ -140,19 +140,16 @@ void main() { ); }); - final flameTester = FlameTester( - () => PinballTestGame(assets: assets), - ); - final debugModeFlameTester = FlameTester( - () => DebugPinballTestGame(assets: assets), - ); - - final flameBlocTester = FlameBlocTester( - gameBuilder: () => PinballTestGame(assets: assets), - blocBuilder: () => gameBloc, - ); - group('PinballGame', () { + final flameTester = FlameTester( + () => PinballTestGame(assets: assets), + ); + + final flameBlocTester = FlameBlocTester( + gameBuilder: () => PinballTestGame(assets: assets), + blocBuilder: () => gameBloc, + ); + group('components', () { // TODO(alestiago): tests that Blueprints get added once the Blueprint // class is removed. @@ -247,6 +244,8 @@ void main() { final newState = _MockGameState(); when(() => newState.isGameOver).thenReturn(false); + await game.ready(); + expect( game.descendants().whereType().length, greaterThan(0), @@ -439,6 +438,10 @@ void main() { }); group('DebugPinballGame', () { + final debugModeFlameTester = FlameTester( + () => DebugPinballTestGame(assets: assets), + ); + debugModeFlameTester.test( 'adds a ball on tap up', (game) async { @@ -452,6 +455,7 @@ void main() { when(() => tapUpEvent.eventPosition).thenReturn(eventPosition); when(() => tapUpEvent.raw).thenReturn(raw); + await game.ready(); final previousBalls = game.descendants().whereType().toList();