Осваиваем Playables API для анимации в Unity

Думаю, многим, кто работает с Unity, приходилось работать с анимациями. После кропотливой работы с аниматором, на выходе получалось такое:

Ад машины состояний аниматора

Ад машины состояний аниматора

Для меня осталось загадкой, почему Playables API остался без обзоров и туториалов, и я постарался это исправить. Playables API — это мощный инструмент для работы с анимацией в Unity, который предлагает ряд преимуществ по сравнению с традиционными методами анимации:

  • Управление временем

  • Playables API позволяет вам программно управлять анимацией

  • API Playables позволяет динамически смешивать анимации

  • PlayableGraph можно создать во время выполнения вместо того, чтобы иметь огромный «универсальный» аниматор

PlayableGraph — это сердце Playables API в Unity. Представьте его как «сцена» или «полотно», на котором вы располагаете и связываете разные Playable-объекты, чтобы создать сложную анимацию.

К Playables API я прибегнул при решении конкретной задачи, разрабатывая решение для создания сетевых детерминированных файтингов на Unity. Нужно было получить производительный и гибкий инструмент для:

  • Рендера просчитанной анимации

  • Запекания позиций костей на всей продолжительности анимационных клипов

Я не буду вдаваться в подробности просчета времени анимаций/переходов, а сфокусируюсь только на воспроизведении клипов и основных вызовах API.

Преимущество подхода

Начнем с того что Playables позволяет нам вынести клипы в аккуратный ScriptableObject:

7987a4fc5feb206b4fa13c5baed8436f.png

Который в свою очередь содержит наборы scriptableobject с информацией наших «анимациях»:

13e9209ea8748643ec045ab41e4f361e.png

Запекание анимации (позиций костей)

Данный пример проще для разбора, так как использует базовые возможности api. Для понимания технической стороны запекания:

  1. В костях персонажа создаются якоря

  2. Симулируем воспроизведение клипа

  3. Получаем и записываем данные

1 и 3 пункты мы опустим, так как они не имеют отношения к api. Рассмотрим воспроизведение:

Первым делом нужно создать PlayableGraph. Создаем PlayableGraph и запустим при старте через вызов playableGraph.Play ():

private PlayableGraph playableGraph;
private AnimationClipPlayable clipPlayable;
private int bakedClipIndex; 

private void Start()
{
    playableGraph = PlayableGraph.Create();
    playableGraph.Play();
}

AnimationClipPlayable — это один из основных типов Playable-объектов в Playables API, который непосредственно управляет воспроизведением анимации, хранящейся в AnimationClip.

На данном этапе ничего не произойдет, так как наш PlayableGraph пустой. Чтобы воспроизводить анимации, нам нужно заполнить его данными. Для получения клипа обратимся к scriptableobject с изображения выше:

public class AnimationSet : ScriptableObject
{
    [field: SerializeField] public AnimationClip AnimationClip { get; private set; }
    //...
}

Далее создадим coroutine который будет:

  1. Получать информацию о клипе и создавать AnimationClipPlayable

  2. Создавать AnimationPlayableOutput и передавать в него AnimationClipPlayable

  3. Симулировать проигрывание анимации, продвигая время на Time.fixedDeltaTime

AnimationPlayableOutput — это как «мостик», который связывает вашу анимацию, управляемую Playables API, с реальным объектом в игре, который будет анимироваться.

private IEnumerator BakeProcess()
{
    // Тут animator это ссылка на Unity animator на вашем персонаже
    var playableOutput = AnimationPlayableOutput.Create(playableGraph, "AnimationsBaker", animator); 
    // Создаем clipPlayable получая ссылку на клип animationSet.AnimationClip
    // В реальной задаче я прохожусь по всем animationSet используя bakedClipIndex
    clipPlayable = AnimationClipPlayable.Create(playableGraph, animationSet.AnimationClip); 
    playableOutput.SetSourcePlayable(clipPlayable); // задаем clipPlayable в playableOutput
    // Время анимации 
    double currentTime = 0f;
    while (true)
    {
        currentTime += Time.fixedDeltaTime;
        // Именно SetTime отвечает за проигрывание клипа, продвигая его время на currentTime
        clipPlayable.SetTime(currentTime);

        // Получение позиции якорей и запись данных
      
        // Если достигли длины клипа можем брать след. клип
        if (currentTime >= animationSet.AnimationClip.length)
        {
            bakedClipIndex++;
            StartCoroutine(BakeProcess());
            break;
        }
        yield return null;
    }
}

55b7f5cb018fd0797e2369f5c319aa52.gif

Стоит отметить, что данный функционал возможно реализовать используя возможности API аниматора, но Playables дает больше возможностей, гибкости и, как заявлено, производительности. Если данная тема востребована, в следующей части мы рассмотрим продвинутый пример использования Playables для анимации персонажа, который не уступает по функционалу обычному аниматору.

Мой канал в телеграмм Unity Show-Off для поиска вдохновения. Мой блог.

© Habrahabr.ru