Русский
Русский
English
Статистика
Реклама

Баланс

Автоматизация или смерть как управлять тысячами единиц игрового контента с помощью гугл-таблиц

10.06.2021 18:12:02 | Автор: admin

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

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

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

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

На первых годах жизни Pixel Gun 3D использовалась простая схема: весь контент добавлялся и редактировался вручную. Нужно поменять урон пушке? Заходишь в Unity, открываешь нужный файл и правишь руками. Дело на пару минут.

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

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

Нужно было глобально что-то менять.

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

Из гугл-таблиц в Unity

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

Для получения данных с таблиц мы используем Google Apps Script. Первое время заводили отдельные скрипты на каждую таблицу, в которых обрабатывали данные в JSON. Затем, получая в редакторе JSON, применяли их по назначению.

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

Так выглядит наш скрипт:
function doGet(e){ if (e === undefined || e.parameter === undefined) {   return FailWithMessage("nullable parameters"); } var tableId = e.parameter["table"]; var listName = e.parameter["list"]; if (listName !== undefined && listName !== "" && listName !== "null") {   var startRow = parseInt(e.parameter["startRow"]);   var startColumn = parseInt(e.parameter["startColumn"]);   var numRow = parseInt(e.parameter["numRow"]);   var numColumn = parseInt(e.parameter["numCol"]);   return GetSigleList(tableId, listName, startRow, startColumn, numRow, numColumn); } else {   return GetAllTable(tableId); }}  function GetSigleList(tableId, listName, startRow, startColumn, numRow, numColumn){ var ss = SpreadsheetApp.openById(tableId); if (ss == null) {   return FailWithMessage("table with name: " + tableId + "not found"); } var sheet = ss.getSheetByName(listName); if (sheet == null) {   return FailWithMessage("list with name: " + listName + "not found"); }  if (numRow < 1) numRow = sheet.getLastRow(); if (numColumn < 1) numColumn = sheet.getLastColumn(); var range = sheet.getRange(startRow, startColumn, numRow, numColumn); var data = range.getValues(); var str = JSON.stringify(data);  var resultObject = {   "resultCode": 2,   "message": str }; var result = JSON.stringify(resultObject); return ContentService.createTextOutput(result);}  function GetAllTable(tableId){ var ss = SpreadsheetApp.openById(tableId); if (ss == null) {   return FailWithMessage("table with name: " + tableId + "not found"); }  var result = {};  var listModes = ss.getSheets(); for(var i = 0; i< listModes.length; i++) {   var sheet = listModes[i];   var sheetName = sheet.getSheetName();     var range = sheet.getRange(1, 1, sheet.getLastRow(), sheet.getLastColumn());   var data = range.getValues();   result[sheetName] = data; }  var str = JSON.stringify(result);  var resultObject = {   "resultCode": 2,   "message": str };  var result = JSON.stringify(resultObject); return ContentService.createTextOutput(result);} function FailWithMessage(message){ var result = {   "resultCode": 1,   "message": message };   var str = JSON.stringify(result);  return ContentService.createTextOutput(str);}

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

После публикации получится ссылка такого формата:

https://script.google.com/macros/s/WwlCZODTDRXJaHhdjfwFRcKtHRQOHqzYisjndduZzDihMpXehLrNxdi/exec

Ее нужно использовать для запуска скрипта. Чтобы скрипт знал, с какой таблицы нужны данные, в get-запрос подставляем ID таблицы. Получить его можно из URL таблицы. Например, в https://docs.google.com/spreadsheets/d/example_habr/edit#gid=0, ID будет example_habr.

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

Полный запрос будет выглядеть так:

https://script.google.com/macros/s/WwlCZODTDRXJaHhdjfwFRcKtHRQOHqzYisjndduZzDihMpXehLrNxdi/exec?table=example_habr&list=MyList&startRow=1&startColumn=2&numRow=10&numCol=5

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

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

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

Но мы пошли дальше.

Из Unity в гугл-таблицы

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

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

