Дьявол в деталях, олени в ступоре: возвращение "жевателей воздуха" и шумного кулера (AI-evolution #7)

С чего бы начать… Позанимался проектом в последние дни. Как вы помните, вместо сферических агентов я решил добавить милых и привлекательных оленят. И тем самым открыл ящик Пандоры.

Поколение ноль. Табула раса

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

В скачанных мной ассетах всё было доступно «из коробки», в том числе и анимации (стояния, ходьбы и бега; анимации сна и поедания не было, но и то хорошо). Пришлось поразбираться, погуглить, как подключить переключение анимаций к проекту. В двух словах — нужно было в контроллере оленя создать дополнительную переменную, которая принимала в себя существующий контроллер, и настроить отношения типа: «если скорость оленя выше нуля, включай анимацию ходьбы, если выше определённого значения — анимацию бега, иначе стой».

Здесь задается анимация. В зависимости от положения ползунков, решается какая анимация будет проигрываться.
Здесь задается анимация. В зависимости от положения ползунков, решается какая анимация будет проигрываться.

Я на радостях, что со сложной частью закончил, даже приступил наконец к обучению оленей. В процессе обучения столкнулся с багом — некоторые олени стояли на поле как вкопанные, причём дебаггер ясно показывал, что значение скорости не равно нулю. То есть они пытались куда-то пойти, но не могли. Я, предвкушая долгий поиск проблемы, немного сник, но меня пронзило чувство дежавю — когда писал проект на React + Python, у меня ведь возникла точно такая же проблема.

Слегка обученные олени, но с тем самым багом "жевателей воздуха".

Я решил проверить свою догадку, и так оно и оказалось — намертво застывали те олени, которые доедали куст в компании с другим оленем. Механику поедания я сделал точно такую же, как раньше, но урока из происшествия не вынес) Напомню: логика поедания в большей части была написана на объекте куста. То есть там написано: «если олень наступил на меня, я передаю ему сигнал, что он кушает». Это происходит с каждым оленем, тут всё нормально. Но вот что дальше происходит в кусте: «если олень доедает меня, я передаю ему сигнал, что он перестал кушать». Вот тут и была проблема: я опять забыл освобождать от куста ВСЕХ поедающих оленей, а не только первого найденного. В итоге это можно назвать возвращением «жевателей воздуха».

Дьявол в деталях, олени в ступоре: возвращение "жевателей воздуха" и шумного кулера (AI-evolution #7)

Но это всё мелочь, баг решился, и я решил было продолжить обучение, но меня опять стал напрягать возрастающий шум кулера при запуске симуляции. Тут мне пришлось подробнее изучить встроенный в Unity дебаггер и сборщик статистики. Я попытался оптимизировать всё, что могло повысить нагрузку на процессор. Во-первых, немного упростил отрисовку куста, убрав прозрачность материала. Во-вторых, я узнал, что у меня происходит отрисовка всех объектов на карте, даже тех, которые сейчас не отображаются на экране. И это было исправлено (включил куллинг). Это слегка снизило нагрузку, но вообще недостаточно для ощутимого эффекта.

Опуская скучные технические детали, скажу, что в итоге проблема оказалась как раз в подключенных к оленям анимациях. У каждого животного есть скелет (Rig), который является набором костей, подключенных к тушке. Я никогда особо не задумывался, как в играх происходит анимирование сущностей, но ситуация с оленями вынудила разобраться в этом подробнее.

Окно статистики в правом верхнем углу. В этой версии уже применены некоторые "оптимизаторские приемы". Главная проблема - Non-SRP Compatible. Как я понял бОльшая часть нагрузки идет от них
Окно статистики в правом верхнем углу. В этой версии уже применены некоторые "оптимизаторские приемы". Главная проблема - Non-SRP Compatible. Как я понял бОльшая часть нагрузки идет от них

В двух словах — все вычисления о том, что один сустав сдвинулся на миллиметр туда, а другой сюда, ложатся на центральный процессор (Skinned Mesh Agent). Если бы олень был главным героем моей игры и единственным подобным объектом на экране, то всё было бы нормально. Но если все движения нужно рассчитывать для каждого из 30 агентов каждый кадр, то нагрузка получается неслабая. А если агентов станет еще больше? Продолжать что-либо добавлять в проект в текущем виде точно нельзя, пока не решу проблему с оптимизацией.

Дьявол в деталях, олени в ступоре: возвращение "жевателей воздуха" и шумного кулера (AI-evolution #7)

На этой ноте, чтобы отвлечься, сел поиграть в Against The Storm, посмотрел на развернувшуюся картинку и сразу же задался вопросом: «Ну вот здесь же тоже минимум полсотни, а то и больше объектов, которым нужно движения прорисовывать каждый кадр. Почему здесь кулер молчит?». Спросил у гугла, на что он мне ответил: «Это потому, что там используется технология VAT (Vertex Animation Texture)». Это технология оптимизации анимации в играх в реальном времени, при которой движение вершин трехмерной модели запекается (меня позабавило, что такое слово официально используется) в обычную текстуру, а затем воспроизводится с помощью шейдера на видеокарте (GPU). То есть, проще говоря, обработка анимации снимается с центрального процессора и передается видеокарте.Узнал, что можно взять готовую анимацию и «запечь» её. Быстрый гуглёж выдал первое решение (от FuadJfrv на GitHub), которое оказалось неудачным. Расписывать не буду, потому что пока сам до конца не разобрался с этой штукой) Но подумал, что, возможно, не выполнено какое-то из условий, которое было представлено в конце файла README:

  • У объекта должен быть компонент Animator.
  • Объект должен содержать только один компонент Skinned Mesh Renderer.
  • Объект должен использовать только один материал.
  • Совет: При запекании нескольких анимаций рекомендуется, чтобы начальная и конечная позы анимаций были схожими. В противном случае переход между анимациями может выглядеть резким.
Видео с гитхаба FuadJfrv

Сейчас хочу попробовать подключить OpenVAT, для чего нужно скачать и познакомиться с Blender. Скорее всего, я усложняю и есть инструменты попроще, но всё же попробую сначала этот метод.

Дьявол в деталях, олени в ступоре: возвращение "жевателей воздуха" и шумного кулера (AI-evolution #7)

В общем, вместо обучения нейросетей и калибровки поведения я внезапно ушёл в дремучие дебри низкоуровневой графической оптимизации и запекания текстур. Но, пожалуй, в этом и есть весь кайф разработки своего проекта с нуля — ты никогда не знаешь, какая именно задача заставит тебя расширять кругозор в следующий раз. Зато теперь, если всё получится, мои олени смогут бегать огромными стадами, а процессор будет спокоен.На этом пока всё. Пойду качать Blender и крутить ползунки. Получится ли "запеканка из анимаций" узнаем потом. Спасибо за внимание)

Дьявол в деталях, олени в ступоре: возвращение "жевателей воздуха" и шумного кулера (AI-evolution #7)
33
11