lenec ru

← все посты

Animator vs Animancer vs ручная state machine: что выбрать для Unity

13K

На каждом проекте я через два месяца упирался в потолок Animator'а, на котором завязал персонажа. Потом смотрел на Animancer и думал «надо бы». На третьем проекте написал свою state machine и понял, что есть выбор не «одно из двух», а «инструмент под задачу». Расскажу, какой инструмент выбирать, что у каждого болит, и как живёт гибрид.

Опираюсь на Unity 6 (6000.x), Animancer 8.x.

Что у тебя есть из коробки: Mecanim Animator

Animator с Animator Controller — это та штука, в которую тебя приводит туториал. Графический state-machine: круги-состояния, стрелки-переходы, параметры (bool, int, float, trigger). Bленды через Blend Tree. Layers с Override и Additive.

Что хорошо:

  • Визуальный редактор — дизайнер может сам править переходы.
  • Blend Tree удобен для движения (idle/walk/run по скорости).
  • Avatar Mask и Layers позволяют миксовать анимации (бег + прицеливание сверху).
  • Root Motion из коробки.

Что болит:

  • State explosion. На 30 анимациях граф уже невозможно читать.
  • Любое изменение — клик-клик-клик в редакторе. В коде состояний нет, рефакторить — мучение.
  • Триггеры — глобальное состояние с непонятным временем жизни. Поставил Attack, забыл сбросить, через два кадра он сработал второй раз. Классика.
  • Sub-State Machines помогают, но всё равно не масштабируются.
  • Нельзя нормально из коды задать «играй вот этот клип сейчас» — нужно создавать состояние и переход в редакторе.
  • Производительность нелинейная. Чем больше параметров и переходов, тем медленнее CrossFade'ы.

Вывод: Animator подходит, когда у тебя меньше 15–20 состояний и стабильная анимация-логика. Платформер с 6 состояниями (idle/walk/run/jump/fall/attack) — на нём. Боевая система с 50 ударами и комбо — нет.

Animancer: код-фёрст альтернатива

Animancer (в Asset Store) — библиотека от Кибермэна, переписывает Animator под идею «играй конкретный клип, забудь про граф».

[SerializeField] private AnimancerComponent _animancer;
[SerializeField] private AnimationClip _idle;
[SerializeField] private AnimationClip _attack;

private void Update()
{
    if (Input.GetButtonDown("Fire1"))
        _animancer.Play(_attack);
    else if (!_animancer.IsPlaying(_attack))
        _animancer.Play(_idle);
}

Никаких триггеров и параметров. Хочешь воспроизвести клип — вызвал Play. Хочешь дождаться окончания — у тебя возвращается AnimancerState с событиями OnEnd.

private void Attack()
{
    var state = _animancer.Play(_attack);
    state.Events.OnEnd = () => _animancer.Play(_idle);
}

Что хорошо:

  • Логика анимации в коде. Версионируется в git, читается в IDE, рефакторится Find & Replace.
  • Можно создавать состояния и переходы динамически. Замена клипа на ходу — норма.
  • Хорошо работает с ScriptableObject'ами для движений (ClipTransition, LinearMixerTransition).
  • Нет state explosion.
  • Хорошее API для вложенных автоматов и слоёв.

Что болит:

  • Asset Store-зависимость. Лицензия pro-only, обновления — отдельная история.
  • Дизайнер не может тыкать мышкой. Кто-то всегда пишет код.
  • Нет встроенного визуального дебагера состояний (Inspector показывает текущее состояние, но граф — нет).
  • Кривая обучения у новичков выше, чем у Animator'а.
  • Root Motion работает, но настройка отличается, иногда через костыли.

Вывод: Animancer — оптимален для боевых систем, сложных боссов, кастомных AI с большим набором анимаций. Если у тебя в проекте 30+ состояний и команда программистов — стоит.

Своя ручная state machine

Третий путь — собственная state-machine плюс AnimationClipPlayable. Низкоуровневый Playables API в Unity позволяет проигрывать клипы без Animator Controller'а.

public class SimpleAnimator : MonoBehaviour
{
    [SerializeField] private Animator _animator;
    private PlayableGraph _graph;
    private AnimationMixerPlayable _mixer;
    private Dictionary<string, AnimationClipPlayable> _clips = new();

    private void Awake()
    {
        _graph = PlayableGraph.Create("SimpleAnimator");
        _graph.SetTimeUpdateMode(DirectorUpdateMode.GameTime);
        var output = AnimationPlayableOutput.Create(_graph, "Animation", _animator);
        _mixer = AnimationMixerPlayable.Create(_graph, 4);
        output.SetSourcePlayable(_mixer);
    }

    public void AddClip(string id, AnimationClip clip)
    {
        var playable = AnimationClipPlayable.Create(_graph, clip);
        _clips[id] = playable;
        _mixer.AddInput(playable, 0, 0f);
    }

    public void Play(string id, float fadeTime = 0.2f)
    {
        for (var i = 0; i < _mixer.GetInputCount(); i++)
            _mixer.SetInputWeight(i, 0f);
        // ... fade-in выбранного индекса
    }