Начали с простеньких задачек. Например, мы часто превышали лимит в 500 символов на описание апдейта в Google Play. Стор такое отклоняет, нужно переписывать и отправлять заново. Задались вопросом, а есть ли формула для подсчета символов в ячейке? Разумеется, в гугл-таблицах большой перечень базовых формул, которые можно комбинировать как угодно и решать практически любые задачи. Написали в ячейке, чтобы описание апдейта автоматически проверялось на количество символов =ДЛСТР(номер ячейки). Теперь проблемы нет.

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

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

Например, решили составить коллекции для галереи оружия (это такие подборки оружия по схожим признакам: цвет, сеттинг, сезон). Чтобы составить одну коллекцию, нужно оценить визуал, смотрятся ли они вместе, схожий ли у них класс и другие детали. Для этого сделали таблицу, в которую из базы данных выгрузили список всех пушек, написали формулу и подгрузили превьюшки, с которыми можно визуально работать. А уже из таблиц по балансу подтягиваем названия, теги и остальную информацию.

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

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

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

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

Где используем автоматизацию

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

Балансировка контента

Самые базовый способ применения, о котором уже упоминали выше. Так мы ищем проблемные места, добавляем новый контент, не ломая старый, нерфим или бафаем оружие и так далее.

Если есть ошибка в балансе пушки, то апдейтим ее прямо на сервере, после того, как пересчитали в таблице. Такие правки стараемся переводить в формат удаленного контроля, чтобы не выпускать отдельные патчи в сторы.

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

Генерация сущностей

Автоматизацию можно использовать не только для баланса, но и для генерации нового контента. Так, в каждом батлпассе у нас есть порядка 140 разных челленджей (например, сыграть 10 игр в определенном режиме или убить 30 противников из пистолета). Каждый раз придумывать это вручную долго и нудно, поэтому сделали генерацию. Собрали подробный список условий и прямо из гугл-таблицы создаем квесты теперь одна формула придумывает нам все задачи на каждый сезон.

Естественно, со сгенерированным пулом работает уже человек и настраивает кривую сложности. Игрок должен плавно войти в ивент, тем самым повысив конвертацию в платеж или в более длинную игровую сессию.

Подобным образом мы генерируем задачи и для клановых войн.

Пример формулы:

=ifs(I2="EndMatch";ifs(AE2<=TasksData!$O$34+TasksData!$N$34;"TeamDuel";AE2<=TasksData!$O$34+TasksData!$N$34+TasksData!$M$34;ЕСЛИ(A2=0;"TeamDuel";"Spleef");AE2<=TasksData!$O$34+TasksData!$N$34+TasksData!$M$34+TasksData!$L$34;"Duel";AE2<=TasksData!$O$34+TasksData!$N$34+TasksData!$M$34+TasksData!$L$34+TasksData!$K$34;ЕСЛИ(A2=0;"Duel";"BattleRoyale");AE2<=TasksData!$O$34+TasksData!$N$34+TasksData!$M$34+TasksData!$L$34+TasksData!$K$34+TasksData!$J$34;ЕСЛИ(A2=0;"TeamFight";"DeadlyGames");AE2<=TasksData!$O$34+TasksData!$N$34+TasksData!$M$34+TasksData!$L$34+TasksData!$K$34+TasksData!$J$34+TasksData!$I$34;"CapturePoints";AE2<=TasksData!$O$34+TasksData!$N$34+TasksData!$M$34+TasksData!$L$34+TasksData!$K$34+TasksData!$J$34+TasksData!$I$34+TasksData!$H$34;"FlagCapture";AE2<=TasksData!$O$34+TasksData!$N$34+TasksData!$M$34+TasksData!$L$34+TasksData!$K$34+TasksData!$J$34+TasksData!$I$34+TasksData!$H$34+TasksData!$G$34;"Deathmatch";AE2>TasksData!$O$34+TasksData!$N$34+TasksData!$M$34+TasksData!$L$34+TasksData!$K$34+TasksData!$J$34+TasksData!$I$34+TasksData!$H$34+TasksData!$G$34;"TeamFight"); I2="killPlayer";ifs(AE2<=TasksData!$N$35;"TeamDuel";AE2<=TasksData!$N$35+TasksData!$L$35;"Duel";AE2<=TasksData!$N$35+TasksData!$L$35+TasksData!$K$35;ЕСЛИ(A2=0;"TeamFight";"BattleRoyale");AE2<=TasksData!$N$35+TasksData!$L$35+TasksData!$K$35+TasksData!$J$35;"DeadlyGames";AE2<=TasksData!$N$35+TasksData!$L$35+TasksData!$K$35+TasksData!$J$35+TasksData!$I$35;"CapturePoints";AE2<=TasksData!$N$35+TasksData!$L$35+TasksData!$K$35+TasksData!$J$35+TasksData!$I$35+TasksData!$H$35;"FlagCapture";AE2<=TasksData!$N$35+TasksData!$L$35+TasksData!$K$35+TasksData!$J$35+TasksData!$I$35+TasksData!$H$35+TasksData!$G$35;"Deathmatch";AE2>TasksData!$N$35+TasksData!$L$35+TasksData!$K$35+TasksData!$J$35+TasksData!$I$35+TasksData!$H$35+TasksData!$G$35;"TeamFight"); I2="killPet";ifs(AE2<=TasksData!$G$36;"Deathmatch";AE2>TasksData!$G$36;"TeamFight"); I2="killPlayerThroughWall";ifs(AE2<=TasksData!$I$37;"CapturePoints";AE2<=TasksData!$I$37+TasksData!$H$37;"FlagCapture";AE2<=TasksData!$I$37+TasksData!$H$37+TasksData!$G$37;"Deathmatch";AE2>TasksData!$I$37+TasksData!$H$37+TasksData!$G$37;"TeamFight"); I2="killPlayerFlying";ifs(AE2<=TasksData!$I$38;"CapturePoints";AE2<=TasksData!$I$38+TasksData!$H$38;"FlagCapture";AE2<=TasksData!$I$38+TasksData!$H$38+TasksData!$G$38;"Deathmatch";AE2>TasksData!$I$38+TasksData!$H$38+TasksData!$G$38;"TeamFight");I2="ramEscort";"Siege";I2="escortDestroyGate";"Siege";I2="winBrNoChest";"BattleRoyale";I2="crashChest";"BattleRoyale";I2="winBrInParty";"BattleRoyale";I2="flagCapture";"FlagCapture";I2="pointCapture";"CapturePoints")

Пример таблицы с вводными для генератора:

Пример формулы (в ней представлена часть с генерацией описаний задач, Key_номер это ключи локализаций, из которых составляются задачи):

=IFS(I2="endMatch";(ЕСЛИ(T2=0;"Key_7220";"Key_7234"));I2="killPet";ЕСЛИ(W2="None";"Key_7228";"Key_7224");I2="killPlayer";ЕСЛИ(Q2=1;"Key_7227";(ЕСЛИ(W2="NONE";ЕСЛИ(R2=1;"Key_7232";ЕСЛИ(S2=1;"Key_7233";"Key_7221"));"Key_7216")));I2="killPlayerFlying";"Key_7225";I2="killPlayerThroughWall";"Key_7226";I2="ramEscort";"Key_7235";I2="escortDestroyGate";"Key_7236";I2="winBrNoChest";"Key_7229";I2="crashChest";"Key_7230";I2="winBrInParty";"Key_7231";I2="flagCapture";"Key_7237";I2="pointCapture";"Key_7238";I2="";"")

И еще более эпичная, уже проходящая через ячейки параметров и рандомайзеров:

