feat: add rollover sounds (#430)

* feat: rollover sounds

* style: trailing comma
pull/435/head
Allison Ryan 3 years ago committed by GitHub
parent bac790db95
commit 43beb3db39
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -6,4 +6,5 @@ export 'camera_focusing_behavior.dart';
export 'character_selection_behavior.dart'; export 'character_selection_behavior.dart';
export 'cow_bumper_noise_behavior.dart'; export 'cow_bumper_noise_behavior.dart';
export 'kicker_noise_behavior.dart'; export 'kicker_noise_behavior.dart';
export 'rollover_noise_behavior.dart';
export 'scoring_behavior.dart'; export 'scoring_behavior.dart';

@ -0,0 +1,13 @@
// ignore_for_file: public_member_api_docs
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:pinball_audio/pinball_audio.dart';
import 'package:pinball_flame/pinball_flame.dart';
class RolloverNoiseBehavior extends ContactBehavior {
@override
void beginContact(Object other, Contact contact) {
super.beginContact(other, contact);
readProvider<PinballAudioPlayer>().play(PinballAudio.rollover);
}
}

@ -22,12 +22,14 @@ class GoogleGallery extends Component with ZIndex {
side: BoardSide.right, side: BoardSide.right,
children: [ children: [
ScoringContactBehavior(points: Points.fiveThousand), ScoringContactBehavior(points: Points.fiveThousand),
RolloverNoiseBehavior(),
], ],
), ),
GoogleRollover( GoogleRollover(
side: BoardSide.left, side: BoardSide.left,
children: [ children: [
ScoringContactBehavior(points: Points.fiveThousand), ScoringContactBehavior(points: Points.fiveThousand),
RolloverNoiseBehavior(),
], ],
), ),
GoogleWord(position: Vector2(-4.45, 1.8)), GoogleWord(position: Vector2(-4.45, 1.8)),

@ -125,6 +125,7 @@ class PinballGame extends PinballForge2DGame
SkillShot( SkillShot(
children: [ children: [
ScoringContactBehavior(points: Points.oneMillion), ScoringContactBehavior(points: Points.oneMillion),
RolloverNoiseBehavior(),
], ],
), ),
AndroidAcres(), AndroidAcres(),

@ -26,6 +26,7 @@ class $AssetsSfxGen {
String get kickerA => 'assets/sfx/kicker_a.mp3'; String get kickerA => 'assets/sfx/kicker_a.mp3';
String get kickerB => 'assets/sfx/kicker_b.mp3'; String get kickerB => 'assets/sfx/kicker_b.mp3';
String get launcher => 'assets/sfx/launcher.mp3'; String get launcher => 'assets/sfx/launcher.mp3';
String get rollover => 'assets/sfx/rollover.mp3';
String get sparky => 'assets/sfx/sparky.mp3'; String get sparky => 'assets/sfx/sparky.mp3';
} }

