Привет! Меня зовут Кирилл Богатов, я дизайнер голосовых интерфейсов в команде TORTU и заядлый геймер. Когда эти две страсти сталкиваются, рождаются необычные концепты для голосовых игр. Месяц назад я выпустил игру «Охота на Вампуса» для голосового ассистента Алисы. Игра получила много положительных отзывов и побывала в топ-10 развлекательных навыков. В этой статье я поэтапно расскажу о процессе её создания: от переосмысления идей первоисточника — до технической реализации. Рассказывая о своих проектах, я увидел определённый интерес со стороны геймеров, молодых родителей и любителей настолок. Люди начали делиться со мной концептами голосовых игр, многие из которых были ну просто «Оу, май!». Кроме того, меня регулярно спрашивают о планах создать полноценную D&D для Алисы или адаптацию игро-книг Браславского. Как геймер, я хочу видеть больше комплексных голосовых игр вместо очередных викторин и «Угадай %name%», а как VUI-дизайнер — способствовать развитию этого направления, рассказывая о своём опыте и вдохновляя людей на реализацию их идей. В основе игры лежит классическая текстовая аркада Hunt The Wumpus, написанная в 1972 году Грегори Йобом. От оригинала были позаимствованы название и базовые механики. Впоследствии игра обросла собственными идеями и сильно изменилась по настроению. Действие игры происходит в пещере из 20 комнат, связанных между собой коридорами. Цель игры — выследить и подстрелить монстра Вампуса. Если игрок окажется с ним в одной комнате, то игра закончится, а убить монстра можно только выстрелом из соседней комнаты.  Помимо Вампуса в случайных комнатах могут находиться ямы, от которых игрок погибает, и гигантские летучие мыши, которые переносят его в другую случайную комнату. Узнать о приближении опасностей можно по звукам: Вампус рычит, мыши пищат, а из ям насвистывает ветер. Чтобы точно узнать о том, что находится в соседней комнате, нужно выстрелить в неё. 50 лет назад для создания атмосферной игры было достаточно сказать «пещера» и «воняет Вампусом». Сегодня такой трюк не пройдёт. Чтобы удержать внимание игрока и мотивировать его на исследования, игра должна удивлять и регулярно подбрасывать что-то новое. По этой причине я внёс в игру ряд нововведений, которые изменили её до неузнаваемости. Тематические локации. Вместо безликих комнат игрок теперь исследует уникальные места: ледяной туннель, руины подземного города, гейзеры и многие другие.  Диковинки. В комнатах можно найти необычные вещи, привязанные к конкретной локации. На них же завязана часть нелепых шуток. Вы вот видели когда-нибудь вомбата в зеркальном лабиринте? А он там есть. Сундуки. В оригинале игрок просто перемещался между комнатами и изредка стрелял во что-нибудь — и это быстро надоедало. Чтобы разнообразить геймплей, я добавил загадочные сундуки. В каждом из них может попасться как ценный артефакт, облегчающий прохождение, так и ловушка или забавная записка. Ловушки. Время от времени игрок сталкивается со случайными ловушками. Они не могут навредить ему, но каждый такой момент вносит в игру разнообразие и поддерживает общий дух приключений в стиле Индианы Джонса. Катаклизмы. Особые события, повышающие сложность игры и преображающие комнаты. Подробнее расскажу ниже. Таблица очков. В конце похода игра подсчитывает заработанные очки и сравнивает их с результатами других игроков. Так абстрактные цифры превратились в показатель крутости игрока. Достижения. Если игрок попадает в комичную ситуацию, показывает хороший результат или произносит определённые фразы (задает вопросы, грубит или хвалит), то может получить за это ачивку в конце похода. Персона — это то, как голосовой навык общается с пользователем. Поскольку в основе взаимодействия с игроком лежит диалог, то именно от персоны зависит то, как пользователи будет воспринимать игру.  Персона есть во всех голосовых навыках, даже если это обычная викторина. Сравните, как одна и та же реплика могла бы звучать в навыках с разной персоной: Нейтральный: Здравствуйте. В этой викторине мы проверим, насколько хорошо вы знаете популярных блогеров. Готовы начать? Инициативный: Привет. Прошерстила тренды и приготовила новые вопросы о ваших любимых блогерах. Готовы проверить свои знания? Неформальный: Йоп! Бот-блогеровед здесь. Готовы блеснуть знаниями в моей викторине? Есть игры без персонажей, но нет игр без персоны. Создавать персону с нуля крайне тяжело. Чем больше реплик, тем больше вероятность ошибиться при выборе нужных слов. В этом случае я рекомендую сделать следующее: Описать Tone of Voice по методике «Четырёх измерений». Взять за основу одного-двух реально существующих персонажей. В «Вампусе» персона завязана на Рассказчике — харизматичном компаньоне, который сопровождает игроков во время их спуска в пещеру. При его создании я вдохновлялся Бобом — владельцем таверны на полях сражений Hearthstone. Выбрал его за умение подбодрить и заполнить неловкую паузу. Если вводите в игру персонажа, продумайте его биографию и запоминающиеся черты: Как персонаж связан с миром игры? Как попал в него? С какими ситуациями уже сталкивался персонаж? О каких событиях знает и как реагирует на них? Что нравится и не нравится персонажу? Чего он боится? Чем занимается в свободное время? Есть ли что-то особенное в том, как персонаж говорит или мыслит?  Есть ли истории, которыми персонаж мог бы поделиться с игроком? Понимает ли персонаж, что он — часть игры? Если не уделять внимание деталям, то на выходе получится очередной весёлый болванчик или покорный «раб-компаньон». Это простительно для игр с низким уровнем персонификации, но не подходит для тех проектов, где общение с персонажем является важной частью игрового процесса или инструментом создания атмосферы. Уберите детали и персонаж превратится в болванчика. При работе над Рассказчиком я стремился создать образ энтузиаста-романтика, восторженно следующего за своим нанимателем. Выполняя функцию проводника, он акцентирует внимание игрока на красотах пещеры, уберегает его от опасностей и иронизирует над происходящим.  Через реплики Рассказчика игрок также опосредованно получает информацию об окружении. Например, Рассказчик может пожаловаться на холод или сырость в пещере, отогнать змею или испугаться тарантула. Рассказчик говорит голосом Эрмила из Yandex.SpeechKit. Его завораживающий тембр позволил составлять реплики, звучащие не то из программы «В мире животных», не то с экрана загрузки третьего «Ведьмака». Стандартный же голос Алисы был слишком весёлым и не вписывался в атмосферу игры. При тестировании оказалось, что Эрмил слишком торопится и не понимает вопросительных интонаций. Вот что я делал, чтобы справиться с этим: разбивал фразы, которые нельзя прочитать на одном дыхании;  переформулировал неестественно звучащие фразы; вручную расставлял паузы между словами и предложениями; использовал вопросительные слова и частицу «ли», чтобы фраза воспринималась как вопрос даже без нужной интонации. Допиливайте голос вручную. Реплики Рассказчика часто выходят за рамки озвучивания текста с экрана. Он как ассистент, который зачитывает инструкцию и комментирует происходящее: «Здесь можно повернуть налево или направо. О, смотрите какой интересный гриб за тем камнем!». С одной стороны это сокращает количество текста на экране. С другой стороны — добавляет живости и делает реакцию Рассказчика неожиданной для игрока. Новые идеи обычно рождаются из наблюдений и создания связей между ранее несвязанными вещами. Этот процесс наглядно показан в книге Остина Клеона «Кради как художник». Но когда речь заходит о создании чего-то из ряда вон выходящего, нужно что-то помощнее. Здесь на сцену выходит латеральное мышление. Концепция латерального мышления построена на разрывах шаблона. Исследователь берет за основу привычное состояние системы и применяет к ней ряд провокаций. Для примера возьмем базовую механику «Вампуса» и сгенерируем на её основе несколько новых идей для сиквела: Базовое состояние — игрок выслеживает монстра с помощью звуков в пещере. Дополнение — игрок выслеживает монстра и разгадывает загадки. Инверсия — монстр выслеживает игрока / пещера следит за игроком и монстром. Исключение — игрок никого не выслеживает, просто изучает локации. Гиперболизация — игрок выслеживает нескольких монстров. Изменение порядка — игра начинается с того, что игрок уже нашел монстра / монстр съел игрока. Не сковывайте себя рамками адекватности или технических трудностей. Фантазия не терпит ограничений! Собранные идеи рекомендую анализировать по методике «Шести шляп мышления». Даже если вы работаете в одиночку, это поможет рассмотреть ваши идеи с разных сторон и отобрать лучшие. Чтобы быстро разобраться в механике и получить полное впечатление о возможностях игры — посмотрите вот это видео: Пещера Вампуса представляет собой додекаэдр с 20 вершинами-комнатами. В каждой комнате пользователь может совершить два базовых действия: выстрелить или переместиться в одну из трёх соседних комнат. Когда игрок находится рядом с Вампусом и промахивается, монстр начинает паниковать и перемещается в одну из трех соседних комнат. C вероятностью 33% он оказывается в одной комнате с игроком. В этом случае игрок либо умирает, либо перемещается в случайную комнату с помощью летучей мыши. Иногда это приводит к курьёзным ситуациям и мыши переносят его в яму. Связи между комнатами и пути отступления Вампуса пришлось прописывать вручную. Это был самый нудный и кропотливый этап проектирования. Чтобы разбавить монотонный процесс осмотра комнат, в игре периодически что-то происходит. Вот как выглядят первые 10 ходов игрока (ходом считается переход в комнату): Первый (малый) сундук всегда содержит что-нибудь приятное или полезное. Это сделано для того, чтобы сформировать у новых игроков позитивный первый опыт. Если бы в первом же сундуке оказался монстр или ловушка, игрок бы подумал, что в сундуки лучше вообще не лезть. Большие сундуки подстраиваются под текущую ситуацию. Если у игрока осталось мало стрел, то он с большей вероятностью найдёт в сундуке именно стрелы. Если же из первого сундука выпал фрагмент компаса, то из последующих сундуков может выпасть другой фрагмент. Ловушки и трёп рассказчика нужны для поддержания атмосферы и разбавления геймплея. Катаклизм помогает встряхнуть игроков, которые уже пробежали часть комнат и начали скучать. Катаклизм случается на 10 ход и представляет собой одно из трёх событий: землетрясение, потоп или выброс лавы. В это время происходит несколько важных вещей: Вампус и летучие мыши перемещаются в другие комнаты. В пещере появляется ещё одна яма. Меняется описание 2/3 комнат. В зависимости от типа катаклизма они становятся либо разрушенными, либо затопленными, либо сгоревшими. Некоторые комнаты получают особые названия вроде Обсидианового туннеля, который возникает после воздействия лавы на Ледяной туннель. При проектировании «Вампуса» мне хотелось отойти от чисто утилитарного использования голоса. Поэтому Рассказчик умеет не только озвучивать обстановку, но и поддерживать диалог, создавая тем самым иллюзию общения.  Чтобы определить темы для диалогов, я выписал себе все сущности, которые встречаются в игре. Это комнаты, сокровища, опасности и диковинки. Получилось порядка 200 сущностей, из которых Рассказчик может отреагировать примерно на 50–70. Для остальных пока не нашлось остроумного ответа.  Рассказчик также умеет реагировать на типовые ситуации: приветствие, прощание, похвалу, оскорбления и т. д. Многие пользователи пытаются разговаривать с ботами как с живыми людьми, поэтому подобные вещи следует учитывать. Выходите за рамки основного сценария. У большинства событий и реплик в игре есть от 4 до 15 различных вариантов. Это делает каждое прохождение непохожим на предыдущее. Нет ничего хуже, чем бот, который всё время повторяет одно и то же. Особое внимание уделено вступлению. Оно должно было создавать правильный настрой и не наскучить спустя несколько партий. Для этого я составил десяток реплик, вдохновляясь любимыми книгами и играми. Игра подстраивается под успехи пользователя. Чем больше игр сыграно, тем короче становится приветственная реплика (постоянным игрокам не нужно каждый раз напоминать о сути игры). Сделайте реплики вариативными. Каждый звук в «Вампусе» выполняет определённую функцию — предупреждает об опасности или задаёт атмосферу. Например, звук натяжения тетивы создаёт небольшую напряженную паузу между репликой игрока и оглашением результата выстрела. Моя любимая хитрость — звук зажигания факела в конце некоторых реплик Рассказчика. С одной стороны, это добавило в игру больше контекста — в комнатах темно и сыро, поэтому нужно периодически обновлять факелы. С другой стороны, помогло отделить друг от друга несколько реплик, не прибегая к длинным паузам, которые нужны пользователям для того, чтобы успеть осмыслить происходящее и подготовиться к следующей порции информации. Звуки создают атмосферу и заполняют неловкую паузу. Напоследок — несколько выводов по работе со звуком: Оптимальная продолжительность звукового эффекта — 3–5 секунд. Длинные звуки отделяют пользователя от основного геймплея, и он начинает скучать. В одной реплике лучше не использовать больше двух звуков подряд. Иначе можно сбить пользователя с толку. Звуки позволяют подчеркнуть изменение окружения. Например, запуск новой партии или переход к подведению итогов отделяют их от других частей игры. Звуки хорошо подходят для визуализации действий. Например, звук бросания предмета или срабатывания ловушки помогают создать в голове яркие образы. В любой момент игры пользователь может задать вопрос в свободной форме: Как победить Вампуса? Для чего нужны сокровища? Сколько у меня осталось стрел? Почему дует сквозняк? Как узнать, где находится яма? Куда я могу пойти? Напомни номера комнат. Последние две реплики незаменимы для игры без экрана, потому что между озвучиванием номеров комнат и концом реплики Рассказчика проходит достаточно много времени — можно отвлечься и всё забыть. Рано или поздно игрок скажет что-то, что не было предусмотрено системой. Это называется ошибкой No Match. В этом случае игрока нужно нежно вернуть на правильный путь и не показаться слишком навязчивым. В «Вампусе» используется два уровня обработки No Match: 1 уровень. Пользователь столкнулся с ошибкой. В этом случае Рассказчик просит его перефразировать свой запрос. 2 уровень. Пользователь дважды столкнулся с ошибкой первого уровня. На третий раз Рассказчик предлагает ему ознакомиться со списком основных команд. Получая первые логи, я заметил серьёзную проблему. Четверть игроков переставала играть после озвучивания правил (это два экрана текста). Другие игроки начинали играть, не зная правил, после чего закономерно говорили о том, что игра слишком непонятная. Чтобы исправить ситуацию, я ввёл интерактивное обучение — небольшой тестовый забег под наблюдением Рассказчика. В нём игрока учат перемещаться по комнатам, рассказывают о диковинках и даже дают победить его первого Вампуса. Игрокам подобная идея зашла и количество выходов на этапе обучения снизилось почти до нуля. А чтобы игроки не бросались играть, не зная правил, обучение запускается автоматически при первом спуске в пещеру. Проектировать голосовые игры можно разными способами: на Python, JavaScript или с помощью конструкторов диалогов. Последний вариант будет особенно полезен тем, кто мало знаком с языками программирования. Изначально я делал «Вампуса» в конструкторе Aimylogic. Но вскоре стало понятно, что мой проект слишком большой и сложный для этого. Поэтому я перешел в JAICP. JAICP — это платформа для разработки голосовых интерфейсов. Работает на собственном языке DSL, поддерживает Kotlin и JavaScript. На базовом уровне осваивается за пару вечеров. Ниже я расскажу о том, как реализовать несколько полезных функций и покажу, как работают комнаты в «Вампусе». Материал рассчитан на людей, слабо знакомых с программированием. Важно: перед началом работы в Aimylogic или JAICP обязательно ознакомьтесь с их тарифами. От выбранного тарифа зависит максимальное количество новых пользователей, которые смогут сыграть в вашу игру. Подробно об использовании языка DSL можно узнать из документации Just AI. Я лишь кратко опишу базовые вещи, необходимые для понимания этой статьи. В основе работы голосовых навыков лежат стейты (state). Стейт — это текущее состояние системы: что сказал пользователь и как на это отреагировал бот. Что может входить в стейт: intent — намерение пользователя (фразы, на которые должен отреагировать бот); intent! — тоже намерение, но может срабатывать в любом месте диалога; script — различные скрипты (например, назначение переменных); a — ответ бота; buttons — кнопки (саджесты); go! — переход в другой стейт. Переменные выполняют в игре множество функций: считают ходы игрока, убирают  собранные предметы и размещают опасности в случайных комнатах. Переменные бывают локальными и глобальными: $session — локальные, обнуляются при выходе из игры. $client — глобальные, сохраняются для текущего пользователя и не обнуляются при выходе. В «Вампусе» глобальные переменные считают количество завершенных игр и убитых Вампусов. Чем больше игр сыграно, тем короче становится приветственное сообщение. Чем больше монстров убито, тем выше ранг пользователя. При работе с глобальными переменными может возникнуть проблема, связанная с отсутствием стартового значения при первом запуске. Вот как её можно обойти: Локальные переменные определяют всё остальное: ачивки, экипировку, количество стрел и так далее. Стартовые значения для них задаются и обнуляются перед запуском новой партии. Например: В «Вампусе» рандом встречается на каждом шагу: от наименования комнат — до путей бегства монстра. Чтобы события происходили случайно, я использую системную переменную $jsapi.random(х) +1, где x — количество возможных значений. Затем прописываю поведение системы для каждого значения переменной (с помощью условного оператора). Обратите внимание, что оператор = записывает значение в переменную, а оператор == сравнивает с этим значением. Рандом в фразах делается с помощью YAML-справочника. В нём хранятся варианты фраз для каждого случая: Чтобы фраза выбиралась случайно, в стейт добавляется скрипт с методом $reactions.random: Здесь phrases — это название справочника, а pit — вариативная фраза. Чтобы быстро подставлять и менять звуки, их удобно хранить в одном месте. Для этого также используется YAML-справочник.  sil <[200]> указывает на то, что перед воспроизведением звука должна быть пауза в 200 миллисекунд. Коды звуков я беру из Платформы диалогов Яндекса, куда загружаю исходные аудиофайлы. Чтобы сослаться на звук из справочника, достаточно вставить в текст озвучки  конструкцию вида {{sounds.torch.answers}}, где sounds — название файла, а torch — название звука.  Параметр tts (text-to-speech) указывает на то, что произносимая ботом реплика будет отличаться от той, что выводится на экран. В конце игры Рассказчик озвучивает количество полученных пользователем очков и сравнивает это значение с результатами других очков. Также выводится список полученных достижений и случайная напутственная фраза.  Итоговый счёт складывается из значений других переменных, которые заполняются по ходу игры. Например, каждая пройденная комната увеличивает значение переменной $session.moves. Затем к каждой переменной применяется свой множитель очков. Далее считаются ачивки. Для этого тоже используются переменные, расставленные в комнатах. Например, достижение «Неосмотрительный» (ach_quickpit) выдаётся в том случае, если пользователь упал в яму в первые три хода: Итоговая сумма очков заносится в Google Таблицу. Для этого используется встроенная интеграция: Настроить проверку значений таблицы в реальном времени мне не удалось, поэтому я взял выборку из 10 000 последних значений и отсортировал их. Затем выделил ключевые интервалы, чтобы сравнивать с ними результат пользователя. Далее мы выводим детальную информацию по очкам и озвучиваем полученные достижения. Для примера представим, что в игре их только два: Когда игрок попадает в комнату, игра записывает номер комнаты, чтобы пользователь в любой момент мог спросить, где он находится. Далее идёт проверка на наличие в ней особых объектов. Если таких объектов нет — переходим в стейт Event. В стейте Events мы увеличиваем счётчик совершенных пользователем ходов и смотрим на получившееся число. При выпадении определённого значения переходим в соответствующее событие: SmallLoot и BigLoot — сундуки; Trap — ловушка; Talk — трёп Рассказчика; Earthquake, Flud и Lava — варианты катаклизмов. Какой именно произойдет — зависит от значения переменной $session.disaster (генерируется случайно). В стейте Inspection происходит осмотр комнаты. Становится запутаннее, держитесь! Каждая комната в игре — перевёртыш, то есть может называться по-разному в зависимости от значения переменной $session.room_X_name. За счёт этого в пещеру из 20 комнат удалось вместить 40 наименований. В некоторых комнатах проигрываются собственные звуки или уникальные реплики Рассказчика. Чтобы они не воспроизводились при повторном прохождении через комнату, используется переменная $session.room_X_sound. После наступления катаклизма часть комнат меняет своё название. Произойдет ли это или нет — зависит от переменной $session.room_X_status: 1 — не меняется, 2 или 3 — меняется (сделал так для того, чтобы менялось большинство комнат, а не строго половина). Как только диковинка собрана, переменная $session.item_RX_collected принимает значение 1. Это значит, что при повторном посещении комнаты диковинки уже не будет. Ну и последний шаг в этом стейте — проверка наличия опасностей в соседних комнатах и воспроизведение соответствующего звука: Заметьте, что проверка условий заканчивается переходом в Navigation — это вспомогательный стейт с кнопками, который «пристёгивается» к основным: Вот так выглядит полностью «собранный» стейт в самой игре: Дальнейшее развитие событий зависит от пользователя. Если он решает выстрелить в комнату, то выполняется проверка соседних помещений, вычитаются стрелы и объявляются результаты выстрела. Логи — наше всё. Они показывают, как именно пользователи взаимодействуют с игрой: какие слова используют и в каких местах испытывают трудности. Благодаря анализу логов я обнаружил огромное количество неучтённых фраз, которые обогатили словарный запас Рассказчика.  Первое время я просматривал все логи вручную. Но когда их стало приходить по 500–600 штук в день — начал фильтровать по ключевым словам и наличию ошибок. Лучший способ понять проблемы игры на раннем этапе разработки — дать протестировать прототип кому-нибудь из своего окружения. В отличие от логов данный метод позволяет выявить неучтённые моменты и задать вопросы о восприятии игры в целом. После пятой сыгранной партии Рассказчик предлагает пользователю оставить отзыв в свободной форме (голосом, не покидая игры). Это помогло узнать о ключевых проблемах, которые невозможно отследить по логам: однообразии, нехватке стрел и завышенной сложности на старте. А вместе с этим — получить ворох хвалебных отзывов, возмущений и детского мата. Вот мои любимые: Перед обновлением игры я прогоняю нововведения в тестовой комнате. Она находится в релизной версии и активируется особой кодовой фразой. Здесь я оцениваю качество звучания реплик Рассказчика, проверяю интонации, расставляю паузы и акценты.  В комнате также есть кнопки для запуска определённых стейтов. В игре с большим количеством рандома это значительно ускоряет тестирование. В следующих проектах я планирую сделать упор на использовании голоса в различных игровых механиках: решении головоломок, пении и сотворении заклинаний. Ну а пока — буду рад, если вы попробуете «Охоту на Вампуса» и напишите пару слов о своём опыте. Кстати, если вам интересен мир разговорных технологий — ищите актуальные новости, исследования и экспертные заметки в телеграм-канале Hey Voice, который ведёт наша команда TORTU. Разработчик цифровых продуктов