=ifs(L3="DeadlyGames";0;L3="BattleRoyale";0;L3="TeamDuel";0;1=1;ЕСЛИ(I3="killPlayer";ifs(A3=0;ЕСЛИ(СЧЁТЕСЛИ($I$2:I2;"killPlayer")>=(TasksData!$B$34+TasksData!$B$36+TasksData!$B$38)*6;ЕСЛИ(СЧЁТЕСЛИ($I$2:I2;"killPlayer")<(TasksData!$B$34+TasksData!$B$36+TasksData!$B$38+TasksData!$B$47)*6;1;0);0);A3=1;ЕСЛИ(СЧЁТЕСЛИ($I$2:I2;"killPlayer")>=(TasksData!$B$34+TasksData!$B$36+TasksData!$B$38+TasksData!$B$47+TasksData!$B$48+TasksData!$C$34+TasksData!$C$36+TasksData!$C$38)*6;ЕСЛИ(СЧЁТЕСЛИ($I$2:I2;"killPlayer")<(TasksData!$B$34+TasksData!$B$36+TasksData!$B$38+TasksData!$C$34+TasksData!$C$36+TasksData!$C$38+TasksData!$B$47+TasksData!$B$48+TasksData!$C$47)*6;1;0);0);A3=2;ЕСЛИ(СЧЁТЕСЛИ($I$2:I2;"killPlayer")>=(TasksData!$B$34+TasksData!$B$36+TasksData!$B$38+TasksData!$B$47+TasksData!$B$48+TasksData!$C$34+TasksData!$C$36+TasksData!$C$38+TasksData!$C$47+TasksData!$C$48+TasksData!$D$34+TasksData!$D$36+TasksData!$D$38)*6;ЕСЛИ(СЧЁТЕСЛИ($I$2:I2;"killPlayer")<(TasksData!$B$34+TasksData!$B$36+TasksData!$B$38+TasksData!$B$48+TasksData!$C$34+TasksData!$C$36+TasksData!$C$38+TasksData!$D$34+TasksData!$D$36+TasksData!$D$38+TasksData!$B$47+TasksData!$C$47+TasksData!$C$48+TasksData!$D$47)*6;1;0);0));0))

Симуляция процессов

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

Пример части вводных данных:

Пример использованных формул в ячейках:

=СЛУЧМЕЖДУ(1;100)
=СРЗНАЧ(13;15)*6
=СУММ(B4:F4)
=IFS($A32<=G32;"Mythic";$A32<=F32;"Legend";$A32<=E32;"Epic";$A32<=D32;"Rare";$A32<=C32;"Common")

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

Пример результатов дропа по номерам открытий:

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

Воркфлоу создания таблицы

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

Расскажу подробнее по шагам (цифры в примере изменены в рамках конфиденциальности):

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

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

3. Для удобства из другой таблицы с помощью формулы подтягиваются картинки пушек. Арты предварительно загружены на сервис для получения прямой ссылки в формате (важно именно расширение в конце ссылки):

http://адрес_сервиса/путь/номер/имя картинки.jpg

Для добавления в таблицу используем простую функцию:

=IMAGE("https://files.fm/u/wdrhemgnk#/view/special_offer_pixelman_reward_big.png")

Пример формулы для подтягивания артов с перебором:

=ЕСЛИ(ЕНД(ВПР(A4;importrange(имя_таблицы;Лист1!B:F);5;ЛОЖЬ))=ЛОЖЬ;ВПР(A4;importrange(имя_таблицы;Лист1!B:F);5;ЛОЖЬ); ЕСЛИ(ЕНД(ВПР(A4;importrange(имя_таблицы;Лист1!C:F);4;ЛОЖЬ))=ЛОЖЬ;ВПР(A4;importrange(имя_таблицы;Лист1!C:F);4;ЛОЖЬ); ЕСЛИ(ЕНД(ВПР(A4;importrange(имя_таблицы;Лист1!D:F);3;ЛОЖЬ))=ЛОЖЬ;ВПР(A4;importrange(имя_таблицы;Лист1!D:F);3;ЛОЖЬ); ЕСЛИ(ЕНД(ВПР(A4;importrange(имя_таблицы;Лист1!E:F);2;ЛОЖЬ))=ЛОЖЬ;ВПР(A4;importrange(имя_таблицы;Лист1!E:F);2;ЛОЖЬ);НИМА ТАКОГО))))

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

