diff --git a/packages/trashy_road/assets/audio/bounce_back.mp3 b/packages/trashy_road/assets/audio/bounce_back.mp3 new file mode 100644 index 00000000..51d663c0 Binary files /dev/null and b/packages/trashy_road/assets/audio/bounce_back.mp3 differ diff --git a/packages/trashy_road/assets/audio/rating_stars0.mp3 b/packages/trashy_road/assets/audio/rating_stars0.mp3 new file mode 100644 index 00000000..2af571dd Binary files /dev/null and b/packages/trashy_road/assets/audio/rating_stars0.mp3 differ diff --git a/packages/trashy_road/assets/audio/running_time.mp3 b/packages/trashy_road/assets/audio/running_time.mp3 new file mode 100644 index 00000000..38fd311f Binary files /dev/null and b/packages/trashy_road/assets/audio/running_time.mp3 differ diff --git a/packages/trashy_road/assets/audio/steps.mp3 b/packages/trashy_road/assets/audio/steps.mp3 new file mode 100644 index 00000000..e296620d Binary files /dev/null and b/packages/trashy_road/assets/audio/steps.mp3 differ diff --git a/packages/trashy_road/assets/audio/trash_collected.mp3 b/packages/trashy_road/assets/audio/trash_collected.mp3 new file mode 100644 index 00000000..e500ef10 Binary files /dev/null and b/packages/trashy_road/assets/audio/trash_collected.mp3 differ diff --git a/packages/trashy_road/lib/gen/assets.gen.dart b/packages/trashy_road/lib/gen/assets.gen.dart index 6be3b424..d44914f0 100644 --- a/packages/trashy_road/lib/gen/assets.gen.dart +++ b/packages/trashy_road/lib/gen/assets.gen.dart @@ -18,6 +18,9 @@ class $AssetsAudioGen { /// File path: assets/audio/background_music.mp3 String get backgroundMusic => 'assets/audio/background_music.mp3'; + /// File path: assets/audio/bounce_back.mp3 + String get bounceBack => 'assets/audio/bounce_back.mp3'; + /// File path: assets/audio/deposit_trash_1.wav String get depositTrash1 => 'assets/audio/deposit_trash_1.wav'; @@ -39,6 +42,9 @@ class $AssetsAudioGen { /// File path: assets/audio/plastic_bottle.mp3 String get plasticBottle => 'assets/audio/plastic_bottle.mp3'; + /// File path: assets/audio/rating_stars0.mp3 + String get ratingStars0 => 'assets/audio/rating_stars0.mp3'; + /// File path: assets/audio/rating_stars1.mp3 String get ratingStars1 => 'assets/audio/rating_stars1.mp3'; @@ -48,12 +54,22 @@ class $AssetsAudioGen { /// File path: assets/audio/rating_stars3.mp3 String get ratingStars3 => 'assets/audio/rating_stars3.mp3'; + /// File path: assets/audio/running_time.mp3 + String get runningTime => 'assets/audio/running_time.mp3'; + + /// File path: assets/audio/steps.mp3 + String get steps => 'assets/audio/steps.mp3'; + + /// File path: assets/audio/trash_collected.mp3 + String get trashCollected => 'assets/audio/trash_collected.mp3'; + /// File path: assets/audio/wrong_bin.mp3 String get wrongBin => 'assets/audio/wrong_bin.mp3'; /// List of all assets List get values => [ backgroundMusic, + bounceBack, depositTrash1, depositTrash2, depositTrash3, @@ -61,9 +77,13 @@ class $AssetsAudioGen { depositTrash5, hintingArrow, plasticBottle, + ratingStars0, ratingStars1, ratingStars2, ratingStars3, + runningTime, + steps, + trashCollected, wrongBin ]; } diff --git a/packages/trashy_road/lib/src/audio/bloc/audio_cubit.dart b/packages/trashy_road/lib/src/audio/bloc/audio_cubit.dart index 9460d217..7bfbc35e 100644 --- a/packages/trashy_road/lib/src/audio/bloc/audio_cubit.dart +++ b/packages/trashy_road/lib/src/audio/bloc/audio_cubit.dart @@ -8,19 +8,45 @@ part 'audio_state.dart'; class AudioCubit extends Cubit { AudioCubit({required AudioCache audioCache}) - : _effectPlayers = - List.generate(5, (index) => AudioPlayer()..audioCache = audioCache), - _backgroundMusic = Bgm(audioCache: audioCache), + : _backgroundMusic = Bgm(audioCache: audioCache), + _audioCache = audioCache, super(const AudioState()); - final List _effectPlayers; + late final Map _players = { + GameSoundEffects.depositTrash1: AudioPlayer()..audioCache = _audioCache, + GameSoundEffects.depositTrash2: AudioPlayer()..audioCache = _audioCache, + GameSoundEffects.depositTrash3: AudioPlayer()..audioCache = _audioCache, + GameSoundEffects.depositTrash4: AudioPlayer()..audioCache = _audioCache, + GameSoundEffects.depositTrash5: AudioPlayer()..audioCache = _audioCache, + GameSoundEffects.hintingArrow: AudioPlayer()..audioCache = _audioCache, + GameSoundEffects.trashCollected: AudioPlayer()..audioCache = _audioCache, + GameSoundEffects.ratingStars0: AudioPlayer()..audioCache = _audioCache, + GameSoundEffects.ratingStars1: AudioPlayer()..audioCache = _audioCache, + GameSoundEffects.ratingStars2: AudioPlayer()..audioCache = _audioCache, + GameSoundEffects.ratingStars3: AudioPlayer()..audioCache = _audioCache, + GameSoundEffects.runningTime: AudioPlayer()..audioCache = _audioCache, + }; + + late final Map> _pools = { + GameSoundEffects.steps: AudioPool.create( + source: GameSoundEffects.steps.source, + maxPlayers: 6, + audioCache: _audioCache, + ), + GameSoundEffects.wrongBin: AudioPool.create( + source: GameSoundEffects.wrongBin.source, + maxPlayers: 3, + audioCache: _audioCache, + ), + }; final Bgm _backgroundMusic; + final AudioCache _audioCache; + Future _changeVolume(double volume) async { await Future.wait( [ - ..._effectPlayers.map((player) => player.setVolume(volume)), _backgroundMusic.audioPlayer.setVolume(volume), ], ); @@ -34,35 +60,33 @@ class AudioCubit extends Cubit { /// /// If there are no players available, this method will do nothing. Future playEffect(GameAudioData audioData) async { - final player = _effectPlayers.where( - (player) => player.state != PlayerState.playing, - ); + final hasPlayer = _players.containsKey(audioData); + if (hasPlayer) { + final player = _players[audioData]!; + if (player.state != PlayerState.playing) { + await player.play( + audioData.source, + volume: audioData.volume, + mode: PlayerMode.lowLatency, + ); + return; + } + } - if (player.isEmpty) { + final hasPool = _pools.containsKey(audioData); + if (hasPool) { + final pool = await _pools[audioData]!; + await pool.start(volume: audioData.volume); return; } - - await player.first.play( - audioData._source, - volume: audioData._volume, - ); } Future playBackgroundMusic(GameAudioData audioData) async { - if (_backgroundMusic.isPlaying) { - await _backgroundMusic.stop(); - } - - await _backgroundMusic.play( - audioData._source.path, - volume: audioData._volume, - ); + // TODO(alestiago): Temporarily disabled the background music. } Future pauseBackgroundMusic() async { - if (_backgroundMusic.isPlaying) { - await _backgroundMusic.pause(); - } + // TODO(alestiago): Temporarily disabled the background music. } Future toggleVolume() async { @@ -74,9 +98,12 @@ class AudioCubit extends Cubit { @override Future close() { - for (final player in _effectPlayers) { - player.dispose(); - } + _players.forEach((key, value) async { + await value.dispose(); + }); + _pools.forEach((key, value) async { + await (await value).dispose(); + }); _backgroundMusic.dispose(); return super.close(); } diff --git a/packages/trashy_road/lib/src/audio/bloc/audio_state.dart b/packages/trashy_road/lib/src/audio/bloc/audio_state.dart index a9a04da3..8f6f605c 100644 --- a/packages/trashy_road/lib/src/audio/bloc/audio_state.dart +++ b/packages/trashy_road/lib/src/audio/bloc/audio_state.dart @@ -12,13 +12,12 @@ class AudioState extends Equatable { List get props => [volume]; } -class GameAudioData { +class GameAudioData extends Equatable { const GameAudioData._({ - required AssetSource source, - required double volume, + required this.source, + required this.volume, this.duration, - }) : _source = source, - _volume = volume; + }); GameAudioData.fromPath( String path, { @@ -30,11 +29,14 @@ class GameAudioData { duration: duration, ); - final AssetSource _source; + final AssetSource source; - final double _volume; + final double volume; final Duration? duration; + + @override + List get props => [source, volume, duration]; } abstract class GameBackgroundMusic { @@ -47,57 +49,73 @@ abstract class GameBackgroundMusic { abstract class GameSoundEffects { static final depositTrash1 = GameAudioData.fromPath( Assets.audio.depositTrash1, - volume: 0.25, + volume: 0.35, ); static final depositTrash2 = GameAudioData.fromPath( Assets.audio.depositTrash2, - volume: 0.25, + volume: 0.35, ); static final depositTrash3 = GameAudioData.fromPath( Assets.audio.depositTrash3, - volume: 0.25, + volume: 0.35, ); static final depositTrash4 = GameAudioData.fromPath( Assets.audio.depositTrash4, - volume: 0.25, + volume: 0.35, ); static final depositTrash5 = GameAudioData.fromPath( Assets.audio.depositTrash5, - volume: 0.25, + volume: 0.35, ); static final hintingArrow = GameAudioData.fromPath( Assets.audio.hintingArrow, - volume: 0.3, + volume: 0.4, + ); + + static final trashCollected = GameAudioData.fromPath( + Assets.audio.trashCollected, + volume: 0.55, ); - static final plasticTrash = GameAudioData.fromPath( - Assets.audio.plasticBottle, - volume: 0.25, + static final ratingStars0 = GameAudioData.fromPath( + Assets.audio.ratingStars0, + volume: 0.35, ); static final ratingStars1 = GameAudioData.fromPath( Assets.audio.ratingStars1, - volume: 0.25, + volume: 0.35, ); static final ratingStars2 = GameAudioData.fromPath( Assets.audio.ratingStars2, - volume: 0.25, + volume: 0.35, ); static final ratingStars3 = GameAudioData.fromPath( Assets.audio.ratingStars3, - volume: 0.25, + volume: 0.35, ); static final wrongBin = GameAudioData.fromPath( Assets.audio.wrongBin, - volume: 0.25, + volume: 0.35, duration: const Duration(seconds: 1), ); + + static final steps = GameAudioData.fromPath( + Assets.audio.steps, + volume: 0.12, + duration: const Duration(milliseconds: 820), + ); + + static final runningTime = GameAudioData.fromPath( + Assets.audio.runningTime, + volume: 0.3, + ); } diff --git a/packages/trashy_road/lib/src/game/entities/player/behaviors/player_moving_behavior.dart b/packages/trashy_road/lib/src/game/entities/player/behaviors/player_moving_behavior.dart index 11104195..47ddea3b 100644 --- a/packages/trashy_road/lib/src/game/entities/player/behaviors/player_moving_behavior.dart +++ b/packages/trashy_road/lib/src/game/entities/player/behaviors/player_moving_behavior.dart @@ -4,13 +4,17 @@ import 'package:flame_behaviors/flame_behaviors.dart'; import 'package:flame_bloc/flame_bloc.dart'; import 'package:flutter/widgets.dart'; import 'package:trashy_road/game_settings.dart'; +import 'package:trashy_road/src/audio/audio.dart'; import 'package:trashy_road/src/game/game.dart'; enum Direction { up, down, left, right } /// A behavior that allows the player to move around the game. final class PlayerMovingBehavior extends Behavior - with FlameBlocReader, ParentIsA { + with + FlameBlocReader, + ParentIsA, + HasGameReference { /// The delay between player moves. static const moveDelay = Duration(milliseconds: 200); @@ -62,6 +66,8 @@ final class PlayerMovingBehavior extends Behavior _isMoving = false; parent.position.setFrom(_targetPosition); _previousPosition.setFrom(_targetPosition); + + game.audioBloc.playEffect(GameSoundEffects.steps); } } diff --git a/packages/trashy_road/lib/src/game/entities/trash/trash.dart b/packages/trashy_road/lib/src/game/entities/trash/trash.dart index 0b6cfc41..81fb20dd 100644 --- a/packages/trashy_road/lib/src/game/entities/trash/trash.dart +++ b/packages/trashy_road/lib/src/game/entities/trash/trash.dart @@ -119,7 +119,7 @@ class Trash extends PositionedEntity /// Collects the trash. void collect() { - game.audioBloc.playEffect(GameSoundEffects.plasticTrash); + game.audioBloc.playEffect(GameSoundEffects.trashCollected); findBehavior() .children .whereType() diff --git a/packages/trashy_road/lib/src/game/widgets/game_stopwatch.dart b/packages/trashy_road/lib/src/game/widgets/game_stopwatch.dart index ad2a6221..7443bea1 100644 --- a/packages/trashy_road/lib/src/game/widgets/game_stopwatch.dart +++ b/packages/trashy_road/lib/src/game/widgets/game_stopwatch.dart @@ -1,6 +1,7 @@ import 'package:basura/basura.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:trashy_road/src/audio/audio.dart'; import 'package:trashy_road/src/game/game.dart'; import 'package:trashy_road/src/maps/maps.dart'; @@ -20,6 +21,8 @@ class _GameStopwatchState extends State with SingleTickerProviderStateMixin { final _stopwatch = Stopwatch(); + bool _playingRunningOutSoundEffect = false; + late final _animation = AnimationController( vsync: this, duration: const Duration(seconds: 1), @@ -38,10 +41,12 @@ class _GameStopwatchState extends State void _reset() { _animation.reset(); _stopwatch.reset(); + _playingRunningOutSoundEffect = false; } void _onTick() { if (!mounted) return; + _playRunningOutSoundEffect(); final gameBloc = context.read(); final mapsBloc = context.read(); @@ -55,6 +60,22 @@ class _GameStopwatchState extends State } } + void _playRunningOutSoundEffect() { + if (_playingRunningOutSoundEffect) return; + + final gameBloc = context.read(); + final mapsBloc = context.read(); + final map = mapsBloc.state.maps[gameBloc.state.identifier]!; + final completionSeconds = map.completionSeconds; + + final timeLeft = (completionSeconds * Duration.millisecondsPerSecond) - + _stopwatch.elapsed.inMilliseconds; + if (timeLeft < 10100) { + context.read().playEffect(GameSoundEffects.runningTime); + _playingRunningOutSoundEffect = true; + } + } + @override void dispose() { _animation.dispose(); diff --git a/packages/trashy_road/lib/src/score/widgets/animated_star_rating.dart b/packages/trashy_road/lib/src/score/widgets/animated_star_rating.dart index 19f4c644..6a524c84 100644 --- a/packages/trashy_road/lib/src/score/widgets/animated_star_rating.dart +++ b/packages/trashy_road/lib/src/score/widgets/animated_star_rating.dart @@ -57,9 +57,8 @@ class _AnimatedStarRatingState extends State } void _playSoundEffect() { - if (widget._rating == 0) return; - final soundEffect = switch (widget._rating) { + 0 => GameSoundEffects.ratingStars0, 1 => GameSoundEffects.ratingStars1, 2 => GameSoundEffects.ratingStars2, 3 => GameSoundEffects.ratingStars3,