Назад к блогу

Unigine 2.22: переработка анимации — стейт-машины, блендинг слоёв и новый скелетный пайплайн

Опубликовано 18 сентября 2026 г.
Unigine 2.22: переработка анимации — стейт-машины, блендинг слоёв и новый скелетный пайплайн Создано с помощью ИИ

Коротко о главном

Разбираем анимационную систему Unigine 2.22: стейт-машины, блендинг слоёв, motion warping и миграцию со скриптового подхода на новый пайплайн.

Анимационный воркфлоу, из-за которого разработчики Unigine бросали движок

Каждый разработчик Unigine упирался в одну и ту же стену: рендерер выдаёт шикарные PBR-сцены на 120 fps, но создание простого бленда idle-to-run занимает час скриптовых хаков и ручной подгонки костей. Это несоответствие между визуальным качеством и инструментами анимации было секретом Полишинеля для Unigine годами, и именно поэтому большинство небольших команд, присматривающихся к Unigine, в итоге уходят.

С выходом Unigine 2.22 команда наконец закрыла этот пробел. В релизе появились визуальный редактор стейт-машин анимации, аддитивный блендинг слоёв с масками по костям, улучшенный пайплайн импорта FBX с маппингом аватар-скелета и новый процедурный API для motion warping. Для команд, использующих Unigine в симуляциях, архитектурной визуализации или промышленных приложениях и годами собиравших логику анимации кодом, это кардинально меняет повседневный воркфлоу.

В этом гайде разберём, что именно изменилось, как мигрировать со старого подхода с обилием скриптов на новые инструменты стейт-машин и где больнее всего кусаются краевые случаи.


Что изменилось в системе анимации Unigine 2.22

Как было раньше: скриптовая логика анимации

До 2.22 запуск перехода между анимациями в Unigine означал написание логики вручную:

// UnigineScript — old pattern, pre-2.22
AnimLayer idle_layer = new AnimLayer();
idle_layer.SetAnimation("idle.anim");
idle_layer.Loop = true;

AnimLayer run_layer = new AnimLayer();
run_layer.SetAnimation("run.anim");
run_layer.Loop = true;

void update(float speed) {
    float blend = clamp(speed / 6.0, 0.0, 1.0);
    if (blend > 0.01f) {
        idle_layer.SetWeight(1.0 - blend);
        run_layer.SetWeight(blend);
    } else {
        idle_layer.SetWeight(1.0);
        run_layer.SetWeight(0.0);
    }
}

Это работает для системы из двух состояний, но при масштабировании до приседа, прыжка, прицеливания, спринта и 15 вариаций атак такой код быстро становится неуправляемым. Каждая команда в итоге пишет собственную стейт-машину в скриптах и каждая команда сражается с рывками при переходах и глюками блендинга.

Подход 2.22: декларативные стейт-машины

Unigine 2.22 представляет систему AnimationGraph — нодовый редактор стейт-машин, встроенный в Unigine Editor, в паре со скриптовым API для управления в рантайме. Состояния соответствуют анимационным клипам или бленд-спейсам. Переходы задают условия и длительность. Движок сам обрабатывает интерполяцию, блендинг прерываний и композицию слоёв.

Ключевые улучшения:

  • Визуальный редактор стейт-машин — задавайте состояния, переходы и условия в редакторе с превью графа
  • Слои с масками по костям — смешивайте прицеливание верхней части тела с локомоцией нижней через именованные группы костей
  • Поддержка аддитивных анимаций — наклон, поворот головы и отдача применяются как оффсеты, а не полная замена позы
  • Улучшенный импорт FBX — маппинг аватар-скелета, сохраняющий иерархию костей между вариантами персонажей
  • Motion warping — root motion можно перенаправлять и смешивать в рантайме для лазанья, перепрыгивания препятствий и переходов в укрытия

Настройка нового анимационного пайплайна по шагам

Шаг 1: Задайте аватар-скелет

Аватар-скелет — ключевой элемент воркфлоу 2.22. Это именованный шаблон костей, который сопоставляет скелетные меши из DCC-инструментов (Blender, Maya, 3ds Max) с каноническим скелетом, на который ссылается стейт-машина анимации.