    private void OnEnable() => _graph.Play();
    private void OnDisable() => _graph.Stop();
    private void OnDestroy() => _graph.Destroy();
}

Это упрощённый каркас. Реальная реализация — несколько сотен строк, с обработкой fade'ов, событий, лоопов, очередей.

Что хорошо:

  • Полный контроль. Знаешь, что и как происходит на каждом кадре.
  • Минимум зависимостей — только Unity API.
  • Производительность — самая высокая из трёх вариантов, потому что нет лишних слоёв.
  • Подходит для крайних случаев: процедурная анимация, генерируемые клипы, тысячи объектов.

Что болит:

  • Писать с нуля — недели работы.
  • Поддерживать — отдельная задача. Любая мелочь (event'ы анимации, root motion, layer'ы) — пишешь сам.
  • Документации нет, потому что это твой код.
  • Через год кто-то новый в команде смотрит на этот код и говорит «может, перепишем на Animator».

Вывод: своя машина — для очень специфичных случаев. Прокидной авто-боулинг с тысячей шаров — да. Обычный платформер — нет.

Что выбираю я в реальности

Зависит от проекта.

  • Прототип, gamejam — Animator. Быстрый старт, не надо думать.
  • Мобильный F2P с одним героем — Animator. Дизайнер сам соберёт граф, фичи добавлять не нужно часто.
  • ПК-инди с боевой системой и 50+ анимациями — Animancer. Окупится в первый же месяц.
  • Мобильная стратегия с тысячами юнитов — собственный решение поверх Playables (или GPU skinning через ECS, если совсем массово).

Часто гибрид: главные персонажи на Animancer'е, NPC-фон на Animator'е с простым графом, юниты массово — на собственном решении.

Технические штуки, которые работают везде

Animation Events vs события через код

Animation Events (callback'и в момент кадра анимации) — встроенная штука Unity. Удобна для попадания удара по тайму атаки, эффекта на шаге. Минус — теряются при ретаргетинге, требуют наличия скрипта на том же GameObject'е.

Альтернатива — события на аниматоре/Animancer'е по нормализованному времени. Программный, контролируемый.

Root Motion: на или off

Root Motion — когда анимация двигает корневой transform. Удобно для пере-сервированных анимаций (атаки с разворотом, прыжки с расстоянием). Болит, когда персонажа двигает контроллер по физике, а Root Motion накатывает сверху и они спорят.

Я обычно так: на персонаже Root Motion off, использую анимацию для визуала, движение через Rigidbody/CharacterController. Для специальных action'ов (контекстные взаимодействия, добивания) включаю Apply Root Motion на конкретный участок и снимаю обратно после события.

Animation Compression

Анимации FBX часто весят больше нужного. В Import Settings → Animation Type → Animation Compression поставь Optimal или Keyframe Reduction. Без видимой потери качества жмёт в 2–3 раза.

Avatar Mask

Когда нужно играть две анимации одновременно на разных частях тела (бег ногами + стрельба руками), Avatar Mask — твой друг. В Animator это второй слой с маской рук. В Animancer — отдельный AvatarMask на стейте.

Тесты и дебаг

Что я добавляю в каждый проект, независимо от выбора:

  • Дебаг-overlay. На экране кружок с именем текущей анимации, нормализованным временем, fade-in/out фазой. Спасает от часов поиска «почему персонаж застревает».
  • Хоткей «показать все клипы». В девелопменте — на F1 список всех клипов в проекте, можно тыкнуть и проиграть. Художники любят.
  • Логирование переходов. В девбилде — лог в консоль каждого Play/CrossFade с источником.

Что не делать

  • Не вкладывай 100 анимаций в один Animator Controller. Если граф не помещается на экран — пора рефакторить.
  • Не используй Animator.SetTrigger для логики, чувствительной к таймингу. Триггеры — для одноразовых событий, не для синхронизации.
  • Не пиши свою state machine, если у проекта дедлайн через два месяца. Не успеешь.
  • Не миксуй Animator и Animancer на одном персонаже одновременно. Мне попадались такие — это сложно и хрупко.

Чек-лист выбора

  1. Сколько анимаций у главного персонажа? <15 — Animator, 15–50 — Animancer, >50 или массово — своя.
  2. Кто будет править логику? Дизайнер — Animator. Программист — Animancer или своя.
  3. Нужен ли Root Motion из коробки? Animator проще всего.
  4. Нужны ли динамические клипы (не известные на этапе сборки)? Animancer или своя.
  5. Это пет-проект на пару месяцев или продакшн на год+? Соразмерно вкладывайся.

Если ещё не пробовал Animancer и проект не маленький — попробуй на отдельной ветке за выходные. Перенеси одного героя, сравни с Animator'ом. Часто решение принимается за пятнадцать минут после первой переписанной системы.

Комментарии 0

  • Будьте первым, кто оставит комментарий.

Войдите, чтобы оставить комментарий.