diff --git a/README.md b/README.md index d2a692d..9a184e6 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,10 @@ - Звуки на WebAudio, вибрация на телефоне - Вся графика нарисована на Canvas — внешних ресурсов нет +## Документация + +[📋 Процесс разработки](docs/DEVLOG.md) — этапы (ТЗ → прототип → автотесты → публикация), инструменты, найденные баги, гигиена секретов и как развивать игру дальше. + ## Запуск Просто открой [index.html](index.html) в браузере (десктоп или телефон). Для локальной разработки: diff --git a/docs/DEVLOG.md b/docs/DEVLOG.md new file mode 100644 index 0000000..9233f1d --- /dev/null +++ b/docs/DEVLOG.md @@ -0,0 +1,175 @@ +# Процесс разработки + +Как игру «Рыбалка» спроектировали, собрали, проверили и опубликовали. Документ описывает этапы, инструменты и повторяемые команды — чтобы любой (включая вас через полгода) мог воспроизвести и продолжить процесс. + +``` +ТЗ → макет-прототип → среда проверки → автотесты → визуал → баги → публ. → поддержка + 1 2 3 4 5 6 7 8 +``` + +--- + +## Этап 1. Требования и дизайн (ТЗ) + +**Исходное ТЗ:** браузерная игра про рыбалку, играбельная на телефоне. + +Ключевые решения: + +| Решение | Зачем | +|---|---| +| Один самодостаточный `index.html` (HTML+CSS+JS) | Нет сборки, зависимостей и бэкенда. Файл можно открыть где угодно: телефон, десктоп, статический хостинг | +| Вся графика на Canvas, без внешних ассетов | Игра работает офлайн, грузится мгновенно | +| Портретная ориентация, управление одним пальцем | Все действия — тап/удержание по экрану, больших тач-мишеней (кнопки ≥ 44 px) | +| HTML-оверлеи для меню/магазина/карточек, Canvas — только мир игры | Кнопки и текст резкие и доступные, игровой мир — 60 fps | +| WebAudio для звуков (без файлов), `navigator.vibrate` для тактильного отклика | Снова ноль внешних ресурсов; на телефоне — вибрация на клёв/рывки/обрыв | +| Прогресс в `localStorage` | Мобильная игра без аккаунтов и сервера | + +Механика (единый цикл): + +``` +бросок (удержание = мощность) → ожидание → клёв («!») → подсечка (тап в окно) +→ вываживание (тяни/отпускай) → {улов / обрыв / срыв} → монеты → магазин +``` + +## Этап 2. Реализация + +Структура `index.html` (все секции помечены комментариями в файле): + +1. **Данные** — `SPECIES` (8 рыб: сила, редкость, цена, мин. дистанция), `RODS`, `LURES`. *Баланс игры крутится здесь.* +2. **Сохранение** — `save` + `localStorage` (монеты, снасти, коллекция, статистика). +3. **Холст** — масштабирование под `devicePixelRatio`, пересчёт layout'а при `resize`. +4. **Звук** — генеративный WebAudio: 12 эффектов из осцилляторов и шума. +5. **Состояния** — машина из 9 состояний: `MENU → IDLE → CHARGE → CAST → WAIT → APPROACH → BITE → REEL → SNAP/CARD → (IDLE)`. +6. **Физика вываживания** — `updateReel()`: напряжение шнура, дистанция, фазы «тянет/отдыхает». *Сложность крутится здесь.* +7. **Отрисовка** — небо, вода, рыбы-силуэты, лодка, поплавок, частицы, риплы, метры. +8. **Ввод** — Pointer Events (`pointerdown/up`), работают и палец, и мышь (важно для автотестов!). +9. **UI** — магазин, дневник, карточка улова. + +## Этап 3. Среда проверки + +Игра проверена в контейнере **Alpine Linux 3.24**. Минимальный набор инструментов: + +| Инструмент | Версия (в нашем прогоне) | Зачем | Установка | +|---|---|---|---| +| Node.js | v24.18.1 | Статический анализ JS, запуск автотестов | `apk add nodejs npm` | +| Chromium | 151.0.7922 | Headless-браузер для настоящего прогона игры | `apk add chromium` | +| puppeteer-core | 25.9.0 | Управление Chromium через CDP (клики, скриншоты, чтение состояний) | `npm i puppeteer-core` (в `tests/`) | +| Noto Color Emoji | — | Чтобы в headless-скриншотах эмодзи рендерились как на телефоне | `apk add font-noto-emoji` | +| git | 2.54.0 | Публикация | `apk add git` | +| curl | — | Проверка API Gitea, smoke-запросы | уже есть в Alpine | + +> В браузере на телефоне для *игры* не нужен ни один инструмент из таблицы — только сам браузер. + +## Этап 4. Статическая проверка + +Первый рубеж — компиляция кода настоящим движком (V8): + +```bash +# вытащить JS из HTML и проверить синтаксис +node --check game.js +``` + +Ловит опечатки, несбалансированные скобки, висячие конструкции — до запуска в браузере. + +## Этап 5. Автотесты (плейтест через CDP) + +`tests/test.js` — сценарный автотест: headless Chromium загружает игру, а «игрок» управляет мышью (в браузере мышь порождает те же Pointer Events, что и палец). Тест читает и подталкивает внутренние состояния игры (`state`, `biteTimer`, `reel.d`) через `page.evaluate`, чтобы покрыть все ветки без ожидания случайностей. + +**Что покрыто (21 проверка):** + +- меню → старт → зарядка → бросок → ожидание +- клёв (ускорен через `biteTimer`) → подсечка → `REEL` +- **все три исхода вываживания**: улов → карточка, обрыв шнура (`SNAP`), срыв рыбы +- магазин: открытие, покупка удочки и приманки (с проверкой баланса и экипировки) +- дневник: рендер, пойманная рыба в коллекции +- **сохранение после перезагрузки страницы** (coins/снасти/статистика) +- ноль ошибок в консоли и `pageerror` + +Запуск: + +```bash +cd tests +npm install +node test.js # при необходимости: CHROMIUM=/путь/к/хромью +``` + +## Этап 6. Визуальная проверка + +`tests/shots.js` — та же техника, но с фиксацией скриншотов каждого состояния (`screenshots/shot-*.png`). Скриншоты просматривает разработчик: ловит проблемы, которые не видны по коду и логам (сбившиеся метки, отсутствующие глифы, нечитаемые метры). + +## Этап 7. Найденные баги (ради чего всё это) + +Автотест + скриншоты поймали то, что ручное «посмотрел глазами» пропустило: + +| # | Баг | Как нашли | Лечение | +|---|---|---|---| +| 1 | **Критический:** после поимки рыбы игра вешалась навсегда. `catchFish()` ставил `reel=null`, но кейс `REEL` в `updateGame()` на следующей строке читал `reel.holding` → исключение убивало цикл `requestAnimationFrame` | Автотест: второй бросок не происходил; `pageerror` показал точную строку | `if(state==='REEL'&&reel){...}` | +| 2 | Точка крепления лески висела в углу экрана в меню/карточке | Визуальный осмотр скриншотов | Условие отрисовки по состояниям | +| 3 | Рыбка-маркер не стояла на краю полосы «дистанция» | Визуальный осмотр | Полоса = оставшаяся дистанция, маркер на её конце | +| 4 | Глиф `✕` (U+2715) отсутствовал в некоторых шрифтах | Скриншот магазина | Заменён на `×` (U+00D7) | + +**Вывод:** для игр с таймерами и случайностями автотест, который *управляет* временем и состоянием, покрывает за минуту то, что вручную ищут часами. + +## Этап 8. Публикация в Gitea + +Использовано: Gitea API (создание репозитория) + обычный `git push`. + +```bash +# 1. Создать репозиторий (токен: Настройки → Приложения → Токены, права: Repository) +curl -X POST -H "Authorization: token <ТОКЕН>" -H "Content-Type: application/json" \ + -d '{"name":"fishing-game","private":false,"auto_init":false}' \ + https://git.shstk.ru/api/v1/user/repos + +# 2. Наполнить ветку и запушить +git init -b main +git add -A +git commit -m "..." +git remote add origin https://git.shstk.ru/andrey/fishing-game.git +git push -u origin main # git попросит логин/пароль (или токен в URL — см. ниже) +``` + +Игра доступна анонимно по сырой ссылке: +`https://git.shstk.ru/andrey/fishing-game/raw/branch/main/index.html` + +### 🔐 Гигиена секретов (проверено на себе) + +1. **Токен — одноразовый.** Для публикации достаточно кратковременного токена с правами на репозиторий; после push его **отзываем** (в нашем случае — отозван, старый токен теперь отдаёт `401`). +2. **Токен не попадает в историю и конфиг.** Если передавать токен в URL (`https://user:TOKEN@host/...`), после push удалите его из конфига: + `git remote set-url origin https://git.shstk.ru/andrey/fishing-game.git` +3. **В репозиторий и в документы — только плейсхолдеры** (`<ТОКЕН>`), реальные секреты нигде не фиксируются. + +## Этап 9. Поддержка и развитие + +**Крутить баланс:** константы `SPECIES` / `RODS` / `LURES` в начале `index.html`; физика вываживания — в `updateReel()`. + +**Цикл изменений:** + +```bash +# 1. поправить index.html +# 2. прогнать автотест +cd tests && node test.js # ждём 21/21 +# 3. (опционально) свежие скриншоты +node shots.js +# 4. закоммитить и запушить +git add -A && git commit -m "что изменилось" && git push +``` + +**Идеи развития** (по нарастающей сложности): +- ночной клёв (своя таблица рыб по времени) +- босс-рыба с механикой «разрывать и переподсекать» +- продажи улова / рыбный прайс по редкости +- мировая таблица лидеров (нужен простой бэкенд или Gitea-Actions с webhook'ом) +- офлайн-режим PWA (service worker в тот же файл) + +--- + +## Шпаргалка: минимум инструментов по этапам + +| Этап | Инструменты | +|---|---| +| Игра (конечный пользователь) | Любой браузер на телефоне/десктопе | +| Написание/изменение кода | Текстовый редактор (или LLM-сессия) | +| Статическая проверка | Node.js | +| Автотесты | Node.js + npm + Chromium + puppeteer-core | +| Визуальная проверка | Скриншоты из автотеста + Noto Color Emoji в headless | +| Публикация | git + curl + Gitea (токен с минимальными правами, отзываемый) |