feat: add cow moo sound (#412)

pull/414/head
Allison Ryan 2 years ago committed by GitHub
parent 80da24b43c
commit 8b40a642ec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -4,4 +4,5 @@ export 'bonus_noise_behavior.dart';
export 'bumper_noise_behavior.dart';
export 'camera_focusing_behavior.dart';
export 'character_selection_behavior.dart';
export 'cow_bumper_noise_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 CowBumperNoiseBehavior extends ContactBehavior {
@override
void beginContact(Object other, Contact contact) {
super.beginContact(other, contact);
readProvider<PinballAudioPlayer>().play(PinballAudio.cowMoo);
}
}

@ -47,7 +47,7 @@ class AndroidAcres extends Component {
AndroidBumper.cow(
children: [
ScoringContactBehavior(points: Points.twentyThousand),
BumperNoiseBehavior(),
CowBumperNoiseBehavior(),
],
)..initialPosition = Vector2(-20.7, -13),
AndroidSpaceshipBonusBehavior(),

@ -17,6 +17,7 @@ class $AssetsSfxGen {
String get android => 'assets/sfx/android.mp3';
String get bumperA => 'assets/sfx/bumper_a.mp3';
String get bumperB => 'assets/sfx/bumper_b.mp3';
String get cowMoo => 'assets/sfx/cow_moo.mp3';
String get dash => 'assets/sfx/dash.mp3';
String get dino => 'assets/sfx/dino.mp3';
String get gameOverVoiceOver => 'assets/sfx/game_over_voice_over.mp3';

@ -1,32 +1,36 @@
import 'dart:math';
import 'package:audioplayers/audioplayers.dart';
import 'package:clock/clock.dart';
import 'package:flame_audio/audio_pool.dart';
import 'package:flame_audio/flame_audio.dart';
import 'package:flutter/material.dart';
import 'package:pinball_audio/gen/assets.gen.dart';
/// Sounds available for play
/// Sounds available to play.
enum PinballAudio {
/// Google
/// Google.
google,
/// Bumper
/// Bumper.
bumper,
/// Background music
/// Cow moo.
cowMoo,
/// Background music.
backgroundMusic,
/// IO Pinball voice over
/// IO Pinball voice over.
ioPinballVoiceOver,
/// Game over
/// Game over.
gameOverVoiceOver,
/// Launcher
/// Launcher.
launcher,
/// Sparky
/// Sparky.
sparky,
/// Android
@ -145,8 +149,37 @@ class _BumperAudio extends _Audio {
}
}
class _ThrottledAudio extends _Audio {
_ThrottledAudio({
required this.preCacheSingleAudio,
required this.playSingleAudio,
required this.path,
required this.duration,
});
final PreCacheSingleAudio preCacheSingleAudio;
final PlaySingleAudio playSingleAudio;
final String path;
final Duration duration;
DateTime? _lastPlayed;
@override
Future<void> load() => preCacheSingleAudio(prefixFile(path));
@override
void play() {
final now = clock.now();
if (_lastPlayed == null ||
(_lastPlayed != null && now.difference(_lastPlayed!) > duration)) {
_lastPlayed = now;
playSingleAudio(prefixFile(path));
}
}
}
/// {@template pinball_audio_player}
/// Sound manager for the pinball game
/// Sound manager for the pinball game.
/// {@endtemplate}
class PinballAudioPlayer {
/// {@macro pinball_audio_player}
@ -212,6 +245,12 @@ class PinballAudioPlayer {
createAudioPool: _createAudioPool,
seed: _seed,
),
PinballAudio.cowMoo: _ThrottledAudio(
preCacheSingleAudio: _preCacheSingleAudio,
playSingleAudio: _playSingleAudio,
path: Assets.sfx.cowMoo,
duration: const Duration(seconds: 2),
),
PinballAudio.backgroundMusic: _LoopAudio(
preCacheSingleAudio: _preCacheSingleAudio,
loopSingleAudio: _loopSingleAudio,
@ -232,19 +271,19 @@ class PinballAudioPlayer {
final Random _seed;
/// Registered audios on the Player
/// Registered audios on the Player.
@visibleForTesting
// ignore: library_private_types_in_public_api
late final Map<PinballAudio, _Audio> audios;
/// Loads the sounds effects into the memory
/// Loads the sounds effects into the memory.
List<Future<void>> load() {
_configureAudioCache(FlameAudio.audioCache);
return audios.values.map((a) => a.load()).toList();
}
/// Plays the received audio
/// Plays the received audio.
void play(PinballAudio audio) {
assert(
audios.containsKey(audio),

@ -8,6 +8,7 @@ environment:
dependencies:
audioplayers: ^0.20.1
clock: ^1.1.0
flame_audio: ^1.0.1
flutter:
sdk: flutter

@ -2,6 +2,7 @@
import 'dart:math';
import 'package:audioplayers/audioplayers.dart';
import 'package:clock/clock.dart';
import 'package:flame_audio/audio_pool.dart';
import 'package:flame_audio/flame_audio.dart';
import 'package:flutter_test/flutter_test.dart';
@ -43,6 +44,8 @@ class _MockPreCacheSingleAudio extends Mock implements _PreCacheSingleAudio {}
class _MockRandom extends Mock implements Random {}
class _MockClock extends Mock implements Clock {}
void main() {
group('PinballAudio', () {
late _MockCreateAudioPool createAudioPool;
@ -171,6 +174,10 @@ void main() {
() => preCacheSingleAudio
.onCall('packages/pinball_audio/assets/sfx/launcher.mp3'),
).called(1);
verify(
() => preCacheSingleAudio
.onCall('packages/pinball_audio/assets/sfx/cow_moo.mp3'),
).called(1);
verify(
() => preCacheSingleAudio
.onCall('packages/pinball_audio/assets/music/background.mp3'),
@ -227,6 +234,42 @@ void main() {
});
});
group('cow moo', () {
test('plays the correct file', () async {
await Future.wait(audioPlayer.load());
audioPlayer.play(PinballAudio.cowMoo);
verify(
() => playSingleAudio
.onCall('packages/pinball_audio/${Assets.sfx.cowMoo}'),
).called(1);
});
test('only plays the sound again after 2 seconds', () async {
final clock = _MockClock();
await withClock(clock, () async {
when(clock.now).thenReturn(DateTime(2022));
await Future.wait(audioPlayer.load());
audioPlayer
..play(PinballAudio.cowMoo)
..play(PinballAudio.cowMoo);
verify(
() => playSingleAudio
.onCall('packages/pinball_audio/${Assets.sfx.cowMoo}'),
).called(1);
when(clock.now).thenReturn(DateTime(2022, 1, 1, 1, 2));
audioPlayer.play(PinballAudio.cowMoo);
verify(
() => playSingleAudio
.onCall('packages/pinball_audio/${Assets.sfx.cowMoo}'),
).called(1);
});
});
});
group('google', () {
test('plays the correct file', () async {
await Future.wait(audioPlayer.load());

@ -16,9 +16,7 @@ class _TestGame extends Forge2DGame {
return ensureAdd(
FlameProvider<PinballAudioPlayer>.value(
audioPlayer,
children: [
child,
],
children: [child],
),
);
}

@ -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('CowBumperNoiseBehavior', () {
late PinballAudioPlayer audioPlayer;
final flameTester = FlameTester(_TestGame.new);
setUp(() {
audioPlayer = _MockPinballAudioPlayer();
});
flameTester.testGameWidget(
'plays cow moo sound on contact',
setUp: (game, _) async {
final behavior = CowBumperNoiseBehavior();
final parent = _TestBodyComponent();
await game.pump(parent, audioPlayer: audioPlayer);
await parent.ensureAdd(behavior);
behavior.beginContact(Object(), _MockContact());
},
verify: (_, __) async {
verify(() => audioPlayer.play(PinballAudio.cowMoo)).called(1);
},
);
});
}

@ -4,7 +4,7 @@ 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:pinball/game/behaviors/bumper_noise_behavior.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';
@ -117,16 +117,33 @@ void main() {
);
flameTester.test(
'three AndroidBumpers with BumperNoiseBehavior',
'two AndroidBumpers with BumperNoiseBehavior',
(game) async {
await game.pump(AndroidAcres());
final bumpers = game.descendants().whereType<AndroidBumper>();
var behaviorCount = 0;
for (final bumper in bumpers) {
expect(
bumper.firstChild<BumperNoiseBehavior>(),
isNotNull,
);
if (bumper.firstChild<BumperNoiseBehavior>() != null) {
behaviorCount++;
}
}
expect(behaviorCount, equals(2));
},
);
flameTester.test(
'one AndroidBumper with CowBumperNoiseBehavior',
(game) async {
await game.pump(AndroidAcres());
final bumpers = game.descendants().whereType<AndroidBumper>();
expect(
bumpers.singleWhere(
(bumper) => bumper.firstChild<CowBumperNoiseBehavior>() != null,
),
isA<AndroidBumper>(),
);
},
);
});

Loading…
Cancel
Save