Этап 4: звук при поедании еды

Короткий звуковой эффект при съедании еды через SoundPool.

- res/raw/eat.wav — короткий синтезированный «блип» (PCM 16-bit моно)
- SoundPool с AudioAttributes USAGE_GAME, загрузка сэмпла один раз
- воспроизведение в step() при поедании еды
- освобождение SoundPool в DisposableEffect при уходе с экрана

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
george 2026-06-17 17:44:46 +03:00
parent d8efaa53a6
commit 05d65eab14
3 changed files with 29 additions and 1 deletions

View File

@ -79,4 +79,9 @@ app/src/main/
диск не блокирует UI. Смотри `LaunchedEffect` для загрузки рекорда и диск не блокирует UI. Смотри `LaunchedEffect` для загрузки рекорда и
`scope.launch { ScoreStore.saveBest(...) }` при новом рекорде. `scope.launch { ScoreStore.saveBest(...) }` при новом рекорде.
Идеи для следующих этапов: экранные кнопки-стрелки, анимации, звук, паузу. - **`snake-stage-4`** — звук при поедании еды через **SoundPool**: короткий
WAV в `res/raw/eat.wav` грузится один раз, играется при каждом съедании
(низкая задержка). Смотри настройку `SoundPool` и `soundPool.play(...)`
в `step()`; освобождение ресурса — в `DisposableEffect`.
Идеи для следующих этапов: анимации поедания, пауза, выбор размера поля.

View File

@ -1,5 +1,7 @@
package com.example.snake package com.example.snake
import android.media.AudioAttributes
import android.media.SoundPool
import androidx.compose.foundation.Canvas import androidx.compose.foundation.Canvas
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.detectDragGestures import androidx.compose.foundation.gestures.detectDragGestures
@ -9,6 +11,7 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableIntStateOf
@ -86,6 +89,24 @@ fun SnakeGame() {
best = ScoreStore.bestFlow(context).first() best = ScoreStore.bestFlow(context).first()
} }
// Этап 4: звук поедания еды через SoundPool (короткий эффект, низкая задержка).
// Пул и загруженный сэмпл создаём один раз и освобождаем при уходе с экрана.
val soundPool = remember {
SoundPool.Builder()
.setMaxStreams(4)
.setAudioAttributes(
AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_GAME)
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.build()
)
.build()
}
val eatSoundId = remember { soundPool.load(context, R.raw.eat, 1) }
DisposableEffect(Unit) {
onDispose { soundPool.release() }
}
fun reset() { fun reset() {
snake = startSnake snake = startSnake
dir = Cell(0, -1) dir = Cell(0, -1)
@ -112,6 +133,8 @@ fun SnakeGame() {
snake = if (grew) listOf(nh) + snake else listOf(nh) + snake.dropLast(1) snake = if (grew) listOf(nh) + snake else listOf(nh) + snake.dropLast(1)
if (grew) { if (grew) {
score++ score++
// звук поедания (left=1f, right=1f, prio=1, без зацикливания, обычная скорость)
soundPool.play(eatSoundId, 1f, 1f, 1, 0, 1f)
if (score > best) { if (score > best) {
best = score best = score
// новый рекорд — сохраняем на диск (асинхронно, UI не блокируется) // новый рекорд — сохраняем на диск (асинхронно, UI не блокируется)

Binary file not shown.