Реалистичная анимация персонажей в играх с помощью ИИ
Разработчиками из Эдинбургского Университета представлен новый алгоритм для создания реалистичных движений персонажей в играх. Обученная на Motion Capture траекториях нейросеть пытается копировать движения реальных людей, но при этом адаптирует их под персонажей видеоигр.
Одна нейросеть способна управлять сразу несколькими действиями в игре. Открывание дверей, перенос предметов, использование мебели. При этом она динамично изменяет положения ног и рук, чтобы персонаж мог реалистично держать ящики разного размера, садиться на разные по размеру стулья, а также пролезать в проходы разной высоты.
Обычно под управлением персонажами в играх с помощью ИИ подразумевается полный контроль усилий в конечностях, основанный на каком-нибудь физическом движке, имитирующем законы физики. Этим занимается область машинного обучения под названием Reinforcement Learning. К сожалению, таким способом пока не удается достичь реалистичных движений.
С другой стороны, можно попытаться обучить нейросеть имитировать движения реальных людей, снятые с помощью Motion Capture. Именно таким способом около года назад был достигнут значительный прогресс в реалистичной анимации 3d персонажей.
По этой теме было несколько последовательных научных работ, но наиболее полное описание можно почитать в работе Towards a Virtual Stuntman, посвященной нейросети DeepMimic (https://www.youtube.com/watch?v=vppFvq2quQ0).
Основная идея заключается в том, чтобы при обучении имитировать движения человека начинать эпизод не с самого начала Motion Capture трека, как делали раньше, а из случайных точек по всей траектории. Существующие алгоритмы Reinforcement Learning исследуют окрестности точки старта, поэтому до конца траектории чаще всего не доходили. Но если каждый эпизод начинать вдоль всего трека, то увеличивается шанс, что нейросеть обучится повторять всю траекторию.
Позднее эту идею подхватили и совсем в других областях. Например, обучая нейросеть на прохождениях игр людьми, и тоже начиная эпизоды не с начала, а из случайных точек (конкретно в этом случае с конца, и постепенно сдвигаясь к началу), в OpenAI обучили нейросеть проходить игру Montezuma’s Revenge. Которая не поддавалась до этого обычным Reinforcement Learning алгоритмам.
Без этого трюка попытки обучить нейросеть копировать сложные движения заканчивались неудачей, потому что нейросеть находила более короткий путь. Пусть не дающий такую большую награду, как за всю траекторию, но все же какая-то награда была. К примеру, вместо того чтобы делать кувырок назад, нейросеть просто слегка подпрыгивала и плюхалась на спину.
Но с таким подходом, нейросеть без проблем изучает траектории практически любой сложности.
Основная проблема DeepMimic, мешавшая напрямую применить ее к видеоиграм, заключается в том, что не удалось обучить нейросеть выполнять сразу несколько разных анимаций. Приходилось на каждую анимацию обучать отдельную нейросеть. Авторы пробовали объединять их разными способами, но больше 3-4 анимацией скомбинировать не удавалось.
В новой работе эта проблема тоже не устранена до конца, но сделан большой прогресс по плавному переходу между разными анимациями.
Надо отметить, что этой проблемой страдают все существующие на данный момент подобные анимационные нейросети. Например, эта нейросеть, тоже обученная на имитации Motion Capture, способна на физическом движке честно управлять огромным числом мышц (326!) гуманоидного персонажа. Адаптируясь к разному весу поднимаемых тяжестей и различным повреждениям суставов. Но при этом для каждой анимации нужна отдельная обученная нейросеть.
Стоит понимать, что цель подобных нейросетей не просто повторить человеческую анимацию. А повторить ее на физическом движке. При этом Reinforcement Learning алгоритмы делают это обучение надежным и устойчивым к помехам. Потом такую нейросеть можно перенести на физического робота, отличающегося по геометрии или по массе от человека, но он все равно будет продолжать реалистично повторять движения людей (начиная обучение с чистого листа, как уже говорилось, добиться такого эффекта пока не удается). Или, как в работе выше, можно виртуально исследовать как будет двигаться человек с травмами ног, чтобы разработать более комфортные протезы.
Еще в первой DeepMimic были зачатки такой адаптации. Можно было перемещать красный шар, а персонаж каждый раз бросал мяч в него. Прицеливаясь и соизмеряя усилие броска, чтобы попасть точно в цель. Хотя был обучен на единственном треке Motion Capture, который такой возможности не предоставляет.
Поэтому это можно считать полноценным обучении ИИ, а имитация человеческих движений просто позволяет ускорить обучение и сделать движения визуально более привлекательными, привычными для нас (хотя с точки зрения нейросети они могут быть не самыми оптимальными при этом).
Новая работа пошла в этом направлении еще дальше.
Здесь нет физического движка, это чисто анимационная система для видеоигр. Но упор сделан на реалистичное переключение между несколькими анимациями. И на взаимодействие с игровыми предметами: перенос предметов, использование мебели, открывание дверей.
Архитектура нейросети состоит из двух частей. Одна (Gating network) на основе текущего состояния state и текущей цели goal выбирает какую анимацию использовать, а другая (Motion prediction network) предсказывает ближайшие кадры анимации.
Все это было обучено на наборе Motion Capture треков с помощью имитационного Reinforcement Learning.
Но главное достижение этой работы в другом. В том, как разработчики научили нейросеть работать с разными по размеру предметами и протискиваться в разные по ширине или высоте проходы. Чтобы положения рук и ног выглядели реалистично и соответствовали размерам объекта, с которым персонаж взаимодействует в игре.
Секрет оказался прост: augmentation!
Сначала из Motion Capture трека они определили точки контакта рук с подлокотниками кресла. Потом заменили модель кресла на более широкую, и пересчитали Motion Capture траекторию, чтобы руки касались подлокотников в тех же точках, но на более широком кресле. И заставили нейросеть имитировать эту новую, сгенерированную Motion Capture траекторию. Аналогично с размерами ящиков, высотой проходов и т.д.
Повторив это множество раз с различными 3d моделями окружения, с которыми предстоит взаимодействовать игроку, нейросеть научилась реалистично обращаться с разными по размеру предметами.
Для взаимодействия с окружением в самой игре, дополнительно понадобилось вокселизировать предметы вокруг, чтобы это работало как сенсоры на входе нейросети.
В итоге получилась очень хорошая анимация для игровых персонажей. С плавными переходами между действиями и с возможностью реалистично взаимодействовать с различными по размеру предметами.
Настоятельно рекомендую просмотреть видео, если кто этого еще не сделал. В нем очень подробно рассказано как они этого добились.
Этот подход можно применять для анимации в том числе четвероногих животных, получая непревзойденное качество и реалистичность движений животных и монстров:
3D модель и анимация — как создаются и оживают персонажи игр
Сейчас Social Quantum работает над новой мобильной игрой в жанре Survival RPG, где действия разворачиваются в постапокалиптическом мире. Здесь у нас все сопутствующее: зомби, дефицит ресурсов и немногочисленные выжившие. Помимо стартовых наработок, вроде видения, геймплея и лора, мы с самого начала начали думать о персонажах нашей будущей игры.
Любой персонаж начинается с понимания, как он должен выглядеть, какое впечатление производить на игрока и какими способностями обладать. Если с главными героями все понятно, игрок должен ощущать связь с персонажем, то с противниками из числа монстров все не так просто. Единственная очевидная вещь — они должны вызывать ярко выраженное чувство опасности. Так как мы говорим про зомби, у нас были небольшие особенности в силу специфики игры. В мире, который мы создаем, присутствуют разнообразные противники, опустим самых обычных и перейдем сразу к одному из наших мутантов — Zombie Ape, ведь его визуал должен был отвечать еще и не совсем типичным абилкам.
Вводные по персонажу
Мы ставили цель создать быстрого, хлесткого противника, от которого сложно убежать, с рваным темпом боя, способным резко прыгать на несколько метров.
Вкладываемый характер — животное-охотник, сильно развитые инстинкты. Все эти качества должны выглядеть правдоподобно и соотносится с тем, как выглядит персонаж. Вводная антропометрия: сильные, подвижные ноги придают ему высокую скорость и маневренность. Длинные и крепкие руки позволяют наносить быстрые и хлесткие удары. Также они используются как помощь при передвижении.
Ключевые визуальные аспекты:
Концепт
Базовый концепт является отправной точкой для 3D художника. В нашем случае, концепт является обрисовкой драфтовой модели, поэтому и стадия моделинга нуждалась в ряде доработок, а именно:
В решении этих задач нам помогла обширная референсная база:
Blockout
Изначально процесс разработки персонажа был нелинейным, команда экспериментировала в поисках форм и дизайна. Особое внимание уделялось читаемости силуэта, передаче общей атмосферы агрессивного, звериного характера персонажа. И, конечно, было необходимо отразить зомби-составляющую, в соответствии с сеттингом игры.
Поэтапный процесс разработки highpoly выглядел следующим образом:
В процессе поиска были предложены различные типы пропорций и было принято решение остановиться на данном варианте, чтобы продолжить проработку персонажа.
Highpoly
При создании персонажей с открытыми участками тела или лицом, на стадии хайполи делается полипейнт в ZBrush:
Это результат хайполи в максимальном качестве, но помним, что для игр на мобильных девайсах существует ряд ограничений. Поэтому для билда мы используем подход без использования Xgen для создания волос и ниток. Сейчас это не укладывается в наш пайплайн из-за отсутствия текстур с прозрачностью обусловленных оптимизацией.
Lowpoly
Процесс создания лоуполи для мобильных платформ имеет ряд условий из-за того, что мобильные устройства более требовательны к производительности и оптимизации. Эти условия накладывали на команду художников ряд ограничений:
Вопрос кастомизации, при ограниченном количестве UV-сетов, была решена следующим образом — финальная лоуполи и текстуры включают в себя все возможные элементы, прическу, обувь, различные варианты одежды. Отдельные части кастомизации комбинируются между собой, создавая дополнительную вариативность.
Texturing
Процесс текстуринга логически разбит на 3 этапа, где общая концепция — двигаться от общего к частному:
Все вышеперечисленное в равной степени относится к карте roughness, которая должна донести до зрителя исчерпывающую информацию о характере и вариативности бликов персонажа.
Использование гибкого подхода с возможностью экспериментировать, позволило нам отклониться от изначальной задумки, для достижения более реалистичного вида.
Анимация Zombie Ape
Мы добрались до поведения нашего противника в игре.
Разработка анимаций для каждого персонажа начинается с анимационного концепта, обсуждения и поиска вектора развития каждого из героев. Каким мы его хотим видеть, как он должен себя вести, как реагировать на игрока и т.д. Этот поиск начинается геймдизайнерами с разработки технического задания и продолжается вплоть до финальной интеграции анимации в игру.
На примере конкретного скилла персонажа Zombie Ape покажем, как проходит процесс анимации от планирования до финализации в движке. Для этого скилла цель стояла в создании набора анимаций напрыгивания противника на главного героя и сбивания его с ног. Нужно учитывать как дизайнерские требования: расстояния, тайминги, так и характер персонажей.
Работа над анимацией начинается с того, что для движка игры создается функциональный набросок анимации, на котором «отбиваются» все требования дизайна, функциональность анимационной фичи. И на основе этого наброска собирается заказ на полишинг и финализацию.
На этапе планирования чистовой анимации, мы стараемся не использовать в качестве референсов анимации из готовых игр, но смотрим на реальных актеров, животных или записываем референсы с себя. Для персонажа Zombie Ape кажется очевидным вдохновиться поведением обезьян и перемешать это с поведением актеров, играющих зомби.
Перед нами стоит задача передачи характера и архетипа противника, по возможности в каждом движении и действии, каждый элемент должен напоминать нам об агрессивности или спокойствии, быстроте или медлительности каждого персонажа. Ведет себя он как бешеное животное или как медлительный зомби, мы его видим из игровой камеры или имеем заранее поставленную кинематографичную камеру.
Именно для этого противника стояла цель — создание некого микса человека и животного. Что-то, что может быть анималистичным, странным, жутким и при этом активным, мы не должны забывать что это быстрый, опасный противник.
Первичные наброски анимаций могут существовать в билде продолжительное время, на них происходят основные манипуляции с персонажем и, при необходимости, задается последующее анимационное развитие. Финализация и полишинг анимации — наиболее трудоемкие задачи. Мы стараемся минимизировать усилия и придерживаться итерационного подхода, то есть опробовать максимум идей на более ранних этапах: на черновых анимациях и на блокинге.
Далее утвердив «что будет происходить», мы приступаем к созданию более подробного характерного блокинга «как это будет выглядеть», что мы можем усилить и в каком виде хотим видеть то или иное действие. На этом этапе мы экспериментируем с характером движения, можем что-то добавлять или отказываться. Например, здесь мы решили вернуть кувырок противника во время падения и добавить разворот на 90 градусов.
Корректируем тайминг анимации, продолжаем делать полишинг и микро-корректировки.
На каждом этапе работы мы общаемся с техническими аниматорами и геймдизайнерами. Технические аниматоры разрабатывают и интегрируют первичные наброски анимации, а в дальнейшем, параллельно с работой аниматоров, занимаются настройкой и отладкой анимационных багов непосредственно в билде игры. Во время общения с геймдизайнерами мы выясняем, соответствует ли характер персонажа в игре изначальной задумке.
Модульные спрайтовые персонажи и их анимация
Эта запись девлога целиком посвящена моей системе анимации персонажей, она наполнена полезными советами и фрагментами кода.
За последние два месяца я создал целых 9 новых действий игрока (такие забавные вещи как блокировка щитом, уворачивание в прыжке и оружие), 17 новых носимых предметов, 3 набора брони (пластинчатый, шёлковый и кожаный) и 6 видов причёсок. Также я завершил создавать всю автоматизацию и инструменты, поэтому всё уже используется в игре. В статье я расскажу, как этого добился!
Надеюсь, эта информация окажется полезной и докажет, что необязательно быть гением, чтобы самостоятельно создать подобные инструменты/автоматизацию.
Краткое описание
Изначально я хотел проверить, можно ли совместить наложенные друг на друга спрайты с синхронизованными аниматорами для создания модульного персонажа с заменяемыми причёсками, снаряжением и носимыми предметами. Можно ли объединить нарисованную вручную пиксельную анимацию с по-настоящему настраиваемым персонажем.
Разумеется, такие функции активно используются в 3D- и 2D-играх с пререндеренными спрайтами или в 2D-играх со скелетной анимацией, но насколько я знаю, существует не так много игр, совмещающих созданную вручную анимацию и модульных персонажей (обычно потому, что процесс оказывается слишком монотонным).
Я раскопал этот древний GIF моего первого месяца работы с Unity. На самом деле этот модульный спрайт оказался одним из первых моих экспериментов в разработке игр!
Я создал прототип при помощи системы анимаций Unity, а затем для проверки концепции добавил одну рубашку, одну пару штанов, одну причёску и три предмета. Для этого потребовалось 26 отдельных анимаций.
В то время я создавал всю свою анимацию в Photoshop и не заморачивался автоматизацией процесса, поэтому он был очень скучным. Потом я подумал: «Так, основная идея сработала, позже я добавлю новые анимации и снаряжение». Оказалось, что «потом» — это несколько лет спустя.
В марте этого года я нарисовал дизайн большого количества брони (см. мой предыдущий пост), и заметил, как этот процесс можно сделать более удобным. Я продолжал откладывать реализацию, потому что даже при наличии автоматизации нервничал, что ничего не получится.
Я ожидал, что придётся отказаться от кастомизации персонажа и создать единственного главного героя, как в большинстве игр с ручной анимацией. Но у меня был план действий, и настало время проверить, смогу ли я победить этого монстра!
Спойлер: всё получилось замечательно. Ниже я раскрою свои ***секреты***
Модульная система спрайтов
I. Познай свои границы
Предварительно я провёл много тестов арта и контроля времени, чтобы выяснить, сколько может занять такая работа, и будет ли достижим для меня подобный уровень качества.
Я придумал численную оценку для каждой анимации и отказался от всего с плохими показателями. Изначально я планировал создать 6 наборов брони, но быстро осознал, что это перебор, и выбросил три типа.
Аспект отслеживания времени оказался очень важным, и я крайне рекомендую использовать его, чтобы отвечать на вопросы типа: «Сколько врагов могу я позволить себе создать в игре?». Всего после нескольких тестов мне удалось экстраполировать достаточно точную оценку. При дальнейшей работе над анимациями я продолжил следить за временем и пересматривать мои ожидания.
Поделюсь копией моего журнала работы за последние два месяца. Учтите, что это время идёт в добавок к моей обычной работе, где я провожу по 30 часов в неделю:
II. Смена палитры ради светлого будущего
С умом используя цвета в дизайне спрайтов, можно отрисовать один спрайт и создать множество различных вариаций при помощи смены палитры. Можно менять не только цвета, но и создавать различные включаемые и отключаемые элементы (например, заменой цветов на прозрачность).
Каждый набор брони имеет 3 вариации, а смешивая верхние и нижние части, можно получить множество комбинаций. Я планирую реализовать систему, в которой можно собрать один набор брони для внешнего вида персонажа, а другой — для его характеристик (как в Terraria).
В процессе работы меня приятно удивляли обнаруживаемые любопытные комбинации. Если соединить пластинчатый верх с шёлковым низом, то можно получить нечто в стиле боевого мага.
Лучше всего реализовывать смену палитр, используя в спрайте цвета, кодирующие значение, чтобы в дальнейшем можно было брать их для поиска настоящего цвета из палитры. Я знесь немного упрощаю, так что вот видео, с которого можно начать:
Я не буду объяснять всё в подробностях, а вместо этого расскажу о способах реализации этой техники в Unity, и об их плюсах и минусах.
1. Текстура поиска для каждой палитры
Это наилучшая стратегия для создания вариаций врагов, фонов и всего того, где множество спрайтов иеет одинаковую палитру/материал. Различные материалы нельзя сгруппировать в батчи, даже если они используют одинаковый спрайт/атлас. Работа с текстурами довольно мучительна, но палитры можно изменять в реальном времени, заменяя материалы, с помощью SpriteRenderer.sharedMaterial.SetTexture или MaterialPropertyBlock, если вам нужны разные палитры для каждого экземпляра материала. Вот пример фрагментной функции шейдера:
2. Массив цветов
Я остановился на этом решении, потому что мне нужно было заменять палитры каждый раз, когда меняется внешний вид персонажа (например, при надевании предметов), и создавать некоторые палитры динамически (чтобы отобразить выбранные игроком цвета волос и кожи). Мне показалось, что во время выполнения и в редакторе для этих целей гораздо проще будет работать с массивами.
Я представил свои палитры как тип ScriptableObject и использовал для их редактирования инструмент MonoBehaviour. Проработав долгое время над редактированием палитр в процессе создания анимаций в Aseprite, я понял, какие инструменты мне требуются и писал эти скрипты соответствующим образом. Если вы хотите написать собственный инструмент для редактирования палитр, то вот какие функции я обязательно рекомендую реализовать:
— Обновление палитр на различных материалах при редактировании цветов для отображения изменений в реальном времени.
— Присваивание названий и изменение порядка цветов в палитре (используйте поле для хранения индекса цвета, а не его порядка в массиве).
— Выбор и редактирование нескольких цветов за раз. (Совет: поля Color в Unity можно копипастить: просто нажмите на один цвет, скопируйте, нажмите на другой цвет, вставьте — теперь они одинаковы!)
— Применение цвета оверлея ко всей палитре
— Запись палитры в текстуру
3. Единая текстура поиска для всех палитр
Если вы хотите переключать палитры на лету, но в то же время вам необходим батчинг для снижения количества вызовов отрисовки, то можно использовать эту технику. Она может оказаться полезной для мобильных платформ, но использовать её довольно неудобно.
Во-первых, нужно будет упаковать все палитры в одну большую текстуру. Затем вы используете цвет, заданный в компоненте SpriteRenderer (AKA цвет вершины) для определения строки, которую надо считать из текстуры палитры в шейдер. То есть палитра этого спрайта управляется через SpriteRenderer.color. Цвет вершины — это единственное свойство SpriteRenderer, которое можно менять без нарушения батчинга (при условии, что все материалы одинаковы).
В большинстве случаев лучше всего использовать для управления индексом альфа-канал, потому что вам скорее всего не понадобится куча спрайтов с различной прозрачностью.
Чудеса замены палитр и слоёв спрайтов. Так много комбинаций.
III. Автоматизируйте всё и применяйте подходящие инструменты
Для реализации этой функции автоматизация была совершенно необходимой, потому что в результате у меня получилось около 300 анимаций и тысячи спрайтов.
Первым моим шагом стало создание экспортёра для Aseprite, чтобы управлять моей безумной схемой слоёв спрайтов при помощи удобного интерфейса командной строки. Это просто скрипт на perl, который обходит все слои и метки в моём файле Aseprite и эспортирует изображения в определённой структуре каталогов и имён, чтобы я смог в дальнейшем их считывать.
На стороне Unity серьёзные проблемы у меня возникли в двух местах: в загрузке/нарезке спрайтшита и в построении клипа анимации. Мне бы очень помог понятный пример, поэтому вот фрагмент кода из моего импортёра, чтобы вы не так мучились:
Если вы этого ещё не делали, то поверьте — начать создавать собственные инструменты очень легко. Самый простой трюк заключается в размещении в сцене GameObject с привязанным к нему MonoBehaviour, которое имеет атрибут [ExecuteInEditMode]. Добавьте кнопку, и вы готовы к бою! Помните, что ваши личные инструменты не обязаны выглядеть хорошо, они могут быть чисто утилитарными.
При работе со спрайтами автоматизировать стандартные задачи довольно легко (например, создание текстур палитр или пакетную замену цветов в нескольких файлах спрайтов). Вот пример, с которого можно начать учиться изменению своих спрайтов.
Как я перерос возможности Mecanim: жалоба
Со временем прототип системы модульных спрайтов, который я создал с помощью Mecanim, стал самой большой проблемой при апгрейде Unity, потому что API постоянно сильно менялся и был плохо задокументирован. В случае простого конечного автомата было бы разумно иметь возможность запрашивать состояния каждого клипа или менять клипы во время выполнения. Но нет! Из соображений производительности Unity запекает клипы в их состояния и заставляет нас использовать для их смены неуклюжую систему переопределений.
Сам по себе Mecanim не такая уж плохая система, но мне кажется, что ему не удаётся реализовать свою основную заявленную особенность — простоту. Идея разработчиков заключалась в том, чтобы заменить то, что казалось сложными и мучительным (скриптинг) чем-то простым (визуальным конечным автоматом). Однако:
— Любой нетривиальный конечный автомат быстро превращается в дикую паутину узлов и соединений, логика которой разбросала по разным слоям.
— Простым случаям использования мешают обобщённые требования системы. Чтобы проиграть одну-две анимации, вам нужно создавать новый контроллер и назначать состояния/переходы. Разумеется, присутствует и излишняя трата ресурсов.
— Забавно, что в результате вам всё равно приходится писать код, ведь чтобы конечный автомат делал что-то интересное, нужен скрипт, вызывающий Animator.SetBool и подобные ему методы.
— Для многократного использования конечного автомата с другими клипами нужно дублировать его и заменять клипы вручную. В дальнейшем вам придётся вносить изменения в нескольких местах.
— Если вы хотите изменять то, что находится в состоянии во время выполнения, то у вас проблемы. Решением будет или плохой API, или безумный граф с одним узлом для каждой возможной анимации.
Рассказ о том, как разработчики Firewatch попали в ад визуального скриптинга. Самое забавное в том, что когда докладчик показывает наиболее простые примеры, они всё равно выглядят безумно. Зрители в буквальном смысле стонут на 12:41. Добавьте огромные затраты на обслуживание, и вы поймёте, почему я сильно не люблю эту систему.
Многие из этих проблем даже не вина разработчиков Mecanim, а просто естественный результат несовместимых идей: нельзя создать общую и в то же время простую систему, а описывать логику при помощи изображений сложнее, чем просто словами/символами (кто-нибудь помнит флоучарты UML?). Я вспомнил фрагмент из доклада Зака Макклендона на Practice NYC 2018, и, если найдётся время, рекомендую вам посмотреть видео целиком!
Однако я разобрался. Визуальный скриптинг всегда порицается агрессивными «пиши свой собственный движок» нердами, не понимающими потребностей художника. К тому же нельзя отрицать, что бОльшая часть кода выглядит как непостижимый технический жаргон.
Если вы уже немного программист и делаете игры со спрайтами, то вам возможно стоит подумать дважды. Когда я начинал, то меня был уверен, что никогда не смогу написать что-то связанное с движком лучше, чем разработчики Unity.
