forked from george/snake-compose
Этап 5: анимация поедания еды
В момент съедания на месте еды расходится затухающее кольцо. - Animatable popProgress (0->1 за один укус), запуск из step() - отрисовка кольца в drawBoard() через drawCircle + Stroke, радиус растёт, прозрачность падает с прогрессом - цвет кольца — цвет еды Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
05d65eab14
commit
da783d9f0d
@ -84,4 +84,8 @@ app/src/main/
|
||||
(низкая задержка). Смотри настройку `SoundPool` и `soundPool.play(...)`
|
||||
в `step()`; освобождение ресурса — в `DisposableEffect`.
|
||||
|
||||
Идеи для следующих этапов: анимации поедания, пауза, выбор размера поля.
|
||||
- **`snake-stage-5`** — анимация поедания: в момент укуса на месте еды
|
||||
расходится затухающее кольцо. Смотри `Animatable` (`popProgress` 0→1),
|
||||
запуск анимации в `step()` и отрисовку кольца в `drawBoard()`.
|
||||
|
||||
Идеи для следующих этапов: пауза, выбор размера поля, экран настроек.
|
||||
|
||||
@ -2,6 +2,8 @@ package com.example.snake
|
||||
|
||||
import android.media.AudioAttributes
|
||||
import android.media.SoundPool
|
||||
import androidx.compose.animation.core.Animatable
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.Canvas
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.gestures.detectDragGestures
|
||||
@ -27,6 +29,7 @@ import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.geometry.Size
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.drawscope.DrawScope
|
||||
import androidx.compose.ui.graphics.drawscope.Stroke
|
||||
import androidx.compose.ui.input.pointer.pointerInput
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
@ -107,6 +110,11 @@ fun SnakeGame() {
|
||||
onDispose { soundPool.release() }
|
||||
}
|
||||
|
||||
// Этап 5: анимация поедания — расходящееся затухающее кольцо на месте еды.
|
||||
// popProgress идёт 0→1 за один укус; 1f означает «анимация завершена».
|
||||
val popProgress = remember { Animatable(1f) }
|
||||
var popCell by remember { mutableStateOf(food) }
|
||||
|
||||
fun reset() {
|
||||
snake = startSnake
|
||||
dir = Cell(0, -1)
|
||||
@ -135,6 +143,12 @@ fun SnakeGame() {
|
||||
score++
|
||||
// звук поедания (left=1f, right=1f, prio=1, без зацикливания, обычная скорость)
|
||||
soundPool.play(eatSoundId, 1f, 1f, 1, 0, 1f)
|
||||
// запускаем анимацию вспышки на месте съеденной еды
|
||||
popCell = nh
|
||||
scope.launch {
|
||||
popProgress.snapTo(0f)
|
||||
popProgress.animateTo(1f, animationSpec = tween(durationMillis = 260))
|
||||
}
|
||||
if (score > best) {
|
||||
best = score
|
||||
// новый рекорд — сохраняем на диск (асинхронно, UI не блокируется)
|
||||
@ -196,7 +210,7 @@ fun SnakeGame() {
|
||||
) {
|
||||
// Игровое поле
|
||||
Canvas(modifier = Modifier.fillMaxSize()) {
|
||||
drawBoard(snake, food)
|
||||
drawBoard(snake, food, popCell, popProgress.value)
|
||||
}
|
||||
|
||||
// Счёт сверху (+ индикатор скорости — видно ускорение)
|
||||
@ -231,7 +245,7 @@ private fun Overlay(title: String, subtitle: String, modifier: Modifier = Modifi
|
||||
}
|
||||
|
||||
/** Вся отрисовка поля внутри DrawScope. */
|
||||
private fun DrawScope.drawBoard(snake: List<Cell>, food: Cell) {
|
||||
private fun DrawScope.drawBoard(snake: List<Cell>, food: Cell, popCell: Cell, popProgress: Float) {
|
||||
val cell = minOf(size.width / COLS, size.height / ROWS)
|
||||
val offX = (size.width - cell * COLS) / 2f
|
||||
val offY = (size.height - cell * ROWS) / 2f
|
||||
@ -256,6 +270,18 @@ private fun DrawScope.drawBoard(snake: List<Cell>, food: Cell) {
|
||||
snake.forEachIndexed { idx, c ->
|
||||
drawCell(c, if (idx == 0) HEAD else BODY, cell, offX, offY)
|
||||
}
|
||||
|
||||
// анимация поедания: расходящееся затухающее кольцо на месте съеденной еды
|
||||
if (popProgress < 1f) {
|
||||
val center = Offset(offX + popCell.x * cell + cell / 2f, offY + popCell.y * cell + cell / 2f)
|
||||
val radius = cell * (0.4f + popProgress * 1.4f)
|
||||
drawCircle(
|
||||
color = FOOD.copy(alpha = 1f - popProgress),
|
||||
radius = radius,
|
||||
center = center,
|
||||
style = Stroke(width = cell * 0.16f)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun DrawScope.drawCell(c: Cell, color: Color, cell: Float, offX: Float, offY: Float) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user