Skip to content
This repository has been archived by the owner on Mar 13, 2024. It is now read-only.

Commit

Permalink
feat: audio and more audio
Browse files Browse the repository at this point in the history
  • Loading branch information
alestiago committed Mar 10, 2024
1 parent bfd3cc8 commit 74e16b5
Show file tree
Hide file tree
Showing 12 changed files with 143 additions and 52 deletions.
Binary file added packages/trashy_road/assets/audio/bounce_back.mp3
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added packages/trashy_road/assets/audio/steps.mp3
Binary file not shown.
Binary file not shown.
20 changes: 20 additions & 0 deletions packages/trashy_road/lib/gen/assets.gen.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

83 changes: 55 additions & 28 deletions packages/trashy_road/lib/src/audio/bloc/audio_cubit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,45 @@ part 'audio_state.dart';

class AudioCubit extends Cubit<AudioState> {
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<AudioPlayer> _effectPlayers;
late final Map<GameAudioData, AudioPlayer> _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<GameAudioData, Future<AudioPool>> _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<void> _changeVolume(double volume) async {
await Future.wait(
[
..._effectPlayers.map((player) => player.setVolume(volume)),
_backgroundMusic.audioPlayer.setVolume(volume),
],
);
Expand All @@ -34,35 +60,33 @@ class AudioCubit extends Cubit<AudioState> {
///
/// If there are no players available, this method will do nothing.
Future<void> 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<void> 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<void> pauseBackgroundMusic() async {
if (_backgroundMusic.isPlaying) {
await _backgroundMusic.pause();
}
// TODO(alestiago): Temporarily disabled the background music.
}

Future<void> toggleVolume() async {
Expand All @@ -74,9 +98,12 @@ class AudioCubit extends Cubit<AudioState> {

@override
Future<void> 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();
}
Expand Down
58 changes: 38 additions & 20 deletions packages/trashy_road/lib/src/audio/bloc/audio_state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,12 @@ class AudioState extends Equatable {
List<Object> 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, {
Expand All @@ -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<Object?> get props => [source, volume, duration];
}

abstract class GameBackgroundMusic {
Expand All @@ -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,
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<Player>
with FlameBlocReader<GameBloc, GameState>, ParentIsA<Player> {
with
FlameBlocReader<GameBloc, GameState>,
ParentIsA<Player>,
HasGameReference<TrashyRoadGame> {
/// The delay between player moves.
static const moveDelay = Duration(milliseconds: 200);

Expand Down Expand Up @@ -62,6 +66,8 @@ final class PlayerMovingBehavior extends Behavior<Player>
_isMoving = false;
parent.position.setFrom(_targetPosition);
_previousPosition.setFrom(_targetPosition);

game.audioBloc.playEffect(GameSoundEffects.steps);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ class Trash extends PositionedEntity

/// Collects the trash.
void collect() {
game.audioBloc.playEffect(GameSoundEffects.plasticTrash);
game.audioBloc.playEffect(GameSoundEffects.trashCollected);
findBehavior<PropagatingCollisionBehavior>()
.children
.whereType<RectangleHitbox>()
Expand Down
21 changes: 21 additions & 0 deletions packages/trashy_road/lib/src/game/widgets/game_stopwatch.dart
Original file line number Diff line number Diff line change
@@ -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';

Expand All @@ -20,6 +21,8 @@ class _GameStopwatchState extends State<GameStopwatch>
with SingleTickerProviderStateMixin {
final _stopwatch = Stopwatch();

bool _playingRunningOutSoundEffect = false;

late final _animation = AnimationController(
vsync: this,
duration: const Duration(seconds: 1),
Expand All @@ -38,10 +41,12 @@ class _GameStopwatchState extends State<GameStopwatch>
void _reset() {
_animation.reset();
_stopwatch.reset();
_playingRunningOutSoundEffect = false;
}

void _onTick() {
if (!mounted) return;
_playRunningOutSoundEffect();

final gameBloc = context.read<GameBloc>();
final mapsBloc = context.read<GameMapsBloc>();
Expand All @@ -55,6 +60,22 @@ class _GameStopwatchState extends State<GameStopwatch>
}
}

void _playRunningOutSoundEffect() {
if (_playingRunningOutSoundEffect) return;

final gameBloc = context.read<GameBloc>();
final mapsBloc = context.read<GameMapsBloc>();
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<AudioCubit>().playEffect(GameSoundEffects.runningTime);
_playingRunningOutSoundEffect = true;
}
}

@override
void dispose() {
_animation.dispose();
Expand Down
Loading

0 comments on commit 74e16b5

Please sign in to comment.