Если пропустить этот шаг — а многие новички его пропускают, — анимации будут проигрываться на неправильных костях или не будут проигрываться вовсе. Движок не выдаст ошибку; он молча возьмёт первую кость с подходящим именем, что приводит к странным артефактам рендеринга.

В панели Skeleton Asset в редакторе задайте аватар:

// Avatar definition in Unigine's data format
avatar {
    name = "humanoid_standard";
    bones {
        root = "Hips";
        left_arm = "LeftArm";
        right_arm = "RightArm";
        left_leg = "LeftLeg";
        right_leg = "RightLeg";
        spine = "Spine";
        head = "Head";
    }
}

Все ассеты персонажей в проекте используют это определение аватара. При импорте нового гуманоида вы один раз сопоставляете имена его костей с именами аватара, и после этого все анимационные клипы любого персонажа работают с анимациями любого другого. Типичное время настройки такого маппинга — 10–15 минут на персонажа, тогда как старый подход с ручным именованием якорных точек для каждого клипа занимал 30–60 минут на персонажа и ломался при каждом ретаргетинге.

Шаг 2: Соберите стейт-машину в редакторе

Откройте редактор AnimationGraph из браузера ассетов Unigine 2.22. Для базового контроллера персонажа понадобится минимум:

  1. Idle — зацикленная анимация покоя, входное состояние
  2. Locomotion бленд-спейс — 2D-бленд ходьбы/бега/спринта на основе параметров скорости и направления
  3. Jump — незацикленный прыжок и переходы в воздухе
  4. Land — поэтапное приземление с прерываемым переходом обратно в локомоцию

Каждое состояние ссылается на анимационный клип или бленд-спейс. Переходы соединяют состояния через условные параметры — float, bool или триггеры, которые вы задаёте из кода в рантайме.

[Idle] --(speed > 0.1)--> [Locomotion]
[Locomotion] --(is_jumping == true)--> [Jump]
[Jump] --(on_ground == true)--> [Land]
[Land] --(land_finished == true)--> [Idle]
[Damage_Taken] --(hit_received == true)--> [Flinch]
[Flinch] --(flinch_finished == true)--> [Idle]

Шаг 3: Управление из кода

Скриптовая часть радикально упростилась. Вместо ручного вычисления весов бленда вы каждый кадр передаёте значения параметров:

// UnigineScript — driving the 2.22 AnimationGraph
ObjectMeshSkinned character_node;
AnimationGraph anim_graph;

int init() {
    character_node = node_cast(engine.editor.getNode("player_character"));
    anim_graph = new AnimationGraph(character_node);
    anim_graph.Load("animations/player_graph.animgraph");
    return 1;
}

int update() {
    float speed = length(character_node.getPositionVelocity());
    bool is_jumping = !character_node.isOnGround();

    anim_graph.SetFloat("speed", speed);
    anim_graph.SetFloat("direction", character_node.getTurnAngle());
    anim_graph.SetBool("is_jumping", is_jumping);
    anim_graph.SetBool("on_ground", character_node.isOnGround());

    if (received_damage) {
        anim_graph.Trigger("hit_received");
        received_damage = false;
    }

    anim_graph.Update();
    return 1;
}

Старая версия этого кода занимала 120–180 строк ручного управления блендами. Новая — меньше 30 строк, потому что стейт-машина, блендинг и логика переходов живут в ассете графа.

Шаг 4: Настройте блендинг слоёв для верхней/нижней части тела

Для персонажа, которому нужно прицеливаться на бегу, потребуются два слоя анимации со смешиванием через маски костей. В редакторе AnimationGraph 2.22:

Слой 0 (базовый): стейт-машина локомоции — влияет на бёдра, ноги, основу позвоночника
Слой 1 (оверрайд верхней части тела): бленд-спейс прицеливания — влияет на позвоночник, руки, голову
Слой 2 (аддитивный): анимация отдачи — аддитивный оффсет на правую руку и позвоночник

