Game development
21 March 2011

[Перевод] Тепловые карты: Настраиваем геймплей с помощью простой системы сбора и анализа игровых показателей

В этой статье, взятой из сентябрьского выпуска 2010 года журнала Game Developer's Magazine, Chris Pruett, работающий в Google консультантом по разработке игр, рассказывает как он быстро и несложно реализовал удобную систему оценки игрового процесса в своей игре на платформе Android, под названием Replica Island.

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

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

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

Начиная с малого


Мои первые шаги в игростроении я делал, создавая игры для Game Boy Advance. В то время подход к тестированию был довольно простым: мы давали соседским ребятам специальное устройство Game Boy Advance, подключённое к видеомагнитофону, записывали их игру, после чего просматривали записи. Это позволяло нам отлавливать явные и крупные ошибки.

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

В настоящее время я работаю консультантом в области создания игр для Google Android. Моя первая игра для этой платформы, аркада Replica Island, не сильно отличается от игр для Game Boy, которые я писал 10 лет назад. Но кое-что изменилось — я не работал больше на компанию, создающую игры, я написал её для себя, с помощью одного художника, в основном работая в свободное время.



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

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

Как-же поступить независимому разработчику? Завершив разработку Replica Island, я понял, что нет никакой гарантии того, что играть в эту игру будет интересно. Игра разрабатывалась «в вакууме» и мне был нужен сторонний взгляд на игровой процесс, прежде чем я мог быть уверен в том, что игру пора выпускать.

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

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

Размышляя над системой показателей


После того случая, я подумал о системе сбора и обработки показателей для игрового процесса, которая была разработана компанией Naughty Dog для игры Crash Bandicoot.

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

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

Одним из интересных принципов, которым я руководствовался при создании своей системы, была идея компании Naughty Dog о том, что надо избегать завершения игры всеми силами. Их конечная цель заключалась в устранении моментов, когда игрок застревал на каком-то этапе и не мог продолжать игру.

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

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

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

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

Пример того, как такой тип систем может быть наиболее эффективно использован, можно увидеть в выступлении Георга Золлера (Georg Zoeller) о замечательной системе телеметрии, которую они используют в BioWare.

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

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

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

Базовая система


Система, которую я написал, состояла из трёх частей: поток выполнения, который во время игрового процесса собирает данные о действиях игрока и отправляет на сервер, сервер как таковой, и инструмент для обработки собранных данных.

Сервер — это слишком громко сказано, конечно. Мой сервер был представлен 30-ю строчками PHP-скрипта, который проверял GET-запрос по HTTP и записывал результаты в базу данных MySQL. Структура GET-запроса, в свою очередь, тоже была очень простой. Это название события, координаты, версия кода, идентификатор сессии и временная отметка (timestamp).

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

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

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

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

Отмечаем неудачи на карте ярко-красным цветом


Как только моя базовая система стала работать, я выпустил обновление для бета-тестеров и стал наблюдать за поступающими данными. Довольно быстро были выявлены повторяющиеся ситуации.

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

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

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

Первый прогон системы показал лишь маленькую точку на уровне, где погиб игровой персонаж. Но с увеличением количества игроков я начал получать что-то вроде тепловых карт (heat maps), обозначающих места гибели игроков на картах уровней, которые было намного проще воспринимать. Глава, посвящённая созданию тепловой карты, будет представлена ниже по тексту.


Тепловая карта, сгенерированная на основе данных о гибели игровых персонажей в Replica Island

Наглядное пособие по просчётам в дизайне игры


Комбинация высокоуровневой игровой статистики и обозначение на уровнях мест гибели игровых персонажей пролила свет на недочёты в игровом дизайне. Я понял, к примеру, что большое количество игроков погибало при встрече с самым первым монстром. Это было не потому, что монстр был очень силён. После детального изучения ситуации я пришёл к выводу, что монстр появлялся в месте, где главный способ атаки — прыжок сверху — был трудно реализуем из-за низко расположенного потолка.

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

Также основательные изменения претерпела структура моих уровней. У меня было достаточное количество уровней с высоким временем, которое затрачивалось на их прохождение, но с малым количеством смертей. И я понял, что игроки попросту заблудились на уровне. Я переделал эти уровни, чтобы сделать порядок их прохождения более понятным. В паре случаев я даже полностью переделал уровни с нуля.

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

Мне была нужна система контроля за игровым персонажем, которая не требовала бы наличия геймпада. Так как главный герой игры, зелёный робот Android использовал ракетные двигатели на ногах, чтобы летать. Базовая модель движения заключалась в том, чтобы получить импульс ещё на поверхности перед прыжком и, используя этот импульс, с помощью двигателей лететь в нужном направлении. Двигатели расходовали энергию довольно быстро, но энергия возобновлялась при приземлении. И идея заключалась в том, что игрок будет совершать прыжок, а затем, аккуратно расходуя энергию, достигать нужных мест, либо совершать точные прыжки на противников, атакуя их.

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

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

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

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

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

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

Выпуск игры


Итак, после нескольких тестовых прогонов, мои графики стали показывать стандартное нормальное распределение, к которому я и стремился. Пришло время выпускать игру и я решил оставить систему сбора показателей встроенной в игру. Мне было интересно, будет-ли информация, которую я буду получать от новых пользователей, отличаться от той, которую я получал от группы бета-тестеров. Был только один способ выяснить это.

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

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

