me.neoascetic

Принципы современного игрового ИИ. Второй урок

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

2.1 Авторинг

На практике существует множество способов создания игрового контента, но в данном пункте будет рассмотрен подход с его ручным созданием, процесс которого и называется “авторинг”. Казалось бы, какое отношение создание контента имеет к искусственному интеллекту? Как будет рассмотрено, у авторинга есть весомые преимущества по сравнению с другими подходами, как с точки зрения бизнеса, так и с технологической точки зрения - главным образом потому, что это наиболее простой способ добавления необходимого контента в игру.

Итак, можно выделить семь основных причин, по которым авторинг имеет широкое применение:

  1. Ручное создание контента приводит к более глубокому пониманию предметной области, а также к появлению более-менее формального определения или спецификации, что может поспособствовать автоматизации процесса генерации контента в будущем; авторинг можно использовать как фундамент, от которого потом отталкиваться

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

  3. С точки зрения методологии данный подход позволяет получить рабочий прототип как можно раньше, постепенно его дорабатывая, что скажется на процессе разработки только положительным образом

  4. Авторинг препятствует росту сложности, потому как вручную созданный цифровой контент лежит на диске и работает чаще всего независимо друг от друга; контент легче создать руками, чем написать код для его создания; меньше кода - меньше сложности в игровом движке

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

  6. Авторинг лучше масштабируется горизонтально - например, можно усилить работу над созданием контента ближе к релизу, наняв больше людей, в то время как на ранних этапах разработки достаточно небольшого количества контента; генерация с помощью кода чревата тем, что многие требования могут не быть учтены на ранних этапах, и написанный для генерации движок может просто не позволить иметь те или иные возможности; может понадобиться его рефакторинг, etc

  7. Наконец, ручное создание контента предполагает более высокое его качества; контент, сгенерированный с помощью алгоритмов, имеет свойство быть среднего качества, хотя его вариаций может быть существенно больше

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

Одним из способов достижения компромисса, помимо преимуществ с точки зрения архитектуры, является использование модульного подхода. При этом подходе целостная сущность дробится на несколько частей, и затем требуется создать несколько вариаций каждой из этих частей. При этом подходе, создание нового варианта контента можно возложить на алгоритм. Таким образом, мы получаем комбинаторное увеличение количества вариантов контента, и, вероятно, не сильно потеряем в качестве.

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

Существует два основных подхода к тому, чтобы привнести модульность в создаваемый вручную контент.

Первый - это подход с созданием шаблонов, подход “сверху - вниз”. В уже имеющимся контенте некоторые места заменяются на плейсхолдеры, то есть контент становится шаблоном, а актуальные значения в него добавляются по требованию. Например, в генераторе ландшафта могут быть объявлены места для точек интереса, а непосредственно выбор того, что там сгенерировать - дом, озеро или пещеру - осуществляется на последующем этапе. Данный подход хорош тем, что с него просто начать, и в некоторых случаях результат может быть весьма высококачественным; с другой стороны, количество вариаций ограничено вручную, и это вариации могут казаться весьма монотонными.

Другой подход, “снизу - вверх” - блочный подход. Основная идея - начать с простых блоков и затем комбинировать из них более сложные. Данный подход не страдает от малого объёма вариаций и для некоторых случаев подходит просто идеально, но зачастую не все комбинации имеют смысл, могут смотреться нелепо, то есть качество хромает.

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

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

2.2 Data-driven движки

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

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

Во-вторых, необходимо стремиться к тому, чтобы информация в файлах хранилась преимущественно в текстовом виде, в формате, легко читаемом и редактируемом человеком. Главный плюс такого подхода - это поддержка со стороны бесчисленного количества уже существующих утилит, редакторов, валидаторов, чего сложно добиться с бинарными файлами. Можно посмотреть в сторону JSON, INI и лисповских S-Expr.

В-третьих, помимо именования файлов, необходимо подумать и об иерархии директорий - не будет же все лежать в одной! Каких-то особых рекомендаций тут нет, кто-то группирует по типу файлов, кто-то - по подсистемам, к которым файлы относятся. Просто нужно выбрать наиболее подходящий вариант.

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

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

Загрузка игрового движка может занимать некоторое время. С целью уменьшить время ожидания на перезапуск игры после модификации исходников must-have должна быть поддержка движком горячей перезагрузки ресурсов, в том числе скриптов. Это может быть реализовано как с помощью простого сочетания клавиш, так и с помощью более хитроумного подхода с мониторингом файловой системы. Это настолько полезный с точки зрения экономии времени подход, что некоторые движки даже предоставляют возможность перекомпилировать исходный код и подгружать результат компиляции на лету.

Зачастую используется разделение классов неправильного поведения на ошибки (errors) и предупреждения (warnings), которые просто печатаются куда-либо. Хорошей практикой является сведение предупреждений к ошибкам, что способствует тому, что их перестают игнорировать (как зачастую бывает) - нужно воспринимать данные как либо валидные, либо нет. С другой стороны, просто прерывать программу в случае ошибки чревато потерями времени, и чем больше команда, тем существеннее эти потери. Альтернативой здесь может быть graceful degradation до состояния, в котором можно продолжать работу.

На период разработки полезным может быть использовать очевидные и заметные плейсхолдеры для отсутствующего контента или каких-то особенностей геймплея, которые необходимо трактовать особым образом. Например, использовать яркие текстуры для объектов, для которых финальные текстуры ещё не готовы. Это не позволит случайно пропустить их перед релизом, а тестировщикам - понять, что такое поведение ожидаемо, а не является багом.

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

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

В случае, если процесс компиляции дорогостоящ, можно рассмотреть подход с центральным хранилищем скомпилированных ассетов, доступных для всех членов команды по уникальному идентификатору - например, по хэшу.

2.3 Алгебра для генерации контента

Для целей генерации контента с помощью алгебры используются функции. Функции - это описание отношений между входными параметрами и их результатом. Функциональное программирование является естественной связью между математическими функциями и их использовании с помощью компьютера. Нас главным образом интересуют непрерывные функции, то есть функции без “скачков”. Их можно использовать в различных областях - для генерации звуковых сигналов, модификаторов движений и анимации, факторами, влияющими на принятие решений, для генерации текстур и ландшафтов и так далее. В функциях чаще всего нам интересны их очертания - зная, как функция отвечает на изменения аргумента, мы сможем понять, в каких случаях лучше применить одну, а в каких - другую. Существует множество типов функций - помимо простых и примитивных, таких как identity и линейная функции, существуют квадратичные и кубические функции, функции инверсии, экпоненциальные, логарифмические, тригонометрические (sin, cos) функции, и, часто имеющие применение в машинном обучении, сигмоида и softplus.

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

Новым направлением в генерации контента (в том числе компьютерной графики) является использование distance fields. При использовании этого подхода вместо расположения внешнего объекта, например игровой модели, состоящей из полигонов, используется функциональный подход, и каждая точка нужной модели вычисляется на основе входной точки и какой-то сложной формулы. Дублирование и вращение данных объектов выполняется определёнными преобразованиями над входным вектором точек. Такой подход можно применять для генерации ландшафта a-la Minecraft, например, начав с простого шара и затем итеративно добавлять все новые и новые функции, с каждой приближаясь к требуемому результату.

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

Самым популярным примером является шум Перлина.

Предыдуший урок

Cледующий урок