Анимация персонажей

Статус: план. Решения зафиксированы 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 / Walk1
1 — Armsруки и их детиAttackOverride1
2 — Blockрука со щитом + щитBlock-позаOverride0→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, читаемость попадания
Возвратостальноетяжёлое восстановление

Три правила сверх таймингов:

  1. Удар — единицы кадров, не десятки. Двадцать пять кадров на замах меча читаются как вата независимо от кривых.
  2. Оверлаппинг обязателен. Части не двигаются синхронно: предплечье отстаёт от плеча на кадр, оружие от предплечья — на два, голова от корпуса — на кадр. Оружие должно догонять руку, а не ехать приклеенным. Крайние ключи при этом остаются на месте.
  3. Тангенсы по фазам, не плоские. Плоский тангенс на каждом ключе = микро-остановка; удар стартует с нулевой скорости и разгоняется вместо взрыва. Нужно: резкий выход из 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.