Этот подход казался лучшим решением. Несмотря на то, что код игры открыт, и любой мог изучить структуру отправляемых пакетов данных (я заранее убедился, что ничего из присылаемых данных нельзя сопоставить с конкретным человеком или устройством), я давал игрокам возможность сказать «нет, спасибо».

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

Как закономерный результат я получил огромный объём данных для анализа — более 14 миллионов информационных точек, около гигабайта информации о событиях, которые были сгенерированы пользователями моей игры. На момент написания статьи их количество составляло 1 200 000 игроков.

Фактически такой объём информации сломал мою систему довольно быстро. У меня есть статистика, собранная с первых 13 000 игроков, которую я опубликовал на сайте игры Replica Island. Но после выпуска игры, большинство моих инструментов для анализа перестали работать.

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

Так или иначе, этот план сработал


Я был очень удовлетворён моей системой учёта событий в Replica Island. С небольшими усилиями, которые мне почти ничего не стоили (серверная часть, которая фиксирует события, обошлась мне меньше, чем аккаунт в Xbox Live), и используя только два типа событий, я был в состоянии быстро и эффективно определить места в игре, где игроки сталкивались с проблемами.

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

Применение PHP и MySQL для реализации серверной части было хорошим решением. Фиксирование событий было настолько тривиальным, что я уверен, это могло быть просто реализовано на любом языке. На PHP реализация всей серверной части заняла у меня менее получаса.

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

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

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

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



Шишки, которые я набил


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

Решение использовать PHP для серверной части было хорошим ходом. Однако было ошибкой использовать PHP для обработки полученных данных. Моя идея заключалась в том, чтобы делать всё через веб-интерфейс. Я даже написал свой редактор уровней на PHP и JavaScript.

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

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

Я столкнулся с большим количеством ошибок в интерфейсе PHP GD (формирование изображений с альфа-каналом попросту не работало), и решил просто уменьшить размер изображений уровней для последующей их обработки.

Для этой статьи я переписал этот инструмент используя Python и ImageMagick.

И результаты оказались впечатляющими. Код этой реализации можно скачать с официального сайта Game Developer magazine.

В итоге, хоть эти данные и говорили мне о том, когда игровой персонаж погибал и сколько требовалось времени для прохождения уровней, они не давали мне информации для того, чтобы определить моменты, не связанные с гибелью главного персонажа игры, когда игроки бросают играть в мою игру (game shelf moments).

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

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

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

Будущее


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

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

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

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

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

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

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

Полученные от системы показателей выгоды с лихвой перекрывали затраты на неё в Replica Island. Сохранив простоту клиентской и серверных частей, я получил много полезной информации о дизайне уровней игры и привычках игроков, и, как результат, игра только стала лучше.

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

Как генерировать тепловые карты

Генерация тепловых карт не так сложна, но вот поиск точной инструкции потребовал от меня определённых усилий. Я использовал метод похожий на описанный здесь.



Основные действия заключаются в следующем:

  1. Генерируем изображение круга в градациях серого, цвет которого изменяется от чёрного в центре до прозрачного у краёв по радиальному градиенту. Это изображение для отображения точки, в которой произошло событие.
  2. Генерируем прямоугольное изображение с цветным градиентом. Низ изображения пусть будет белым или красным, или любого цвета, который вы выберете как обозначение наиболее «горячего» места на тепловой карте. Верх изображения должен быть чёрным с несколькими цветами в промежутке. Это изображение будет использоваться как «словарь» для формирования графического отчёта по статистическим данным позднее.
  3. Генерируем список координат прошедших событий.
  4. Рассчитываем максимальное количество точек возникших событий, которые находятся в непосредственной близости друг от друга. Это будет максимальным значением «тепла» для тепловой карты.
  5. Для каждого уникального местоположения в списке событий рисуем изображение круга по координатам события. Рисуем изображение с коэффициентом непрозрачности, рассчитываемым по формуле:

    (количество событий в этом месте)/(максимальное значение "тепла") * 100%

    Используем умножающую модель переноса (multiply transfer mode: src * dst) для смешивания точек нарисованных кругов друг с другом.

    Накладываем полученное изображение на прозрачную канву (canvas).

    Когда процесс будет завершён мы получим изображение с большим количеством тёмных пятен с изменяющейся интенсивностью черного цвета. Это — промежуточное изображение, которое будет подвергаться обработке на следующем шаге.
  6. Берём изображение полученное на предыдущем шаге и добавляем в него цвет. Берём уровень прозрачности (alpha) для каждой точки и на его основе вычисляем Y-координату в «цветном словаре», построенном на шаге 2 для того, чтобы рассчитать цвет для обрабатываемой точки.
  7. Берём полученное изображение и накладываем его поверх изображения игрового уровня. Места возникновения событий будут показаны как цветные области, где увеличивающаяся интенсивность цвета показывает области, в которых происходило больше событий.

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

Можно легко выявить ошибки точности (precision bugs), которые будут заметны при большом количестве событий, так что вклад одного события в общую картину будет менее одного процента.

Инструменты вроде ImageMagick могут помочь вам решить эту задачу.

+52
4k 92
Comments 14