Важный момент: после подгрузки данных мы их вырезаем (ctrl+x) и вставляем без привязки к формуле (ctrl+x+v). Формулу затем удаляем, иначе после каждого обновления страницы она будет пересчитывать все строки. В данном случае более 800.

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

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

5. Также используем форматирование ячеек. Оно может выполнить множество полезных преобразований. Например, покрасить ячейки, в которых значение больше 2 или меньше 1.

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

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

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

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

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

График популярности оружияГрафик популярности оружия

Жизнь после автоматизации

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

Теперь легче что-то поменять, перезалить или вообще обновить целый класс оружия, в котором 100+ позиций. Достаточно вбить новые параметры все автоматически и с учетом механик пересчитается под норму (а часть параметров пересчитаются сами под новые вводные).

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

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

P.S. Данный подход меняет сам принцип работы с различными инструментами. Например, в том же Slack можно видеть BB-коды наглядно с помощью простой команды #:

Полезные ссылки

Подробнее..

Перевод Как мощность влияет на геймплей численный подход к дизайну игры для достижения ее честности

05.01.2021 12:12:21 | Автор: admin

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

Стоит заметить, что термин честность здесь более уместен, чем баланс, ведь последний чаще можно истолковать неверно. Честность подразумевается в том смысле, что опыт игроков должен быть выдержанным и правдоподобным. Честный геймплей должен быть приятным и увлекательным, даже если он не совсем четко сбалансирован. Идеальный баланс, в свою очередь, может сделать игру более скучной. По словам ведущего дизайнера Overwatch Джеффа Каплана:

Восприятие баланса гораздо важнее, чем сам баланс.

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

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

Во всех описанных случаях могут быть разные стратегии и отправные точки, которые можно использовать для достижения этой самой честности. Одна из них, на которой мы и сфокусируемся в данной статье, мощность (Power). Для ее определения прибегнем к термину, введенному Эрнестом Адамсом в Основах геймдизайна:

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

Другими словами, мощность это мера возможностей игрока.

Далее в статье мы:

  • обсудим, как можно измерить мощность и как это влияет на решения по геймдизайну;

  • проанализируем, как мощность зависит от ресурсов игры и как связана с игровой экономикой;

  • проведем численный анализ, который можно использовать для понимания сложности и точек развития игры;

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

  • наконец, вкратце обсудим саму концепцию мощности.

Характеристика мощности через ресурсы

Концепция мощности очень субъективна не только для самой игры в целом, но и для ее цели. Для примера рассмотрим гипотетическую игру Run & Gun, в которой игроки сражаются на арене, гоняясь друг за другом на авто и стреляя друг в друга. Побеждает последний выживший, как это было в боевом режиме Mario Kart. В этой игре оружие и его свойства теснее связаны с концепцией мощности, чем скорость машины.

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

Концепцию мощности будет легче понять, если мы будем рассматривать факторы ее измерения как игровые ресурсы. Ресурсы это любые элементы, которые можно измерить численно. В Run & Gun, у нас есть следующие ресурсы (или атрибуты):

  1. V Здоровье транспортного средства: сколько очков XP есть у вашего авто;

  2. F Сила оружия: сколько урона наносит ваша пушка одним выстрелом;

  3. A Количество боеприпасов: сколько раз вы можете выстрелить до перезарядки;

  4. S Скорость: насколько быстро вы движетесь;

  5. T Время перезарядки: сколько времени длится перезарядка.

Имея данные об этих ресурсах, можно определить формулы, по которым рассчитывается мощность транспортного средства. Суммирование всех этих ресурсов то есть, V + F + A + S + T является исходным (наивным) подходом. Проблема в том, что он предполагает, что более высокое время перезарядки приводит к более высокой мощности, хотя это, конечно же, не так. Самое простое решение этой проблемы принять максимальное время перезарядки к примеру, 60 секунд, и использовать его для инвертирования значения времени перезарядки. Например:

Поскольку 60 секунд максимальное время перезарядки, если T = 60, его вклад в значение мощности будет минимален: 60/60 = 1. С другой стороны, если T окажется совсем небольшим например, 1 или 2, его вклад в значение мощности будет 60 либо 30, что определенно гораздо больше. В дальнейшем мы будем использовать термин вклад для обозначения того, насколько атрибут увеличивает или уменьшает расчетное значение мощности.

В качестве полного примера предположим, что автомобиль Fusca имеет следующие атрибуты: V = 10, F = 2, A = 3, S = 10, T = 30. Тогда мощность Fusca равна 10 + 2 + 3 + 10 + 2 = 27. Для сравнения возьмем другой автомобиль Hilux со следующими атрибутами: V = 15, F = 3, A = 1, S = 7, T = 60. Отсюда мощность Hilux равна 15 + 3 + 1 + 7 + 1 = 27. Хотя эти две машины имеют разные характеристики, мощность их одинакова.

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

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

Как мощность влияет на экономику

Экономика игры управляет игровыми ресурсами: правилами и механиками, которые управляют, генерируют и уничтожают ресурсы. Поскольку мощность выражается через ресурсы, она также напрямую связана с экономикой игры.

Игровая экономика обычно связана с четырьмя столпами: Источниками (Sources), которые создают ресурсы; Стоками (Drains), которые уничтожают ресурсы; Преобразователями (Converters), конвертирующими одни ресурсы в другие; и Обмениками (Traders), которые обменивают ресурсы между различными объектами в игре, такими как игроки или NPC.

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

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

На диаграмме ниже показан черновик игровой экономики для Run & Gun, где Fusca противостоит Hilux:

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

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

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

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

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

Мощность, сложность и прогресс

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

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

Более современные игры, такие как Hellblade: Senua's Sacrifice, предлагают автоматический выбор сложности в зависимости от прогресса игрока. По мере того, как игрок путешествует по адскому скандинавскому ландшафту, игра автоматически адаптируется в соответствии с навыками и способностями игрока, чтобы поддерживать необходимый уровень стресса и азарта.

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

Если мощность не меняется в течение игры, различие между абсолютной и относительной сложностью можно игнорировать, как это имеет место в нашей гипотетической игре Run & Gun.

Процесс регулировки мощности с абсолютной сложностью

Предположим, что в Run & Gun есть однопользовательский режим кампании, в котором игрок сталкивается с последовательностью испытаний, чтобы стать королем арены. Если у всех машин стандартная начальная мощность будет 27 (как рассчитано ранее), начальные испытания должны либо иметь более низкое значение мощности, либо равное ему.

Например, первой задачей может быть победа над Pontiac, имеющим атрибуты V = 5, F = 1, A = 1, S = 5, T = 60 и мощность, равную 13. То есть, она составляет почти половину мощности стандартного автомобиля. Это должно помочь новым игрокам познакомиться с игрой и изучить элементы управления без чувства непосредственной угрозы или разочарования.

Однако по мере приближения к титулу короля арены игрок должен столкнуться с противниками с врагами с более высокими характеристиками, что способствует более сложному опыту. Конечным боссом может быть Volkswagen Braslia с атрибутами V = 30, F = 5, A = 3, S = 10, T = 10 и мощностью, равной 54. То есть, мощность босса в два раза превышает мощность стандартного автомобиля. Преимущество использования этого подхода заключается в том, что, если задача оказывается практически невыполнимой, мы можем прибегнуть к корректировке характеристик и затем еще раз сравнить значения мощности.

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

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

  1. Линейный: мощность увеличивается на одно и то же значение на каждом уровне.

  2. Нелинейный: мощность увеличивается нелинейно на каждом уровне например, с умножением на некий коэффициент. На графике каждый уровень умножает предыдущее значение мощности на 1,25 (эквивалент увеличения на 25%).

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

  4. Пилообразный (Sawtooth): мощность последовательно колеблется: то увеличивается, то уменьшается. Такой подход снижает сложность после каждого более сложного испытания. Обратите внимание, что разрыв мощности между уровнями становится выше, но игрок может делать перерыв после каждого такого скачка из-за меньшей сложности следующего шага. Считается, что такой подход обеспечивает хороший темп в течение игры.

