Add synthesized SFX and audio wiring

Pure-Dart WAV synthesizer (tool/gen_sfx.dart) generates place/clear/
combo/win/lose effects; AudioService player pool fires on placement,
line clears, combo streaks, and phase transitions.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-06-11 13:28:02 +09:00
parent 3138fc4b08
commit ad6689b42f
12 changed files with 231 additions and 0 deletions
+19
View File
@@ -4,6 +4,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../game/engine/game_engine.dart';
import '../../game/models/stage.dart';
import '../../l10n/gen/app_localizations.dart';
import '../../services/audio_service.dart';
import '../../state/game_session_notifier.dart';
import '../../state/providers.dart';
import '../theme/palette.dart';
@@ -124,8 +125,26 @@ class _GameScreenState extends ConsumerState<GameScreen> {
}
}
void _playSfx(GameViewState? prev, GameViewState? next) {
if (next == null) return;
final audio = ref.read(audioServiceProvider);
if (prev?.fxTick != next.fxTick && next.lastPlacement != null) {
final placement = next.lastPlacement!;
if (placement.linesCleared > 0) {
audio.play(placement.comboStreak >= 2 ? Sfx.combo : Sfx.clear);
} else {
audio.play(Sfx.place);
}
}
if (prev?.phase != next.phase) {
if (next.phase == GamePhase.won) audio.play(Sfx.win);
if (next.phase == GamePhase.lost) audio.play(Sfx.lose);
}
}
@override
Widget build(BuildContext context) {
ref.listen<GameViewState?>(gameSessionProvider, _playSfx);
final view = ref.watch(gameSessionProvider);
if (view == null) {
return const Scaffold(body: Center(child: CircularProgressIndicator()));