@ -33,6 +33,9 @@ enum PinballAudio {
/// Kicker. /// Kicker.
kicker, kicker,
/// Rollover.
rollover,
/// Sparky. /// Sparky.
sparky, sparky,
@ -56,7 +59,7 @@ typedef CreateAudioPool = Future<AudioPool> Function(
}); });
/// Defines the contract for playing a single audio. /// Defines the contract for playing a single audio.
typedef PlaySingleAudio = Future<void> Function(String); typedef PlaySingleAudio = Future<void> Function(String, {double volume});
/// Defines the contract for looping a single audio. /// Defines the contract for looping a single audio.
typedef LoopSingleAudio = Future<void> Function(String, {double volume}); typedef LoopSingleAudio = Future<void> Function(String, {double volume});
@ -81,18 +84,20 @@ class _SimplePlayAudio extends _Audio {
required this.preCacheSingleAudio, required this.preCacheSingleAudio,
required this.playSingleAudio, required this.playSingleAudio,
required this.path, required this.path,
this.volume,
}); });
final PreCacheSingleAudio preCacheSingleAudio; final PreCacheSingleAudio preCacheSingleAudio;
final PlaySingleAudio playSingleAudio; final PlaySingleAudio playSingleAudio;
final String path; final String path;
final double? volume;
@override @override
Future<void> load() => preCacheSingleAudio(prefixFile(path)); Future<void> load() => preCacheSingleAudio(prefixFile(path));
@override @override
void play() { void play() {
playSingleAudio(prefixFile(path)); playSingleAudio(prefixFile(path), volume: volume ?? 1);
} }
} }
@ -266,6 +271,12 @@ class PinballAudioPlayer {
playSingleAudio: _playSingleAudio, playSingleAudio: _playSingleAudio,
path: Assets.sfx.launcher, path: Assets.sfx.launcher,
), ),
PinballAudio.rollover: _SimplePlayAudio(
preCacheSingleAudio: _preCacheSingleAudio,
playSingleAudio: _playSingleAudio,
path: Assets.sfx.rollover,
volume: 0.3,
),
PinballAudio.ioPinballVoiceOver: _SimplePlayAudio( PinballAudio.ioPinballVoiceOver: _SimplePlayAudio(
preCacheSingleAudio: _preCacheSingleAudio, preCacheSingleAudio: _preCacheSingleAudio,
playSingleAudio: _playSingleAudio, playSingleAudio: _playSingleAudio,

@ -29,15 +29,15 @@ class _MockConfigureAudioCache extends Mock {
} }
class _MockPlaySingleAudio extends Mock { class _MockPlaySingleAudio extends Mock {
Future<void> onCall(String url); Future<void> onCall(String path, {double volume});
} }
class _MockLoopSingleAudio extends Mock { class _MockLoopSingleAudio extends Mock {
Future<void> onCall(String url, {double volume}); Future<void> onCall(String path, {double volume});
} }
abstract class _PreCacheSingleAudio { abstract class _PreCacheSingleAudio {
Future<void> onCall(String url); Future<void> onCall(String path);
} }
class _MockPreCacheSingleAudio extends Mock implements _PreCacheSingleAudio {} class _MockPreCacheSingleAudio extends Mock implements _PreCacheSingleAudio {}
@ -74,7 +74,8 @@ void main() {
when(() => configureAudioCache.onCall(any())).thenAnswer((_) {}); when(() => configureAudioCache.onCall(any())).thenAnswer((_) {});
playSingleAudio = _MockPlaySingleAudio(); playSingleAudio = _MockPlaySingleAudio();
when(() => playSingleAudio.onCall(any())).thenAnswer((_) async {}); when(() => playSingleAudio.onCall(any(), volume: any(named: 'volume')))
.thenAnswer((_) async {});
loopSingleAudio = _MockLoopSingleAudio(); loopSingleAudio = _MockLoopSingleAudio();
when(() => loopSingleAudio.onCall(any(), volume: any(named: 'volume'))) when(() => loopSingleAudio.onCall(any(), volume: any(named: 'volume')))
@ -195,6 +196,10 @@ void main() {
() => preCacheSingleAudio () => preCacheSingleAudio
.onCall('packages/pinball_audio/assets/sfx/launcher.mp3'), .onCall('packages/pinball_audio/assets/sfx/launcher.mp3'),
).called(1); ).called(1);
verify(
() => preCacheSingleAudio
.onCall('packages/pinball_audio/assets/sfx/rollover.mp3'),
).called(1);
verify( verify(
() => preCacheSingleAudio () => preCacheSingleAudio
.onCall('packages/pinball_audio/assets/sfx/cow_moo.mp3'), .onCall('packages/pinball_audio/assets/sfx/cow_moo.mp3'),
@ -346,8 +351,10 @@ void main() {
audioPlayer.play(PinballAudio.google); audioPlayer.play(PinballAudio.google);
verify( verify(
() => playSingleAudio () => playSingleAudio.onCall(
.onCall('packages/pinball_audio/${Assets.sfx.google}'), 'packages/pinball_audio/${Assets.sfx.google}',
volume: any(named: 'volume'),
),
).called(1); ).called(1);
}); });
}); });
@ -358,8 +365,10 @@ void main() {
audioPlayer.play(PinballAudio.sparky); audioPlayer.play(PinballAudio.sparky);
verify( verify(
() => playSingleAudio () => playSingleAudio.onCall(
.onCall('packages/pinball_audio/${Assets.sfx.sparky}'), 'packages/pinball_audio/${Assets.sfx.sparky}',
volume: any(named: 'volume'),
),
).called(1); ).called(1);
}); });
}); });
@ -370,8 +379,10 @@ void main() {
audioPlayer.play(PinballAudio.dino); audioPlayer.play(PinballAudio.dino);
verify( verify(
() => playSingleAudio () => playSingleAudio.onCall(
.onCall('packages/pinball_audio/${Assets.sfx.dino}'), 'packages/pinball_audio/${Assets.sfx.dino}',
volume: any(named: 'volume'),
),
).called(1); ).called(1);
}); });
}); });
@ -382,8 +393,10 @@ void main() {
audioPlayer.play(PinballAudio.android); audioPlayer.play(PinballAudio.android);
verify( verify(
() => playSingleAudio () => playSingleAudio.onCall(
.onCall('packages/pinball_audio/${Assets.sfx.android}'), 'packages/pinball_audio/${Assets.sfx.android}',
volume: any(named: 'volume'),
),
).called(1); ).called(1);
}); });
}); });
@ -394,8 +407,10 @@ void main() {
audioPlayer.play(PinballAudio.dash); audioPlayer.play(PinballAudio.dash);
verify( verify(
() => playSingleAudio () => playSingleAudio.onCall(
.onCall('packages/pinball_audio/${Assets.sfx.dash}'), 'packages/pinball_audio/${Assets.sfx.dash}',
volume: any(named: 'volume'),
),
).called(1); ).called(1);
}); });
}); });
@ -406,8 +421,24 @@ void main() {
audioPlayer.play(PinballAudio.launcher); audioPlayer.play(PinballAudio.launcher);
verify( verify(
() => playSingleAudio () => playSingleAudio.onCall(
.onCall('packages/pinball_audio/${Assets.sfx.launcher}'), 'packages/pinball_audio/${Assets.sfx.launcher}',
volume: any(named: 'volume'),
),
).called(1);
});
});
group('rollover', () {
test('plays the correct file', () async {
await Future.wait(audioPlayer.load());
audioPlayer.play(PinballAudio.rollover);
verify(
() => playSingleAudio.onCall(
'packages/pinball_audio/${Assets.sfx.rollover}',
volume: .3,
),
).called(1); ).called(1);
}); });
}); });
@ -420,6 +451,7 @@ void main() {
verify( verify(
() => playSingleAudio.onCall( () => playSingleAudio.onCall(
'packages/pinball_audio/${Assets.sfx.ioPinballVoiceOver}', 'packages/pinball_audio/${Assets.sfx.ioPinballVoiceOver}',
volume: any(named: 'volume'),
), ),
).called(1); ).called(1);
}); });
@ -433,6 +465,7 @@ void main() {
verify( verify(
() => playSingleAudio.onCall( () => playSingleAudio.onCall(
'packages/pinball_audio/${Assets.sfx.gameOverVoiceOver}', 'packages/pinball_audio/${Assets.sfx.gameOverVoiceOver}',
volume: any(named: 'volume'),
), ),
).called(1); ).called(1);
}); });

