feat: better bonus sound implementation

pull/364/head
Erick Zanardo 3 years ago
parent d0c4e7d987
commit c6d2cfde1b

@ -1,4 +1,5 @@
export 'ball_spawning_behavior.dart';
export 'bonus_noise_behavior.dart';
export 'bumper_noise_behavior.dart';
export 'camera_focusing_behavior.dart';
export 'scoring_behavior.dart';

@ -0,0 +1,41 @@
import 'package:flame/components.dart';
import 'package:flame_bloc/flame_bloc.dart';
import 'package:pinball/game/game.dart';
import 'package:pinball_audio/pinball_audio.dart';
import 'package:pinball_flame/pinball_flame.dart';
/// Behavior that handles playing a bonus sound effect
class BonusNoiseBehavior extends Component {
@override
Future<void> onLoad() async {
await add(
FlameBlocListener<GameBloc, GameState>(
listenWhen: (previous, current) {
return previous.bonusHistory.length != current.bonusHistory.length;
},
onNewState: (state) {
final bonus = state.bonusHistory.last;
final audioPlayer = readProvider<PinballPlayer>();
switch(bonus) {
case GameBonus.googleWord:
audioPlayer.play(PinballAudio.google);
break;
case GameBonus.sparkyTurboCharge:
audioPlayer.play(PinballAudio.sparky);
break;
case GameBonus.dinoChomp:
// TODO(erickzanardo): Add sound
break;
case GameBonus.androidSpaceship:
// TODO(erickzanardo): Add sound
break;
case GameBonus.dashNest:
// TODO(erickzanardo): Add sound
break;
}
},
),
);
}
}

@ -12,4 +12,4 @@ export 'google_word/google_word.dart';
export 'launcher.dart';
export 'multiballs/multiballs.dart';
export 'multipliers/multipliers.dart';
export 'sparky_scorch/sparky_scorch.dart';
export 'sparky_scorch.dart';

