snake-compose/README.md
george e84ffa754d Этап 9: сквозь границы (тор)
Змейка проходит сквозь край поля и появляется с противоположной стороны.

- координаты головы считаются по модулю: (x+dx+COLS)%COLS, (y+dy+ROWS)%ROWS
- столкновение с краем больше не убивает; смерть только от себя/препятствий

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 18:34:45 +03:00

113 lines
6.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 🐍 Змейка на Jetpack Compose
Учебный пример простого Android-приложения для студентов: классическая игра
«Змейка» на одном экране. Цель примера — показать, как устроено реальное
Android-приложение на современном стеке (Kotlin + Jetpack Compose) без лишней
сложности.
## Что внутри
- Игровое поле-сетка 20×30
- Управление **свайпами** (вверх/вниз/влево/вправо)
- Змейка растёт, когда съедает еду; ведётся счёт и рекорд за сессию
- Столкновение со стеной или с собой = Game Over, **тап** — рестарт
- Вся отрисовка — через Compose `Canvas`, игровой цикл — на корутине
## Стек
| Что | Версия |
|-----|--------|
| Язык | Kotlin 2.0.20 |
| UI | Jetpack Compose (BOM 2024.10.01, Material 3) |
| Android Gradle Plugin | 8.13.2 |
| Gradle | 8.13 |
| compileSdk / minSdk | 36 / 24 |
## Как запустить
### Вариант 1: Android Studio (рекомендуется)
1. Открыть папку проекта в Android Studio (**File → Open**).
2. Дождаться Gradle Sync.
3. Подключить телефон по USB (с включённой *отладкой по USB*) или запустить эмулятор.
4. Нажать ▶ **Run**.
### Вариант 2: командная строка
```bash
# нужен JDK 17+ и установленный Android SDK
./gradlew installDebug # соберёт и поставит приложение на подключённое устройство
```
> Файл `local.properties` с путём к SDK создаётся автоматически Android Studio
> и **не** хранится в репозитории — у каждого свой путь.
## Структура проекта
```
app/src/main/
├── java/com/example/snake/
│ ├── MainActivity.kt # точка входа: ComponentActivity + setContent { SnakeGame() }
│ └── SnakeGame.kt # вся игра: состояние, игровой цикл, отрисовка, управление
├── res/values/themes.xml # тема приложения
└── AndroidManifest.xml
```
👉 **Главный файл для изучения — [`SnakeGame.kt`](app/src/main/java/com/example/snake/SnakeGame.kt).**
Там по шагам видно идеи Compose:
- состояние через `remember { mutableStateOf(...) }` — UI сам перерисовывается при его изменении;
- игровой цикл через `LaunchedEffect { while(true) { delay(); step() } }`;
- ввод через `Modifier.pointerInput { detectTapGestures / detectDragGestures }`;
- рисование через `Canvas { drawRoundRect / drawLine }`.
## Управление
| Действие | Жест |
|----------|------|
| Начать игру | свайп или тап |
| Повернуть | свайп ↑ ↓ ← → |
| Рестарт после проигрыша | тап по экрану |
## Этапы (ветки)
Проект задуман как серия этапов — каждый в своей ветке, чтобы можно было
сравнивать версии:
- **`snake-stage-1`** — базовая рабочая игра.
- **`snake-stage-2`** — ускорение: змейка ускоряется с каждой съеденной едой
(задержка шага 150 → 60 мс), на экране индикатор «Скорость». Смотри
функции `stepDelay()` / `speedLevel()` и игровой цикл в `SnakeGame.kt`.
- **`snake-stage-3`** — рекорд сохраняется между запусками через **Jetpack
DataStore** (`ScoreStore.kt`): чтение — `Flow`, запись — suspend-функция,
диск не блокирует UI. Смотри `LaunchedEffect` для загрузки рекорда и
`scope.launch { ScoreStore.saveBest(...) }` при новом рекорде.
- **`snake-stage-4`** — звук при поедании еды через **SoundPool**: короткий
WAV в `res/raw/eat.wav` грузится один раз, играется при каждом съедании
(низкая задержка). Смотри настройку `SoundPool` и `soundPool.play(...)`
в `step()`; освобождение ресурса — в `DisposableEffect`.
- **`snake-stage-5`** — анимация поедания: в момент укуса на месте еды
расходится затухающее кольцо. Смотри `Animatable` (`popProgress` 0→1),
запуск анимации в `step()` и отрисовку кольца в `drawBoard()`.
- **`snake-stage-6`** — пауза: тап по полю во время игры ставит/снимает
паузу (свайпы по-прежнему рулят направлением). Смотри флаг `paused`,
условие в игровом цикле и оверлей «ПАУЗА».
- **`snake-stage-7`** — бонус-еда с таймером: иногда (1 из 4 при поедании
обычной еды) появляется золотая еда на ~36 шагов и даёт +3 очка; вокруг
неё рисуется дуга-таймер, по истечении она исчезает. Время считается в
шагах игры, поэтому пауза учитывается автоматически. Смотри `bonusFood` /
`bonusStepsLeft`, логику в `step()` и отрисовку (`drawArc`) в `drawBoard()`.
- **`snake-stage-8`** — препятствия и уровни: каждые 5 очков — новый уровень,
на поле добавляется порция серых стен; врезался в стену — Game Over. Еда и
бонус не спавнятся в препятствиях, новые стены не ставятся вплотную к голове.
В шапке — «Уровень». Смотри `obstacles`/`level`, `addObstacles()`, проверку
коллизий и повышение уровня в `step()`.
- **`snake-stage-9`** — сквозь границы (тор): змейка проходит сквозь край
поля и появляется с противоположной стороны; смерть остаётся только от
самой себя и от препятствий. Реализовано %-арифметикой по координатам
головы в `step()`.
Идеи для следующих этапов: два игрока, выбор размера поля, экран настроек.