forked from george/snake-compose
Этап 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:
parent
d8efaa53a6
commit
05d65eab14
@ -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`.
|
||||||
|
|
||||||
|
Идеи для следующих этапов: анимации поедания, пауза, выбор размера поля.
|
||||||
|
|||||||
@ -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 не блокируется)
|
||||||
|
|||||||
BIN
app/src/main/res/raw/eat.wav
Normal file
BIN
app/src/main/res/raw/eat.wav
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user