- кадр
- —
- время
- —
- файл
- —
- объектов
- 18 794
Файл едет к вам целиком — вместе с восемью вложенными PNG. Всё, что вы видите, считает wasm-рантайм Rive у вас в браузере: сервер отдаёт статику и больше ничего не делает.
Как это получается
Восемь шагов от рисунка до файла. Инструмент написан один раз; на каждого следующего персонажа повторяются шаги 1–8, и все они бесплатные — деньги тратит только рисование рефа.
-
Реф в рабочей позе
Вход всего пути — один рисунок, не лист кадров. Фон хромакеем, разрешение с запасом (1696×2528 при игровом кадре 576×624), поза рабочая: риг умеет ±90° вокруг нарисованного положения.
-
Скелет объявляется по рисунку
Единственный ручной вход конвейера — двадцать строк данных: полилиния кости, радиус, родитель, порядок по глубине. Координаты снимаются с самого рефа: он в аниме-пропорциях, и конвейерный скелет резал бы по костям, которых на картинке нет.
-
Нарезка на детали
Деталь — трубка переменного радиуса вдоль своей кости, обрезанная силуэтом. Скрытое (39% площади, у шеи 88%) закрашивается ближайшим видимым пикселем той же детали. Двадцать PNG и карта пивотов.
-
Доказать, что карта не врёт
Детали ставятся обходом дерева и обязаны собраться обратно в реф. Мировых координат в карте нет нарочно — иначе сборка проверяла бы сама себя. IoU 0.9901, цвет 0.9607.
-
Сетка и развесовка — числом, не на вкус
Шаг сетки обязан быть субпиксельным, а следующий более редкий — уже нет. Схема весов выбирается по одной мере: изменившиеся пиксели у локтя, делённые на изменившиеся у плеча. Разница между рукой и шлангом измеряется, а не обсуждается.
-
Цикл движения — из канона
Позы не сочиняются: берётся FROZEN["walk"] боевого конвейера — мокап CMU с ретаргетом на длину шага движка.
-
Сборка .riv
Деталь → Image + Mesh + MeshVertex; скелет → RootBone/Bone; привязка → Skin + Tendon + Weight; поза → LinearAnimation + KeyFrameDouble. Полторы секунды на файл.
-
Доказать пикселями настоящего рантайма
Судья — chromium, а не наши расчёты: у node-дубля нет WebGL, и без него image mesh не рисуется вовсе — оба кадра вышли бы одинаково пустыми и совпали бы идеально. Плюс шесть контрольных файлов, каждый обязан провалиться.
Чем это доказано
У каждого утверждения — отрицательный контроль: файл, который обязан провалить ту же проверку. Утверждение без такого контроля в лаборатории не считается доказанным.
| утверждение | как доказано | отрицательный контроль |
|---|---|---|
| персонаж ходит | 5 пар соседних кадров расходятся на 21.5–22.1% | цикл замкнут побитово: 0 px |
| порядок всех 20 деталей по карте | цвет 0.9560 против эталона из карты | --zflip 0.8084 и --tflip 0.8796 |
| скелет — одно дерево | корень тянет все 8 мешей, 19.7% кадра | у восьми корней меши стояли бы |
| привязка работает | вершины ездят | --unbind: 0 различающихся пикселей при непустом кадре |
| сгиб локален | 1676 px у колена, 0 у бедра | линейная рампа: отношение 6.71 |
| наша математика верна | модель предсказывает кадр рантайма, цвет 0.9810 | предсказание для другого момента краснеет |
Измеренные числа
Таблица не переписана руками: она разбирается из README.md репозитория, где каждая строка пересчитывается воротами из артефакта прогона. Подделанная цифра красит ворота.
| величина | измерено | откуда число |
|---|---|---|
| загружается… | ||
Что оказалось неправдой
Опровергнутые гипотезы — каждая с числом. Дороже всего обошлись первые две.
«Силуэт покажет, что персонаж собран правильно»
Нет. Силуэт слеп к порядку отрисовки полностью: IoU 0.9956 у правильного файла, 0.9956 у файла с перевёрнутым потоком и 0.9956 у файла с перевёрнутой склейкой текстур — до четвёртого знака. Ловилось только цветом (0.9560 против 0.8084) и только сверкой с эталоном, построенным не из проверяемого файла. Три раза одна ошибка, три раза одни цифры.
«Первый в потоке рисуется первым»
Наоборот: Rive рисует drawables артборда задом наперёд, как редактор рисует список слоёв. Собранный «по возрастанию z» файл дал дальний рукав поверх груди — 18 977 пикселей из 409 600 при том же силуэте. Это и есть третья вкладка демонстрации выше.
«Формат читается по одним публичным defs»
Наполовину. Номера — да, 371 объект и 3368 свойств. Но правил провода там нет вовсе: bool — сырой байт, Color — 4 байта без varuint, битмап оглавления идёт по 4 ключа на слово uint32, а не по 16. Плюс старые номера свойств: без них не читается больше половины публичных файлов. Источник правды — открытый C++ рантайма, документация в одном месте прямо вводит в заблуждение.
«Убитый прогон мутаций ничего не оставляет»
Оставляет. SIGKILL идёт мимо finally, и порча уехала в коммит вместе с журналом, написанным по числам чистого сборщика: независимый прогон дал 62 из 64 там, где отчёт заявлял 64 из 64. Правило: мутации не запускать в фоне, а отчёт агента считать заявкой, а не фактом.
«Проверить .riv можно в node»
Нельзя, если в файле картинки: загрузка там висит вечно — промис ждёт декодирования ассетов через createImageBitmap, которого в node нет. Вторая половина беды: у node-канваса нет WebGL, и рантайм честно печатает «Image mesh will not be drawn» — оба кадра вышли бы одинаково пустыми и совпали бы идеально. Авторитет по мешам — только браузер.
«Заглушка под перекрытыми кусками лечится только деньгами»
Так было записано, когда сгиб обоих локтей на 90° обнажал плоскую растяжку на 19.4% видимой площади торса. На целом персонаже в ходьбе вся заглушка кадра не превышает 1.12%: рука качается на ±6°, а не складывается вдвое. Цифра 19.4% была верной только для движения, которое тот этап себе сочинил. Дорисовка за деньги на походку не нужна.
«Ветвление скелета просто работает»
Не работает: у дочерней кости в Rive нет своих координат, x() = parent->length(). Десять костей-связок — не украшение, а единственный способ записать дерево карты, не двигая суставы под формат.
«Ворота офлайна работают»
Были декоративными: проба била в example.invalid, а это зарезервированный TLD — он не резолвится нигде и без всякого запрета. Отсюда общее правило лаборатории: у любого отрицательного утверждения проба обязана уметь дать положительный ответ в мире, где утверждение неверно.
Вердикт
За что путь берут
- Один рисунок вместо листа кадров.
- Файл вчетверо легче листа: 1 МБ против 4.2 МБ.
- В тридцать раз легче по текстурной памяти: 4.36 МиБ против 142.6 МиБ.
- Любое движение добавляется кодом за минуты и без единого доллара.
- Качество деформации доказано числами, а не глазами.
Чего он стоит и чего в нём нет
- Новый рантайм в проекте, где сейчас PixiJS: +1.8 МБ wasm на клиента.
- Проверен ровно один цикл движения — кит ударов впереди.
- Диапазон ±90° вокруг нарисованной позы. Это ограничение рефа, а не формата, и лечится рисованием.
- Плоский меш не умеет самоперекрытий: ни рука за спину, ни нога накрест.
- Просадка объёма в суставе 7.85% — линейный блендинг, другого в Rive нет.
Для локомоции путь рабочий и его стоит брать; для кита ударов — не раньше, чем появится реф в нейтральной стойке. Практический ход: следующего персонажа рисовать рефом в нейтральной стойке и пройти по этому конвейеру целиком — тогда станет видно, снимается ли ограничение №2, а это единственный вопрос, который решает судьбу всего пути.