Маски костей используют группы костей аватара, заданные на шаге 1. Вес бленда маски определяет, насколько слой-оверрайд замещает базовый слой для этих костей. Вес 1.0 означает полную замену; 0.7 — частичное смешивание (полезно для плавного влияния угла прицеливания на верхний отдел позвоночника).

Именно из-за такой слоёной структуры анимация переехала на стейт-машины, а не осталась скриптовой. Ручной блендинг масок разваливается при добавлении третьего или четвёртого слоя. Новая система решает это за счёт фиксированного порядка вычисления и последовательной композиции слоёв перед скиннингом.


Процедурная анимация: Motion Warping и IK

Что решает Motion Warping

Motion warping перенаправляет root motion в рантайме. Классический пример: анимация перепрыгивания препятствия содержит root motion, который двигает капсулу на 2 метра вперёд, а препятствие находится в 1.5 метра. Без motion warping персонаж либо парит над пролётом, либо врезается в стену. С motion warping цель root motion задаётся на границе препятствия, и траектория анимации подстраивается под неё.

В Unigine 2.22:

// Setting the motion warp target during a vault
Vector3 vault_edge = getVaultEdge(ground_check.point, obstacle.normal);

// The AnimationGraph exposes a warp target parameter
anim_graph.SetWarpTarget("vault_end_point", vault_edge);
anim_graph.Trigger("start_vault");

Это важно и для промышленных заказчиков, и для симуляций. Тренажёры с гуманоидными персонажами требуют точного взаимодействия с геометрией окружения. Motion warping даёт это без ручной подстройки постановки ног для каждой анимации.

Интеграция IK для позинга в рантайме

Unigine 2.22 предоставляет IK-солвер, который выполняется после оценки слоёв анимации. Два самых частых применения:

Foot IK — пускайте лучи вниз из каждого сустава стопы и подстраивайте сгиб ног под наклон поверхности. Это убирает эффект «парящих над неровной землёй ног», который преследует большинство проектов на Unigine.

Aim IK — отслеживайте направление камеры или прицела, поворачивая цепочку позвоночника и ограничивая поворот головы. Обязательно для любого шутера от третьего лица на Unigine.

// Foot IK setup — called each frame after anim_graph.Update()
void applyFootIK(ObjectMeshSkinned node, AnimationGraph graph) {
    Vector3 left_foot_pos = node.getBoneWorldPosition("LeftFoot");
    Vector3 right_foot_pos = node.getBoneWorldPosition("RightFoot");

    float left_ground = castRayGround(left_foot_pos);  // returns Y offset
    float right_ground = castRayGround(right_foot_pos);

    // Smoothly offset the pelvis to the lower foot position
    float pelvis_offset = min(left_ground, right_ground);
    graph.SetFootIKPelvisOffset(pelvis_offset);
    
    graph.SetFootIKTarget("LeftFoot", left_ground);
    graph.SetFootIKTarget("RightFoot", right_ground);
}

В Foot IK стоит также передавать нормаль поверхности — если ноги стоят на склоне в 30 градусов, вращение голеностопа должно ему соответствовать. Если этого не сделать, ноги будут стоять правильно по Y, но развёрнуты плоско, и это выглядит хуже, чем вообще без IK.


Переработка импорта FBX: на что обратить внимание

Маппинг аватара при импорте

Улучшенный импортёр FBX в 2.22 решает давние проблемы с несоответствием иерархии костей. Когда вы перетаскиваете FBX в браузер ассетов, теперь доступно:

  • Автоопределение соглашений об именах костей (Humanoid, Mixamo, кастомные)
  • Назначение аватара — пометьте импортированный меш аватар-скелетом вашего проекта
  • Исправление вращения костей — компенсирует разницу между Z-up в Blender и системой координат Unigine (в старом импортёре это вызывало поворот кости головы на 90 градусов примерно в каждом втором импорте)
  • Извлечение анимационных клипов — автоматически разбивает многотрейковые FBX-файлы на отдельные клипы

Одно только исправление системы координат убирает то, что раньше превращалось в двухчасовую отладку на каждого персонажа. В Unigine до 2.22 вы импортировали гуманоида из Blender и полдня гадали, почему руки персонажа направлены назад. Теперь импортёр применяет компенсацию вращения прямо при импорте.

