Живой документ планирования: сюда сходятся планы на развитие, актуальные задачи и проработка крупных направлений. Срез-статус архитектуры — docs/demo.md (дорожная карта там — историческая, актуальный план здесь).
Дата ревизии: 2026-09-07.
Статус проекта (2026-09-06): техно-демка движка. Основные силы — движок (packages/engine); игра — витрина и полигон проверки API. Приоритет решений: что двигает движок (API, тулзы, доки, границы), то и делаем; игрового контента ровно столько, сколько нужно демонстрации.
| # | Задача | Статус |
|---|---|---|
| 1 | Срез «меньше ошибок»: реестры ID, pre-commit, разрезание LocationScene, фикстуры, fail-fast waitFor | ✅ сделано (c379f20, 16b59d8, 39c6349) |
| 2 | AI-генерация текстур и спрайтов (работа B, ниже) | B0+B1 ✅ (PoC); B3 проба ✅ (SD-атласы, 6.6 мин/вид); B2 отложено — RD API или ручной арт |
| 3 | AI-генерация аудио (работа A, ниже) | 📋 спланировано, отложено |
| 4 | Акт 2: новые локации/сюжет по docs/world.md |
очередь после акта 1+AI-ассетов |
| 5 | Замена сгенерированных плейсхолдеров арта на AI/ручной арт | в составе работы B |
| 6 | Крупные объекты: footprint в движке, слоты арта (работа C, ниже) | в работе |
| 7 | Возможности аудио-движка (работа D, ниже): sfx-хендл, полифония, наблюдаемость, буферы | ✅ D0–D2 сделаны (2026-09-07) |
| 8 | Синтез звука по запросу для агента (работа E, ниже) | ✅ E1+E2 сделаны (2026-09-07); E3 — опционально |
| 9 | Инфраструктура аудио + музыкальный слой (работы F/G, ниже) | 📋 спланировано |
Отложено: IsoLayout как параметр игры — DEFAULT_ISO в math/iso.ts остаётся единственной точкой входа; прокидывание конфига игры через ~15 мест движка не даёт пользы демо и добавляет второй способ рассинхрона изометрии.
Приоритет между работами A и B: B (изображения) выше — визуальный стиль блокирует восприятие акта 2; аудио сейчас закрывается процедурным генератором (tools/audio/gen.mjs) приемлемо.
tools/pixelart/palette.mjs), размеры спрайтов по docs/art-style.md, pixel-perfect. Любая AI-генерация — только как сырьё с обязательной пост-обработкой: даунскейл nearest, квантизация в палитру, проверка чек-листом арт-библии. AI-вывод не попадает в assets/ напрямую.tools/audio/gen.mjs). AI-вывод приводится к нему (ffmpeg: sample rate, моно, нормализация, обрезка тишины).npm run maps:fresh).hero_sheet.png — контракт арт-библии).| Модель | Размер | Железо | Нативная палитра | Лицензия | Комментарий |
|---|---|---|---|---|---|
| PXG-Tiny (HF: Tarul/pxg-tiny) | 483K | CPU, NumPy, 1.8 МБ | да (прямые индексы) | MIT + CC0 | 16×16, 38 классов. Идеален по железу, но узкий домен |
| PixelGPT-24×24 (github.com/unstonio/pixelgpt-24x24) | 68M | CPU, ~22 с | да, 5 цветов/спрайт, кастомная палитра на запрос | нет (не указана) | 24×24; отказов нет, домен тянет частично (см. B1). Веса в репо; датасет 20K — CC BY 4.0 |
| Pixel Forge (cochranblock) | 1.1–17M | CPU (Rust) | да, 7 встроенных палитр | Unlicense | 108 классов объектов; появилась фича 4-directional sprite sheet relight — кандидат пробы B3 (нужен Rust-тулчейн) |
| SD_PixelArt_SpriteSheet_Generator (Onodofthenorth) | ~1B (SD 1.5) | CPU 6.6 мин/вид fp32 (25 шагов — 11 мин; q8 отвергнуто: медленнее и шумит) | нет (пост-обработка) | Apache-2.0 | 4 ракурса по токенам (FSS/RSS/BSS/LSS); кадры — позы стоя, не walk-цикл. Проба B3 ✅ — рабочий путь для разовых атласов |
| Retro Diffusion SD1.5 (LostMedia, архив) | ~1B | CPU минуты/картинку | нет (пост-обработка) | Public Domain | классика пиксель-диффузии; для офлайн-пакета |
| Retro Diffusion API (RD Plus/Tile/Animation) | облако | — | да | платный API | лучший результат для тайлов/анимаций; фолбэк, если локальное не тянет |
| LPC-style (PIXEL-T2I и др.) | research | GPU | нет | research | перспективное направление для атласов 4 ракурса × walk/slash |
Вывод по классам (после B0+B1): инвентарь/декор — PXG-Tiny локально (её классы); простые объекты 24×24 — PixelGPT (но лицензии нет — только PoC); критичные интерактивы и тайлы сложных поверхностей — RD API (платный) или ручной арт; дообучение под игру не делаем. Персонажи-атласы — кандидаты пробы: SD_PixelArt_SpriteSheet_Generator (Apache-2.0, CPU-минуты) и Pixel Forge relight (нужен Rust); фолбэк — платный RD API или ручной арт. Отдельный путь для полноразмерных атласов с боевыми кадрами — LoRA к Qwen-Image-Edit (Apache-2.0), но база ~20B: только разовая генерация на Kaggle/Colab GPU, результат — в наш пайплайн пост-обработки.
tools/aiart/gen.mjs # вызов модели (seed, промпт из манифеста) → tools/aiart/post.mjs # даунскейл nearest → квантизация palette.mjs → дедуп → tools/aiart/manifest.json # список ассетов: промпт, seed, размер, статус → apps/game/assets/… # только после ручного апрува (review-каталог)
Принципы: манифест-драйвен (как maps/); апрув-гейт (человек смотрит обзорный лист перед попаданием в игру); ревизия в git; проверка стиля — чек-лист docs/art-style.md + скриншот-смоук.
sheet2atlas — сборщик атласа из AI-листов (главная дыра). SD-лист 512² → кроп фигур (bbox, отсев фона) → нормализация в слоты 16×24/32×48 → раскладка в сетку hero_sheet.png + hero_sheet.json (контракт арт-библии). ✅ Сделан (2026-09-06): tools/aiart/atlas.mjs (gen/build/promote) + atlas-manifest.json + sd_sheet.py; движковая база — findFigures/ resizeRect/buildAtlas в @rpg/engine/tools. Палитра — параметр манифеста (game или palettes/<name>.json на 64/128/256 цветов). Фон листов модели неоднородный + сплошная «земля» под фигурами — автодетект фигур не срабатывает, в манифесте работают crop-оверрайды (раскладка 4 фигуры в ряд стабильна).tools/aiart/recolor.mjs (CLI по ключам палитры/hex) + поле remap в манифесте атласов — применяется в build после квантизации (плащ звонаря приведён к C0/C1).assets/ — размер в слоте, все цвета из палитры, детект «мыла» (градиентные пары соседних пикселей — чужая плотность пикселя). ✅ Сделан (2026-09-06): tools/aiart/lint-art.mjs (npm run art:lint по всем assets, негативы проверены); в agent:check не заведён — по мере надобности.tools/aiart/ (post.mjs — кроп/квантизация в палитру + обзорный лист; manifest.json — вердикты). Pixel Forge отложен (нет Rust-тулчейна). Ограничение: критичные для игры ассеты (колодец, столб, рычаг, колокольчик, моты) вне 38 классов модели — для них дообучение / RD API / ручной арт.manifest-b1.json.probe-b3/sd_q8_fss_42_broken.png. Вердикт: путь рабочий для разовых атласов (4 вида ≈ 30 мин/персонажа), но кадры-позы придётся дорабатывать руками; полный прогон — при потребности акта 2. Пробы: apps/game/tools/aiart/probe-b3/. Фолбэк — RD API или ручной арт.Акт 2 захочет здания и крупные деревья — объекты больше 1×1 тайла. Сегодня движок умеет только TallSpec (1 тайл footprint + произвольная высота) и ручной blocked. Большой спрайт, якоренный в один тайл, сортируется глубиной по точке — герой сбоку от дома перекрывается неверно.
(x+w-1)+(y+h-1), IsoDepthLayer.addRect), а не по точке якоря.gen.mjs (композит: модули стен + крыша — наш бесплатный путь); апскейл nearest из 24×24 (только болванка — чужая плотность пикселя рядом со спрайтами 1×); RD API (умеет большие размеры и тайлы, платный).props в TileMapData (footprint, коллизия по всему прямоугольнику, земля под ним), IsoDepthLayer.addRect (глубина по диагонали юго-восточного угла footprint'а), валидация в mapFormat, тесты; LocationScene вставляет map.propViews в общий depth-слой.docs/art-style.md (дом 2×2 — 64×80, дом 3×3 — 96×96, здание 4×4 — 128×112; якорь ставит движок). Плотность пикселя — как у тайлов; AI-апскейл в слоты не попадает.gen.mjs (tiles/house_big.png, 64×80, композит: модули щитов + крыша шпал + козырёк + окна) против апскейл-болванок 24→48 из B1 (apps/game/tools/aiart/probe-c2.png). Вывод: для крупных объектов — код-композит в gen.mjs; апскейл даёт мыло с чужой плотностью пикселя и залитым фоном (только черновая болванка), нативно больших размеров локальные модели не умеют. RD API остаётся опцией, если код-композита не хватит. Неквадратные footprint'ы (2×1, 3×1, 4×2): движок умеет (props w×h; поверхности заполняются обычными тайлами 1×1); AI-генерация — только квадраты (неквадрат собираем из 1×1 на уровне карты), код-композит — любой размер, RD API Tile — нативно прямоугольные.| Класс | Что это сейчас | AI-кандидат | Железо |
|---|---|---|---|
| Окружение (ветер, пепел, вода прудов) | процедурный шум в gen.mjs | Stable Audio 3 Small-SFX (433M, CPU, 120 с) — длинные лупы; луповальность через crossfade-склейку | CPU ок |
| SFX взаимодействия (шаги, клик, удар, звон) | gen.mjs, приемлемо | Stable Audio 3 Small-SFX (тот же рантайм; seed-детерминизм для вариантов) | CPU ок (~секунды-десятки секунд на клип на 4 ядрах) |
| Музыка: фоновые лупы | gen.mjs (треки 3 областей) | Stable Audio 3 Small-Music (433M, CPU, 120 с) — короткие лупы; либо ACE-Step 1.5 2B turbo INT8 (CPU-медленно: минуты-десятки минут на трек, но офлайн-пакет) | Small-Music — да; ACE-Step — терпимо для пакета |
| Музыка: катсцены/сюжетные моменты | нет (трек + звон) | ACE-Step 1.5 — полноценные треки с настроением/структурой, стемы; MusicGen small — фолбэк для мелодических заготовок | офлайн-пакет, не hot-reload |
| Модель | Размер | VRAM/железо | Длина | Лицензия | Роль у нас |
|---|---|---|---|---|---|
| Stable Audio 3 Small-SFX / Small-Music | 433M | CPU (TFLite), ~1.9 ГБ пик | 120 с | веса: Stability Community License; код MIT | основной локальный рантайм для SFX + лупов |
| ACE-Step 1.5 | 2B (turbo INT8) | CPU (медленно), GPU <4 ГБ | 10 с–10 мин, стемы | MIT | музыка катсцен/сюжета, офлайн-пакет |
| MusicGen small (AudioCraft) | 300M | CPU ~4 ГБ RAM | ~30 с | MIT/CC-BY-NC веса | фолбэк, мелодия-условность |
| Stable Audio Open 1.0 | 1.2B | GPU ~2.5–8 ГБ | 47 с | Stability Community | зрелый, но на нашей машине безнадёжно медленный; пропускаем |
tools/aiaudio/gen.mjs # вызов модели, seed, промпт из манифеста (аналог aiart)
→ post: ffmpeg → 22050 моно 16-bit → нормализация → crossfade-луп для ambience/sfx
→ review-каталог → apps/game/assets/audio/{sfx,ambience}/*.wav
Принципы те же: манифест, апрув-гейт, seed-детерминизм, формат игры не меняется (шины master/music/sfx/ambience в движке уже готовы). Лупы проверять скриптом (стык начала/конца без щелчка).
Итог аудио-работ (2026-09, коммиты 57bce47…4c1c56c): в движке — 4 шины, кроссфейд слотов music/ambience, playLoop без слота, pan/rate, шпион onPlayed; в игре — позиционный AudioSystem, локальные амбиент-слои, событийные звуки боя, DEV-лог __gameAudioLog. Ниже — потребности, которые упёрлись в границы AudioManager, по убыванию полезности. Игровая логика (кто слушатель, затухание по дистанции, playAt) остаётся в игре — в движок её не тащить.
play() возвращает void: нельзя остановить, перезапустить «с нуля» (опция «убить прежний экземпляр ключа») или изменить volume/pan/rate в полёте. Самый чувствительный кейс — движущийся источник: панорама фиксируется в момент запуска.MIN_INTERVAL на ключ в игровом AudioSystem. Нужен maxVoices с воровством самых тихих/старых голосов — защита от каскада (десяток combat:* в один тик).onPlayed зовётся только для sfx — лупы (музыка/амбиент/слои) шпион не видит, игре пришлось логировать амбиент вручную в Game.playAmbience. Нет onEnded — нечем ловить «звук доиграл» для синхронизации с анимацией.duration(key) — сейчас её не достать, а нужна катсценам и генератору шагов (не играть следующий шаг раньше конца предыдущего).playBuffer(AudioBuffer) — синтез сейчас только оффлайн (tools/wav.mjs); генерировать вариации звука в рантайме движок не может (сейчас джиттер имитируется rate).SfxHandle { stop(fadeSeconds?), setVolume(v), setPan(v), setRate(v) } — по образцу MusicHandle; опция restart/«один экземпляр на ключ»; maxVoices с воровством голосов.onPlayed на все запуски (sfx + лупы, опции нормализованы); onEnded(key); duration(key). Игра: ручное логирование амбиента убрано (лог целиком из шпиона).playBuffer(key, buffer, opts)
createBuffer(data); примитивы синтеза вынесены в чистый @rpg/engine/tools/synth.mjs (без node-импортов — wav.mjs в браузере не соберётся); игра — systems/StepVariants.ts: 3 процедурных варианта шага на поверхность (сид ~253–256 + сдвиг, полосы как в gen.mjs), до unlock — файловый шаг. Гвард: @rpg/engine/tools/* — публичное API.Движок строится агент-первым: у агента нет слуха, и искать готовые сэмплы в библиотеках он не может. «Генерация по запросу» для агента — параметрический синтез: агент описывает звук спеком (форма, полоса шума, тон, огибающая, сид), движок детерминированно компилирует спек в буфер (база — D2: createBuffer/playBuffer + tools/synth.mjs), факт запуска наблюдаем через __gameAudioLog. Нейросеть позже — опциональный «описание → спек» маппер поверх того же API; сэмпл-библиотеки как основной путь не используются.
audio.playSpec(key, spec, opts?) — kinds hit/chime/scrape/hum, параметры: dur (обяз.), low/high (полоса шума), tone (низкая подложка/основной тон гула), freq (звон), power (крутизна затухания), seed (детерминизм), peak, loop (кроссфейд краёв). Кэш буферов по ключу. Тесты: детерминизм (одинаковые спеки/сиды → одинаковые сэмплы), kinds, кэш.scene:synthesize (спек → playSpec), документация форм спеков в docs/engine/agent.md; проба checks/synth.mjs (в agent:check) — агент слепо синтезирует удар/звон/ скрип/гул, проверка фактов запуска в аудио-логе + отрицательные случаи.✅ Сделана (2026-09-07): mute/suspend при visibilitychange (опция pauseOnHide, по умолчанию включена); bus в PlayOptions разового play (по умолчанию sfx); exponential-фейд на затуханиях (нарастание — линейное: экспонента не определена от нуля).
synth.mjs: осциллятор + огибающая, партия = трек = стем), плейлист/секвенция тем («тема лугов → тема боя → назад»), стем-миксинг — параллельные лупы-слои с независимой громкостью (спокойно/настороженно/бой; ложится на существующий playLoop).~/.cache/rpg-ai/venv, venv-pxgpt), веса в ~/.cache/rpg-ai/; в git — манифесты и review-PNG.