1
0
george 64b47a5536 docs: таблица этапов в README + актуализация описания
- раздел «Этапы» переписан таблицей: что меняется / приём / где в коде
- «Что внутри» и «Управление» приведены к итоговой версии (тор, пауза)

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

99 lines
7.1 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) без лишней
сложности.
## Что внутри (итоговая версия на `main`)
- Игровое поле-сетка 20×30, управление **свайпами**
- Змейка растёт и **ускоряется** с ростом счёта; ведётся счёт, **уровень** и **рекорд** (сохраняется между запусками)
- **Бонус-еда** с таймером даёт больше очков; **препятствия** добавляются с каждым уровнем
- Границы поля **телепортируют** на противоположную сторону (тор); смерть — от самой себя и от препятствий
- **Звук** и **анимация-вспышка** при поедании, **пауза** по тапу
- Вся отрисовка — через 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 }`.
## Управление
| Действие | Жест |
|----------|------|
| Начать игру | свайп или тап |
| Повернуть | свайп ↑ ↓ ← → |
| Пауза / продолжить | тап по полю во время игры |
| Рестарт после проигрыша | тап по экрану |
## Этапы (ветки)
Проект построен как серия этапов: каждый — отдельная **ветка**, добавляющая
**одну** фичу поверх предыдущего. Между соседними ветками — маленький diff,
поэтому легко увидеть, что именно меняется и какой Android/Kotlin-приём за это
отвечает. `main` = последний этап (вся игра целиком).
| Этап (ветка) | Что меняется | Ключевой приём | Где в коде |
|---|---|---|---|
| **`snake-stage-1`** | Базовая игра: поле 20×30, рост змейки, счёт, рекорд за сессию, свайпы, Game Over об стену/себя | Compose `Canvas`, игровой цикл на корутине, состояние в `remember` | весь `SnakeGame.kt` |
| **`snake-stage-2`** | Змейка ускоряется с ростом счёта (150→60 мс), индикатор «Скорость» | задержка шага пересчитывается из состояния | `stepDelay()`, `speedLevel()` |
| **`snake-stage-3`** | Рекорд **сохраняется между запусками** | Jetpack **DataStore** (`Flow` на чтение, `suspend` на запись) | `ScoreStore.kt`, загрузка в `LaunchedEffect` |
| **`snake-stage-4`** | **Звук** при поедании еды | **SoundPool** + `res/raw/eat.wav`, очистка в `DisposableEffect` | `soundPool.play(...)` в `step()` |
| **`snake-stage-5`** | **Анимация-вспышка** (кольцо) в момент укуса | **`Animatable`** (0→1) + `drawCircle` | `popProgress`, `drawBoard()` |
| **`snake-stage-6`** | **Пауза** по тапу во время игры | флаг `paused` в условии игрового цикла | `detectTapGestures`, оверлей «ПАУЗА» |
| **`snake-stage-7`** | **Бонус-еда** с таймером (1 из 4), +3 очка, золотая, исчезает по времени | таймер в «шагах» (учитывает паузу) + `drawArc` | `bonusFood`/`bonusStepsLeft`, `step()`/`drawBoard()` |
| **`snake-stage-8`** | **Препятствия и уровни**: каждые 5 очков +порция стен; врезался — Game Over | генерация на `Set<Cell>`, коллизии, прогрессия | `obstacles`/`level`, `addObstacles()` |
| **`snake-stage-9`** | **Сквозь границы (тор)**: уходишь за край — появляешься с другой стороны | модульная арифметика координат `% COLS/ROWS` | `step()` |
Посмотреть, что именно поменялось на этапе N, удобно diff'ом между ветками,
например для этапа 7:
```bash
git diff snake-stage-6 snake-stage-7
```
Идеи для следующих этапов: два игрока на одном экране, выбор размера поля, экран настроек.