Подходы Steps и Sawtooth очень похожи, поскольку они не подчиняются стандартному математическому уравнению. Разница между ними в том, что Steps никогда не снижает мощность: он либо остается прежним, либо увеличивается, в то время как Sawtooth может либо увеличивать, либо уменьшать мощность для достижения хорошего темпа.

Процесс регулировки мощности с относительной сложностью

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

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

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

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

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

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

Мощность исходит от людей

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

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

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

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

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

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

Пошаговый процесс вычисления мощности

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

  1. Составьте исходное уравнение для расчета мощности (например, суммирование).

  2. Рассчитайте мощность элементов на уровне, включая мощность игрока.

  3. Протестируйте уровень (как можно большее число раз).

  4. Проанализируйте сложность игры и трудности, с которыми сталкивается игрок.

  5. Если игроки считают, что уровень был честным (не обязательно сбалансированным), вы на верном пути, иначе:

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

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

В данном случае мощность Fusca станет 10 + 2 * 2 + 3 + 10 + 2 = 29, а мощность Hilux увеличится до 15 + 2 * 3 + 1 + 7 + 1 = 30. Из-за этого Hilux теперь немного превосходит Fusca. Мы можем уравновесить их, увеличив боезапас Fusca с 3 до 4 или уменьшив время перезарядки с 30 до 20.

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

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

Мощность через эмпатию

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

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

Тем не менее, этот процесс окажется бесполезен, если не уделить ему достаточно внимания, как это сказано в книге Джесси Шелла Искусство геймдизайна:

Самый важный навык для геймдизайнера умение слушать.

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

Хотя в этой статье был рассмотрен пример боевой системы, то же самое можно применить к системе экономики таких игр, как Rise of the Industry, или, скажем, освоения космоса в Spore.

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

Подробнее..

Баланс камень ножницы бумага. Математический подход к решению задачи

26.10.2020 14:14:03 | Автор: admin

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

Статья состоит из 3 частей:

  1. Постановка задачи

  2. Формализация (переход к постановке на математическом языке)

  3. Решение

Постановка задачи

Пусть есть три класса кораблей линкоры, крейсера и эсминцы. У каждого из них есть очки жизней, урон, наносимый противнику при попадании и меткость. Необходимо настроить эти параметры таким образом, чтобы в 60% случаев каждый тип побеждал своего антагониста:

  1. Линкоры побеждают крейсера

  2. Крейсера побеждают эсминцы

  3. Эсминцы побеждают линкоры

Формализация

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

В нашей задаче взаимодействие игроков происходит по следующей схеме:

  1. Игрок 1 делает выстрел. Вероятность попадания p1

  2. В случае попадания урон dam= dam1, где dam1 величина урона, который наносит первый игрок при попадании. В случае промаха dam= 0. Количество здоровья игрока 2 уменьшается на величину dam

  3. Если количество единиц здоровья игрока 2 меньше либо равно 0 (hp2 <= 0), то взаимодействие оканчивается победой игрока 1, в противном случае ход переходит к игроку 2

  4. Игрок 2 делает выстрел. Вероятность попадания p2

  5. В случае попадания урон dam= dam2, где dam2 величина урона, который наносит второй игрок при попадании. В случае промаха dam= 0. Количество здоровья игрока 1 уменьшается на величину dam

  6. Если количество единиц здоровья игрока 1 меньше либо равно 0 (hp1 <= 0), то взаимодействие оканчивается победой игрока 2, в противном случае ход переходит к игроку 1 и процесс начинается заново с пункта 1

Решение

Решение состоит из 3 этапов

  1. Определить вероятность победы игрока 1 за k выстрелов

  2. Определить вероятность победы игрока 1 за произвольное количество выстрелов

  3. Произвести балансировку юнитов в соответствии с условием задачи

Этап 1