@ -1,7 +1,6 @@
import 'package:flame/components.dart';
import 'package:flame_bloc/flame_bloc.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';
@ -22,7 +21,6 @@ class GoogleWordBonusBehavior extends Component
.every((letter) => letter.bloc.state == GoogleLetterState.lit);
if (achievedBonus) {
readProvider<PinballPlayer>().play(PinballAudio.google);
bloc.add(const BonusActivated(GameBonus.googleWord));
for (final letter in googleLetters) {
letter.bloc.onReset();

@ -6,8 +6,6 @@ import 'package:pinball/game/behaviors/behaviors.dart';
import 'package:pinball/game/components/components.dart';
import 'package:pinball_components/pinball_components.dart';
export 'sparky_scorch_noise_behavior.dart';
/// {@template sparky_scorch}
/// Area positioned at the top left of the board containing the
/// [SparkyComputer], [SparkyAnimatronic], and [SparkyBumper]s.
@ -54,7 +52,6 @@ class SparkyComputerSensor extends BodyComponent
renderBody: false,
children: [
ScoringContactBehavior(points: Points.twentyThousand),
SparkyScorchNoiseBehavior(),
],
);

@ -1,15 +0,0 @@
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:pinball_audio/pinball_audio.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
/// Plays the sparky animation sound when in contact with a [Ball]
class SparkyScorchNoiseBehavior extends ContactBehavior {
@override
void beginContact(Object other, Contact contact) {
super.beginContact(other, contact);
if (other is Ball) {
readProvider<PinballPlayer>().play(PinballAudio.sparky);
}
}
}

@ -64,6 +64,7 @@ class PinballGame extends PinballForge2DGame
FlameProvider<AppLocalizations>.value(_l10n),
],
children: [
BonusNoiseBehavior(),
GameBlocStatusListener(),
BallSpawningBehavior(),
CameraFocusingBehavior(),

@ -56,7 +56,9 @@ class _ZCanvas extends CanvasWrapper {
final List<ZIndex> _zBuffer = [];
/// Postpones the rendering of [ZIndex] component and its children.
void buffer(ZIndex component) => _zBuffer.add(component);
void buffer(ZIndex component) => _zBuffer
..add(component)
..sort((a, b) => a.zIndex.compareTo(b.zIndex));
/// Renders all [ZIndex] components and their children.
///
@ -69,8 +71,7 @@ class _ZCanvas extends CanvasWrapper {
/// before the second one.
/// {@endtemplate}
void render() => _zBuffer
..sort((a, b) => a.zIndex.compareTo(b.zIndex))
..whereType<Component>().forEach(_render)
..forEach(_render)
..clear();
void _render(Component component) => component.renderTree(canvas);

@ -0,0 +1,187 @@
// 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_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_flame/pinball_flame.dart';
class _TestGame extends Forge2DGame {
Future<void> pump(
BonusNoiseBehavior child, {
required PinballPlayer player,
required GameBloc bloc,
}) {
return ensureAdd(
FlameBlocProvider<GameBloc, GameState>.value(
value: bloc,
children: [
FlameProvider<PinballPlayer>.value(
player,
children: [
child,
],
),
],
),
);
}
}
class _MockPinballPlayer extends Mock implements PinballPlayer {}
class _MockGameBloc extends Mock implements GameBloc {}
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
group('BonusNoiseBehavior', () {
late PinballPlayer player;
late GameBloc bloc;
final flameTester = FlameTester(_TestGame.new);
setUpAll(() {
registerFallbackValue(PinballAudio.google);
});
setUp(() {
player = _MockPinballPlayer();
when(() => player.play(any()))
.thenAnswer((_) { });
bloc = _MockGameBloc();
});
flameTester.testGameWidget(
'plays google sound',
setUp: (game, _) async {
const state = GameState(
totalScore: 0,
roundScore: 0,
multiplier: 1,
rounds: 0,
bonusHistory: [GameBonus.googleWord],
status: GameStatus.playing,
);
const initialState = GameState.initial();
whenListen(
bloc,
Stream.fromIterable([initialState, state]),
initialState: initialState,
);
final behavior = BonusNoiseBehavior();
await game.pump(behavior, player: player, bloc: bloc);
},
verify: (_, __) async {
verify(() => player.play(PinballAudio.google)).called(1);
},
);
flameTester.testGameWidget(
'plays sparky sound',
setUp: (game, _) async {
const state = GameState(
totalScore: 0,
roundScore: 0,
multiplier: 1,
rounds: 0,
bonusHistory: [GameBonus.sparkyTurboCharge],
status: GameStatus.playing,
);
const initialState = GameState.initial();
whenListen(
bloc,
Stream.fromIterable([initialState, state]),
initialState: initialState,
);
final behavior = BonusNoiseBehavior();
await game.pump(behavior, player: player, bloc: bloc);
},
verify: (_, __) async {
verify(() => player.play(PinballAudio.sparky)).called(1);
},
);
flameTester.testGameWidget(
'plays dino chomp sound',
setUp: (game, _) async {
const state = GameState(
totalScore: 0,
roundScore: 0,
multiplier: 1,
rounds: 0,
bonusHistory: [GameBonus.dinoChomp],
status: GameStatus.playing,
);
const initialState = GameState.initial();
whenListen(
bloc,
Stream.fromIterable([initialState, state]),
initialState: initialState,
);
final behavior = BonusNoiseBehavior();
await game.pump(behavior, player: player, bloc: bloc);
},
verify: (_, __) async {
// TODO(erickzanardo): Change when the sound is implemented
verifyNever(() => player.play(any()));
},
);
flameTester.testGameWidget(
'plays android spaceship sound',
setUp: (game, _) async {
const state = GameState(
totalScore: 0,
roundScore: 0,
multiplier: 1,
rounds: 0,
bonusHistory: [GameBonus.androidSpaceship],
status: GameStatus.playing,
);
const initialState = GameState.initial();
whenListen(
bloc,
Stream.fromIterable([initialState, state]),
initialState: initialState,
);
final behavior = BonusNoiseBehavior();
await game.pump(behavior, player: player, bloc: bloc);
},
verify: (_, __) async {
// TODO(erickzanardo): Change when the sound is implemented
verifyNever(() => player.play(any()));
},
);
flameTester.testGameWidget(
'plays dash nest sound',
setUp: (game, _) async {
const state = GameState(
totalScore: 0,
roundScore: 0,
multiplier: 1,
rounds: 0,
bonusHistory: [GameBonus.dashNest],
status: GameStatus.playing,
);
const initialState = GameState.initial();
whenListen(
bloc,
Stream.fromIterable([initialState, state]),
initialState: initialState,
);
final behavior = BonusNoiseBehavior();
await game.pump(behavior, player: player, bloc: bloc);
},
verify: (_, __) async {
// TODO(erickzanardo): Change when the sound is implemented
verifyNever(() => player.play(any()));
},
);
});
}

@ -1,75 +0,0 @@
// 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/components/components.dart';
import 'package:pinball_audio/pinball_audio.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
class _TestGame extends Forge2DGame {
Future<void> pump(_TestBodyComponent child, {required PinballPlayer player}) {
return ensureAdd(
FlameProvider<PinballPlayer>.value(
player,
children: [
child,
],
),
);
}
}
class _TestBodyComponent extends BodyComponent {
@override
Body createBody() => world.createBody(BodyDef());
}
class _MockPinballPlayer extends Mock implements PinballPlayer {}
class _MockContact extends Mock implements Contact {}
class _MockBall extends Mock implements Ball {}
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
group('SparkyScorchNoiseBehavior', () {});
late PinballPlayer player;
final flameTester = FlameTester(_TestGame.new);
setUp(() {
player = _MockPinballPlayer();
});
flameTester.testGameWidget(
'plays sparky sound',
setUp: (game, _) async {
final behavior = SparkyScorchNoiseBehavior();
final parent = _TestBodyComponent();
await game.pump(parent, player: player);
await parent.ensureAdd(behavior);
behavior.beginContact(_MockBall(), _MockContact());
},
verify: (_, __) async {
verify(() => player.play(PinballAudio.sparky)).called(1);
},
);
flameTester.testGameWidget(
'plays nothing when it is not a ball',
setUp: (game, _) async {
final behavior = SparkyScorchNoiseBehavior();
final parent = _TestBodyComponent();
await game.pump(parent, player: player);
await parent.ensureAdd(behavior);
behavior.beginContact(Object(), _MockContact());
},
verify: (_, __) async {
verifyNever(() => player.play(PinballAudio.sparky));
},
);
}
Loading…
Cancel
Save