@ -0,0 +1,58 @@
// 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 'package:pinball_flame/pinball_flame.dart';
class _TestGame extends Forge2DGame {
Future<void> pump(
_TestBodyComponent child, {
required PinballAudioPlayer audioPlayer,
}) {
return ensureAdd(
FlameProvider<PinballAudioPlayer>.value(
audioPlayer,
children: [child],
),
);
}
}
class _TestBodyComponent extends BodyComponent {
@override
Body createBody() => world.createBody(BodyDef());
}
class _MockPinballAudioPlayer extends Mock implements PinballAudioPlayer {}
class _MockContact extends Mock implements Contact {}
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
group('RolloverNoiseBehavior', () {
late PinballAudioPlayer audioPlayer;
final flameTester = FlameTester(_TestGame.new);
setUp(() {
audioPlayer = _MockPinballAudioPlayer();
});
flameTester.testGameWidget(
'plays rollover sound on contact',
setUp: (game, _) async {
final behavior = RolloverNoiseBehavior();
final parent = _TestBodyComponent();
await game.pump(parent, audioPlayer: audioPlayer);
await parent.ensureAdd(behavior);
behavior.beginContact(Object(), _MockContact());
},
verify: (_, __) async {
verify(() => audioPlayer.play(PinballAudio.rollover)).called(1);
},
);
});
}

@ -97,6 +97,20 @@ void main() {
}, },
); );
flameTester.test(
'RolloverNoiseBehavior to GoogleRollovers',
(game) async {
await game.pump(GoogleGallery());
game.descendants().whereType<GoogleRollover>().forEach(
(rollover) => expect(
rollover.firstChild<RolloverNoiseBehavior>(),
isNotNull,
),
);
},
);
flameTester.test('a GoogleWordBonusBehavior', (game) async { flameTester.test('a GoogleWordBonusBehavior', (game) async {
final component = GoogleGallery(); final component = GoogleGallery();
await game.pump(component); await game.pump(component);

Loading…
Cancel
Save