Пусть параметры игроков даны и равны (hp1, dam1, p1), (hp2, dam2, p2). Для того, чтобы упростить дальнейшие вычисления понизим количество переменных заменив hp и dam на их отношение k=hp/dam. Таким образом, вместо 6 переменных будем работать с 4, а именно (k1, p1), (k2, p2).

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

Итак, вероятность того, что игрок 1 сделав k выстрелов попадет ровно k2 раз равна

C_{k-1}^{k_2-1}p_1^{k_2}(1-p_1)^{k-k_2}

(т.е. среди первых k-1 выстрелов будет ровно k2-1 попадание, и k-ый выстрел будет попаданием). Одновременно с этим игрок 2, сделав k-1 выстрел должен попасть менее k1 раз. Вероятность этого события равна

\sum_{i=0}^{min(k_1-1, k-1)}C_{k-1}^{i}p_2^i(1-p_2)^{k-1-i}

(т.е. игрок 2 попадаем не более чем min(k1-1, k-1) раз). С учетом выше сказанного получаем, что вероятность для игрока 1 победить, сделав ровно k выстрелов равна

\begin{cases} p(1wins|k)=[C_{k-1}^{k_2-1}p_1^{k_2}(1-p_1)^{k-k_2}]\sum_{i=0}^{min(k_1-1, k-1)}C_{k-1}^{i}p_2^i(1-p_2)^{k-1-i}, \: if \: k\geq k_2 \\ p(1wins|k)=0, \: if\: k<k_2\end{cases}

Этап 2

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

p(1wins)=\sum_{i=0}^{\infty}p(1wins|i)

Написать алгоритм, для вычисления вероятности победы игрока 1, имея предыдущую формулу, задача не из трудных. Суммировать можно до тех пор, пока слагаемые больше некоторого малого числа (на пример, 0,0001).

Этап 3

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

Решение можно искать по следующему алгоритму

  1. Понятно, что искомые переменные (hp, dam, p) не могут принимать произвольных значений, а находятся в рамках некоторых интервалов. Зададим эти интервалы:

    1. Для вероятностей победы классов над антагонистами

      1. 0.595 <= p(линкор, крейсер) <= 0.605

      2. 0.595 <= p(крейсер, эсминец) <= 0.605

      3. 0.595 <= p(эсминец, линкор) <= 0.605

    2. Для здоровья: нижняя граница 60, верхняя 200 (здесь беру общие границы для всех классов, но можно было задать для каждого отдельно, на подход к решению это никак не повлияет)

    3. Для урона: нижняя граница 8, верхняя 15

    4. Вероятности будем искать с шагом 0.01, здоровье с шагом 10, а урон с шагом 1.

  2. Находим все (k1, p1), (k2, p2) такие, что 0.595 <= p(x, y) <= 0.605 (p(x, y) вероятность победы игрока x над игроком y см. этап 2)

  3. Находим все (k1, k2, k3, k4, k5, k6, p1, p2, p3) так, чтобы удовлетворять удовлетворить ограничениям из пункта 1.1 данного алгоритма

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

\begin{cases} {hp_1\over{dam_2}}=k_1, {hp_2\over{dam_1}}=k_2 \\ {hp_2\over{dam_3}}=k_3, {hp_3\over{dam_2}}=k_4 \\ {hp_3\over{dam_1}}=k_5, {hp_1\over{s \: dam_3}}=k_6 \end{cases}

Здесь s десятичное число от 0 до 1, смысл которого объясняется ниже

Найденные наборы (hp1, dam1, p1), (hp2, dam2, p2), (hp3, dam3, p3) искомое решение задачи баланса.

Важное замечание по пункту 4 алгоритма. Решения задачи в изначальной постановке не существует т.к. в рамках принятых ограничений один из классов (линкоры) обязательно будет доминировать. Для того чтобы это исправить его антагонисту (эсминцам) необходимо дать бонус к атаке. Параметр s отвечает за коррекцию, на пример, s= 1.3 означает 30% бонус.

Итоги

  1. Поставленная задача решена полностью

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

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

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

Успехов в балансе

Камень, ножницы, бумага. Цу, е, фа ;)

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru