squad-proto/docs/14-monsters.md
z.kirill 4382af2322 Документация: границы движка, работа через модель, дизайн игры до лейтгейма
18-engine.md    границы Tile2D, проекции 2D, формат проекта, второй проект
  19-agent.md     инструменты --tool, скиллы, терминал в редакторе
  20-gdd.md       акты, петли, рост отряда, закон масштаба, бюджет 200 часов
  21-damage.md    семь стихий, статусы, десять реакций, формулы, триггеры
  22-loot.md      редкости, аффиксы, артефакты, зачарования, материалы
  23-endgame.md   Бездна, мутации, лидерборды, 262 ачивки

Плюс правки 01/02/07/15/16/17 под переехавшие пути и новые правила забега.

Дизайнерская часть держится на одном законе: угроза растёт квадратично, сила
отряда — линейно с затуханием, разрыв закрывается ЗНАНИЕМ, а не числами. Из
него выведены и экономика опыта (41 млн против 44 млн дохода), и то, почему
реакции считаются от глубины, а не от урона оружия, и почему в Бездне
множитель HP разрешён, а во всём авторском контенте запрещён.

.claude/commands: скиллы /tile2d, /room, /floor — правила этого движка,
которые нельзя вывести из кода. Главное записано первым: не отчитываться об
успехе без прохода --tool validate.

Числа в этих документах — вход для инструментов, а не украшение. Расхождение
между таблицей и выводом инструмента означает, что неправ документ.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 21:07:45 +03:00

7.9 KiB
Raw Permalink Blame History

14. Твари: конвейер ассетов

Blender-first конвейер для пяти видов: shambler, rusher, spitter, brute, lurker. Отдельный от агентского и намеренно.

Зачем отдельный конвейер

У агента слои бумажной куклы обязаны совпасть пиксель в пиксель: тело, броня и оружие рисуются на одном скелете, и любое расхождение видно сразу. Отсюда в tools/agent_assets.py калибровка, растровый ingest и проверка «экипировка не вылезла за силуэт тела».

У твари слоёв нет вовсе, склеивать нечего. Зато пять видов обязаны различаться силуэтом с одного взгляда: игрок принимает решение по пятну, а не по деталям. Поэтому здесь пять независимых скелетов, никакой общей калибровки — и проверка ровно обратного смысла: validate ругается, если два вида дали похожее пятно.

Общее у двух конвейеров ровно одно и намеренно: неподвижная ортографическая изометрическая камера (elevation_deg = atan(0.5), тот же угол, под которым в игре лежит пол) и вырезка кадра постоянным прямоугольником. Начало координат рига проецируется в центр кадра, поэтому упаковщик режет все кадры одинаково — тварь не «дышит» при повороте.

Файлы

Файл Что делает
assets/monsters/catalog.yaml геометрия ячейки, камера, список видов и клипов — единственный источник чисел
tools/blender/monster_rig.py анатомия и анимации; запускается ВНУТРИ Blender
tools/monster_assets.py CLI: рендер, упаковка листов, манифест, проверки
tools/build_monster_assets.ps1 полный прогон: init → render-all → validate → sf validate
assets/sources/monsters/*.png собранные листы (вход SpriteForge)
assets/manifests/monsters.generated.yaml манифест, пишется sync
generated/monster_sprites.h константы MONSTER_ASSET_* и MonsterAsset(EnemyKind, AnimKind)

Геометрия

Ячейка 48x56, pivot (24,47), восемь направлений в том же порядке, что у агентов: юг, юго-запад, запад, ... Лист direction-major: сначала все кадры юга, затем юго-запада.

pivot_y = 47, а не 55: изометрический пол уходит от точки опоры вниз по экрану, и всё, что у твари позади (хвост бегуна, задние лапы затаившегося), проецируется ниже точки опоры. Девять строк под pivot — запас на глубину подставки, а не воздух.

Клипы: walk 8, attack 6, hit 4, death 8 кадров на направление. fps_scale в каталоге задаёт повадку: бегун частит, громила волочится при одинаковом числе кадров.

Морфологии

Не капсулы. Каждый вид — своя анатомия, а не перекрашенный гуманоид:

  • shambler — вскрытая наружу грудная клетка, рёбра веером, правая рука доросла до колена, в череп вбита пластина; хромает;
  • rusher — четвероногий, горизонтальный хребет, пальцеходящие задние ноги, косы вместо предплечий, вместо головы раскрытый цветок челюстей;
  • spitter — волочащийся железистый мешок больше грудной клетки, вживлённый в горло клапан, сопло вместо лица, атрофированные руки;
  • brute — к плечу приварено второе, наполовину поглощённое тело; грудь забрана болтами; правая рука — молот из сросшейся кости;
  • lurker — плоский зашитый скобами панцирь подвешен под четырьмя длинными выгнутыми лапами, безглазый клин с крюками.

Анимации описаны в нормированном времени (список опорных значений на 0..1) и сэмплируются под число кадров из каталога: frames в каталоге можно менять, не трогая monster_rig.py.

death — это осадка в кучу, а не падение во весь рост. Лежащая во весь рост туша не помещается в клетку 48x56 при повороте «от камеры», и в игре это выглядело бы как обрезанный труп.

Порядок работы

python tools/monster_assets.py monster-init                 # собрать риг .blend
python tools/monster_assets.py monster-render shambler walk # один вид, один клип
python tools/monster_assets.py monster-render-all           # всё
python tools/monster_assets.py validate --strict
python tools/monster_assets.py sync
python -m unittest discover -s tools/tests

monster-render и monster-render-all сами вызывают sync.

Что проверяет validate

  • холст листа равен 48 x (8 направлений x кадры), пустых ячеек нет;
  • силуэт не упирается в кромку клетки — иначе в игре это отрубленная лапа;
  • тварь стоит на общей линии пола (death освобождён: труп имеет право осесть);
  • «роза» из восьми направлений центрирована на pivot_x;
  • зеркальные пары (ЮЗ,ЮВ), (З,В), (СЗ,СВ) совпадают при отражении — ловит перестановку направлений;
  • пять видов различаются пятном (IoU силуэтов шага ниже 0.90);
  • манифест не разошёлся с каталогом по pivot и размеру кадра, и ни один ID не объявлен вторым манифестом.

Запреты

  • ID ассета — строго <kind>_<animation>: ровно это требует tools/asset_coverage.py от группы enemy. Переименуешь — упадёт покрытие.
  • Строковых ID в C++ нет: только MONSTER_ASSET_* из generated/monster_sprites.h.
  • PNG и contact sheet не открывать: проверка только текстом — validate, sf list, sf describe, sf ascii.
  • assets/manifests/enemies.yaml — старые односторонние листы-заглушки. Пока живы оба файла, ID пересекаются, и validate про это предупреждает: снимать предупреждение — это отдельное решение на стороне рантайма, не конвейера.