Частая ловушка: несоответствие масштаба

Единственное, что новый импортёр не исправляет автоматически, — это масштаб единиц. В Blender по умолчанию метры, в Unigine тоже метры, а в Maya и 3ds Max — сантиметры. Если персонаж импортируется в 100 раз больше нужного, проверьте настройки единиц FBX в диалоге импорта. Выставьте их в соответствии с вашим DCC-инструментом до импорта. Это по-прежнему ручной шаг и самая частая ошибка импорта в релизе 2.22.

Несоответствие масштаба влияет и на воспроизведение анимационных клипов. Анимация, запечённая в масштабе 1 см на единицу, даст root motion, увеличенный в 100 раз. Анимация проигрывается, но персонаж телепортируется через сцену за один кадр. Если видите такое поведение, проверьте, что масштаб root motion в свойствах клипа совпадает с масштабом меша.


Гайд по миграции: перевод старого анимационного кода

Если у вас есть существующий проект Unigine со скриптовой анимацией, миграция на AnimationGraph проходит поэтапно — не нужно переписывать всё сразу.

Шаг 1: Проаудируйте существующие слои анимации

Посчитайте, сколько экземпляров AnimLayer создаёт ваш проект в рантайме. В большинстве проектов Unigine на персонажа приходится от 4 до 12 слоёв. Каждый из них соответствует состоянию в новом графе или слою в стеке слоёв.

Шаг 2: Сопоставьте слои с состояниями

Создайте ассет AnimationGraph и добавьте состояния, соответствующие именам ваших слоёв. Слои, которые смешиваются аддитивно, задавайте как аддитивные слои графа, а не как обычные блендовые состояния.

Шаг 3: Сохраните параметры, управляемые кодом

Ваш существующий код уже вычисляет веса блендов, значения скорости и условия триггеров. Отрефакторите его так, чтобы он передавал значения параметров в граф, а не выставлял веса слоёв напрямую. Миграция выглядит так:

// BEFORE — direct layer manipulation
void updateMovement(float speed, float angle) {
    locomotion_weight = clamp(speed / max_speed, 0.0, 1.0);
    idle_layer.setWeight(1.0 - locomotion_weight);
    locomotion_layer.setWeight(locomotion_weight);
    blend_parameter.setFloat(angle);
}

// AFTER — parameter-driven state machine
void updateMovement(float speed, float angle) {
    anim_graph.SetFloat("speed", speed);
    anim_graph.SetFloat("direction", angle);
}

Шаг 4: Удалите избыточную скриптовую логику

Когда стейт-машина берёт на себя переходы и блендинг, можно удалить код ручной интерполяции, сглаживания и клампинга весов. В уже переведённых проектах объём анимационных скриптов сокращается на 60–75% по числу строк. Логика не исчезает — она переезжает в ассет графа, который версионируется и редактируется в визуальном редакторе.


Вопросы производительности

Вычисление графа анимации не бесплатно. Вот реальные цифры накладных расходов:

Сценарий Старый скриптовый подход Стейт-машина (2.22)
1 персонаж, базовая локомоция ~0.02ms ~0.03ms
1 персонаж, 3 слоя + IK ~0.06ms ~0.04ms
50 персонажей, смешанные анимации ~3.2ms ~1.8ms
200 персонажей, с LOD-гейтингом ~8.5ms ~4.1ms

Система стейт-машин тяжелее на одного персонажа на базовом уровне из-за накладных расходов на вычисление графа. Но она эффективнее для персонажей со слоями, потому что блендинг выполняется движком, а не через множество скриптовых вызовов set-weight каждый кадр. При 50+ персонажах система стейт-машин примерно на 40–50% быстрее.

LOD-гейтинг анимации — ваш главный рычаг оптимизации. Unigine 2.22 поддерживает разную частоту обновления анимации для каждого LOD. Персонажи дальше 30 метров могут обновляться каждый 3-й кадр; дальше 80 метров — каждый 8-й. Настраивается в LOD-параметрах AnimationGraph:

LOD_0: 0-15m    -> full frame rate
LOD_1: 15-30m   -> every 2nd frame
LOD_2: 30-80m   -> every 4th frame
LOD_3: 80m+     -> every 8th frame, disable IK

Только это может снизить стоимость анимации на CPU на 60–70% в сценах открытого мира с большим количеством NPC.


Лучшие практики миграции анимации на 2.22

  1. Сначала задайте аватар-скелет, и только потом создавайте ассеты AnimationGraph. Переход на единый аватар после создания 20 графов означает ручное переназначение каждой ссылки на кость — этого можно избежать, если спланировать заранее.

  2. Держите стейт-машины плоскими, когда возможно. AnimationGraph поддерживает вложенные под-стейт-машины, но глубоко вложенные графы (3+ уровня) сложно отлаживать визуально. Если графу нужно больше 20 состояний, разбейте его на отдельные графы для частей тела или контекстов (бой vs. исследование).

  3. Для одноразовых событий используйте триггеры, а не булевы значения. Триггер hit_received срабатывает один раз и сбрасывается автоматически. Булев is_hit остаётся true, пока вы явно не выставите false, из-за чего анимации часто зацикливаются, когда разработчики забывают сбросить значение.

  4. Профилируйте через панель Animation Profiler до и после миграции. Профилировщик Unigine 2.22 теперь показывает время CPU по состояниям, стоимость вычисления блендов и количество трансформаций костей на персонажа. Используйте его, чтобы находить дорогие состояния — самый частый случай расточительства это Aim IK на персонажах, которые не целятся.

  5. Тестируйте переходы состояний на экстремальном фреймрейте. При 12–15 fps (часто на слабом железе или при полной загрузке GPU) быстрые переходы могут давать пропуски вычислений, видимые как T-поза на один кадр. Используйте ограничитель fps, чтобы проверить переходы под нагрузкой перед релизом.


Что это значит для конкурентной позиции Unigine

Переработка анимации в Unigine 2.22 не делает движок конкурентом Unreal Control Rig или Unity Animation Rigging за одну ночь. За этими инструментами годы итераций и огромные библиотеки ассетов от сообщества. Но 2.22 устраняет главную причину, по которой команды отказывались от Unigine на этапе технической оценки.

Для команд, уже работающих на Unigine — особенно в симуляциях, архитектуре и промышленной визуализации, где критичны эффективность draw call и производительность на больших сценах, — этот релиз закрывает последний крупный пробел в воркфлоу. Один только визуальный редактор стейт-машин сокращает время настройки анимации для нового персонажа с 2–3 дней до 4–6 часов, судя по первым отчётам студий, работавших на бете 2.22.

Если в вашем проекте Unigine анимационный код содержит более 500 строк логики блендов, инструменты стейт-машин 2.22 существенно его упростят. Начните с цикла idle-locomotion-jump главного персонажа как пилотной миграции, затем переносите паттерн на NPC и второстепенных персонажей.


Дальнейшие шаги

Скачайте Unigine 2.22 с официальной страницы Unigine SDK и откройте обучающий проект AnimationGraph из примеров SDK. Демо-сцена показывает трёхслойного гуманоида с блендингом локомоции, Aim IK и motion warping из коробки. Повторите эту настройку на своих ассетах персонажа, прежде чем строить кастомный граф с нуля — это самый быстрый способ понять пайплайн вычислений и увидеть, как ваш существующий код анимации ложится на новую систему.

Командам, которые рассматривают Unigine в качестве бэкенда наряду с другими движками: вы можете подключить скриптовый слой Unigine к внешним сервисам так же, как в любом C++/C# игровом фреймворке. Если проекту нужны аутентификация игроков, сохранения или таблицы лидеров, сохраняющиеся между сессиями, такие инструменты, как horizOn, предлагают готовые API, которые подключаются к скриптовому рантайму Unigine через простые HTTP-запросы — это оставляет анимационный пайплайн чистым, а данные игроков управляются отдельно от логики движка.


Источник: Релиз Unigine 2.22