Анимация персонажей
Статус: план. Решения зафиксированы 2026-07-26, реализация отложена — возвращаемся не сегодня. Здесь лежит намерение, принятая архитектура и готчи, чтобы следующий заход не начинался с нуля.
Стилевая рамка, внутри которой это живёт — Vision - Visual Direction.
Намерение
Узнаваемый язык движения по цене, которую тянет соло-разработчик. Персонажи анимируются скелетно (кости), а не покадрово: покадровка на ~30 юнитов не окупается, а нейросети сильнее помогают с картинкой, чем с покадровкой.
Уникальность берём не из экзотического инструмента, а из отдельного слоя движения поверх обычного рига (см. ниже).
Ключевое решение: два слоя движения
Анимация персонажа делится на два независимых слоя, и это главный шов всей темы.
| Слой | Что это | Где живёт | Каким должен быть |
|---|---|---|---|
| 1. Риг | скелет, Idle / Walk / Attack / Hit / Death | клипы Animator | намеренно скучным — его работа быть дешёвым и переиспользуемым |
| 2. «Глаголы движения» | карточный поворот в 3D, дуга выпада, отскок, сплющивание, дрожь | код, твины LitMotion на корневом трансформе | здесь живёт вся узнаваемость |
Почему так, а не «сделать риг покрасивее»:
- Поворот спрайта «как карточки в 3D» невозможен внутри 2D-скелетки — кости плоские, у них нет перспективного разворота. Зато он тривиален как вращение родительского трансформа над всем ригом.
- Слой 2 пишется один раз набором переиспользуемых глаголов (
SpinCard,Lunge,Anticipate,RecoilArc) и применяется к любому существу. Уникальность масштабируется на весь бестиарий бесплатно. Вложи её в клипы — платил бы за неё на каждом архетипе. - Слой 2 инструментонезависим: он переживёт любую смену анимационного софта.
Ложится в существующий контур gamefeel-vfx (CombatFeelDirector + LitMotion), а не в
Animation window.
Правило, из которого всё следует: корневой трансформ юнита аниматор не трогает — значит он целиком территория кода, без конфликтов и масок.
Три оси переиспользования
Анимации делаются один раз на тип существа и архетип оружия, а не на каждого юнита. Оси разводятся в три независимых ассета:
| Ось | Что это | Пример |
|---|---|---|
| Скелет | тип существа | rig.humanoid, rig.quadruped, rig.flyer |
| Набор клипов | архетип оружия | anim.humanoid.spear, anim.humanoid.sword_shield |
| Набор спрайтов | конкретный юнит | lib.unit.pikeman |
Железное правило (ретрофит дорогой): имена и иерархия костей у всех ригов одного типа существа идентичны. Клипы под копьё и под меч анимируют одни и те же кости — расхождение в одной кости обнуляет всё переиспользование.
Оружие не анимируется отдельными кривыми там, где оно должно быть взаимозаменяемым: предмет — дочерний трансформ конечности с нулём собственных кривых, он едет за костью сам. Исключение — клипы, написанные под конкретную связку (например атака «меч и щит», где щит участвует в постановке): там оружие часть хореографии и это осознанно.
Объём на старте: один гуманоидный скелет, 2-3 архетипа оружия. Звери и летуны — позже.
Инструмент
Принято: остаёмся на текущей связке — иерархия Transform + SpriteRenderer + Animator.
Стоимость $0. Ничего не покупаем.
Кость — это просто трансформ, так что связка уже является скелетной анимацией. Всё, что продаётся сверху — веса, деформация мешей, IK и удобный редактор кривых. Из них:
- Меш-деформация отвергалась потому, что растянутые вершины размазывали тексели и аккуратные пиксельные блоки превращались в кашу. Аргумент устарел — см. врезку ниже.
- IK для автобатлера почти не нужен: ноги не цепляются за неровный рельеф.
Довод против меш-деформации опирался на пиксель-арт, а он отменён (
2026-08-01/14)Для плоского сторибука с solid-заливкой размазывать нечего: именно так деформируют Wildfrost и Cult of the Lamb. Вывод «не покупаем» пока в силе по остальным причинам (цена, UX редактора), но эту строку надо пересматривать осознанно — решение за Максом, а не тихой правкой.
- Остаётся UX редактора — единственное, за что имело бы смысл платить.
Пересмотр — после третьего юнита. Критерий один: борьба с Animation window стала дороже самой анимации. Тогда:
| Запасной вариант | Цена | За что |
|---|---|---|
| AnyPortrait | $49 | работает внутри Unity — нет стороннего рантайма и моста к нему; кости, IK, меши, jiggle-bones (физика хвостов/плащей), импорт PSD, C#-API, URP |
| Spine Essential | $69 | зрелость экосистемы и лучший в классе редактор; скины входят; апгрейд до Pro в любой момент за разницу в цене |
Отвергнуто и почему:
- Spine Professional ($379) — переплата за меши/FFD/IK, которые нам вредны или не нужны.
- PixelOver ($19) — умеет кости и даже 3D-поворот костей, но запекает в спрайт-шиты, а нам нужна честная скелетка в рантайме. Хорош для иконок и эффектов, не для этой задачи.
- DragonBones — редактор ушёл в LoongBones (AI-фичи, платные планы, неясная судьба). Рантаймы на GitHub живы, но ставить соло-проект на такое нельзя.
- Creature 2D — Unity-рантайм есть, проект давно без движения.
- Spine Education — лицензируется только учебным заведением, на год, и запрещает коммерческое использование. Игра коммерческая. Студенческой скидки у Esoteric нет.
- 3D→спрайт (пайплайн Dead Cells) — решает проблему поворотов, которой у нас нет (персонажи смотрят только влево/вправо); требует 3D-конвейера и своего рендерера запекания.
Юридическая заметка. Лицензия spine-рантаймов требует, чтобы у каждого, кто их интегрирует, была своя лицензия редактора. Схема «анимирую в стороннем редакторе, экспортирую spine-json, играю опенсорс-рантаймом» — запрещена.
Смешивание анимаций: слои Animator + AvatarMask
Задача: ноги идут, руки бьют, и отдельно — юнит выставляет щит за секунду до удара по внешнему сигналу. Решается одной механикой — слоями с масками, а не кодом по костям.
| Слой | Маска (трансформы) | Клип | Blending | Вес |
|---|---|---|---|---|
| 0 — Base | корпус, голова, ноги | Idle / Walk | — | 1 |
| 1 — Arms | руки и их дети | Attack | Override | 1 |
| 2 — Block | рука со щитом + щит | Block-поза | Override | 0→1 кодом |
Маски не пересекаются, поэтому слои физически не могут помешать друг другу. Слой Block стоит выше Arms и поэтому перебивает атаку рукой со щитом, когда его вес поднимается.
Бить на ходу нужно не всем юнитам (копейщик тычет в движении, двуручник замирает для замаха) — то есть слои нужны как инструмент под часть бестиария, а не как общий режим.
Реакция щитом = вес слоя, а не код по костям
Поза блока рисуется руками (клип в 1-2 ключа), а кодом гоняется единственный float:
SetLayerWeight через LitMotion, 0→1 за ~0.15 с, удержание, 1→0 за ~0.3 с.
Почему так:
- поза выглядит намеренной, с характером — процедурный доворот кости этого не даст;
- работаем механикой самого аниматора, а не воюем с ним;
- плавность въезда и ухода бесплатно, без единого лишнего клипа;
- приём переиспользуется: «вздрогнул от урона», «прицелился», «заряжает каст».
Готчи
Generic Avatar обязателен. Для generic-рига маскирование по трансформам требует назначенного
Avatar. У сценового объекта его нет и он не создаётся сам — маски будут молча игнорироваться, без ошибки и предупреждения. Строится вызовомAvatarBuilder.BuildGenericAvatar(go, "")и сохраняется отдельным ассетом: иерархия у всех гуманоидов одна, аватар нужен ровно один на всех.
LateUpdateненадёжен. Если слой аниматора анимирует те же трансформы, аниматор выигрывает и правки стираются каждый кадр. Ручной код по костям работает только на трансформах, которых нет ни в одном активном клипе — то есть на корне (слой 2 выше).
Мёртвые кривые перебивают слои. Константная кривая, случайно записанная режимом Record, продолжает писать значение и заглушит другой слой, если попадёт под маску. Чистить их нужно со сверкой по префабу: если константа не совпадает с позой префаба, удаление кривой сдвинет часть тела.
Довороты «щит смотрит в сторону конкретного атакующего» — при необходимости через 2D IK
(работает с обычной иерархией трансформов, без SpriteSkin; входит в com.unity.2d.animation).
Принципы тайминга атаки
Резкость удара берётся не из общей скорости клипа, а из контраста между медленным замахом и мгновенным ударом. Эталонная раскладка (проверена на первом юните, 60 fps):
| Фаза | Доля | Смысл |
|---|---|---|
| Замах | ~10 кадров | оттяг назад |
| Задержка на замахе | ~10 кадров | накопление напряжения (anticipation) |
| Удар | ~5 кадров | всё движение целиком |
| Удержание контакта | ~7 кадров | impact hold, читаемость попадания |
| Возврат | остальное | тяжёлое восстановление |
Три правила сверх таймингов:
- Удар — единицы кадров, не десятки. Двадцать пять кадров на замах меча читаются как вата независимо от кривых.
- Оверлаппинг обязателен. Части не двигаются синхронно: предплечье отстаёт от плеча на кадр, оружие от предплечья — на два, голова от корпуса — на кадр. Оружие должно догонять руку, а не ехать приклеенным. Крайние ключи при этом остаются на месте.
- Тангенсы по фазам, не плоские. Плоский тангенс на каждом ключе = микро-остановка; удар стартует с нулевой скорости и разгоняется вместо взрыва. Нужно: резкий выход из idle, торможение в верхней точке замаха, лёгкий довзвод и разгон в контакт без торможения, плавное затухание на возврате.
Открытые вопросы
- Апскейл спрайтов при экспорте. Холст в Aseprite плавающий (порядка 96×96), экспорт с апскейлом. x10 даёт чистые границы блоков при повороте, но раздувает атлас в сто раз. Гипотеза — x2–x4 достаточно, остаток масштаба добирается через Pixels Per Unit. Мерить глазами на одной руке, повёрнутой на 7°: x1 / x4 / x10.
- Сортировка частей. Обе руки наследуют одинаковый sorting order из общего префаба
конечности — дальняя рука рисуется поверх торса. Нужен
SortingGroupна корне юнита (иначе юниты будут прорастать друг сквозь друга на арене) плюс переопределение order на инстансе дальней конечности. - Длина и зацикленность клипа атаки — подбираются под реальную скорость атаки в бою.
- Смешивать покадровых и скелетных юнитов нельзя. Разница в чистоте краёв читается как баг, а не как стиль. Решение «все или никто» нужно принять до пятого юнита.
Связь: Vision - Visual Direction (стилевая рамка, треки визуала), Vision - Concept (запоминаемость), Meta - Decision Journal.