Динамический лут в играх: что стоит учитывать
Люди всех возрастов любят азарт, будь то Kinder Surprise, блэкджек или компьютерные игры. Представьте, у вас в игре есть две коробки: на одной написано »Вы получите 100 монет», а на второй — »Вы получите 50–1000 монет». Сразу понятно, вокруг какой из коробок будет больше ажиотажа. А если при этом коробки покупаются за реальные деньги — мы получим политику и головную боль для разработчиков.
Однако, в этой статье я бы хотел описать личный опыт работы с динамическим лутом с точки зрения гейм-дизайнера, а также раскрыть основные инструменты, позволяющие сделать лут интересным, так как в открытом доступе информации об этом совсем немного.
Почему об этом мало пишут?
Систему динамического лута делали уже во множестве игр, однако если попытаться подсмотреть опыт других проектов, то информации найдется крайне мало. Связано это с тем, что на сегодняшний день подавляющее большинство игр с динамическим лутом — это игры, требующие постоянного соединения с серверами разработчика. В свою очередь, это предоставляет разработчику возможность скрытно и/или точечно для игроков менять правила выдачи тех или иных вещей. Подобный подход может использоваться как для честного поощрения игрока, так и для скрытой манипуляции.
Где это сделано хорошо: Diablo IIIЕсли во время игры вы были особо неудачливы и вам никак не падают легендарные вещи — с течением времени их шанс выпадения будет постоянно увеличиваться.
Где это манипуляция: потенциально любая игра с лутбоксамиЕсли лутбокс был куплен за реальные деньги, а не получен внутриигровым способом — в нем будет лежать более ценный/желанный дроп, даже если это один и тот же лутбокс.
Конечно, ни один разработчик не станет официально признавать, что они манипулируют лутом, однако, если так сделать можно, — это будет реализовано в той или иной степени. Отчасти это и является причиной, по которой так сложно найти информацию о том, как работает лут в той или иной игре.
В отдельных случаях даже приходится вмешиваться на уровне государственных указов, как, например, поступили с лутбоксами в Китае.
- Требуется выводить точный шанс выпадения вещей, включенных в состав лутбокса.
- Нельзя открыть платный лутбокс более 30 раз в сутки.
- Шанс получения желаемой вещи должен увеличиваться с числом открытий лутбокса.
- Должна быть возможность гарантированного получения вещи из лутбокса за N открытий, вне зависимости от указанного шанса.
Какие бывают механики?
В большинстве игр, где имеется система динамического лута, можно вычленить основные приемы, которыми могут пользоваться гейм-дизайнеры. Давайте попробуем разобрать по порядку основные из них:
Слоты дропа
Слот в дропе фактически означает, что игрок гарантированно получит столько вещей, сколько слотов было прописано.
Например: Открывая сундуки в Overwatch, вы всегда
получаете по 4 предмета, будь то:
стикер на стену, скин для персонажа, анимация или эмоция.
Слот может обладать индивидуальным содержимым, отличным от других слотов, например, выдавать только валюту.
- Плюсы: каждое открытие более сбалансированно, игрок примерно представляет, что именно его ждет.
- Минусы: каждый слот требует индивидуальной настройки, сложнее делать дроп с непостоянным числом позиций.
Попытки подбора
Фактически, попытка — это одна итерация подбора лута. Например, у нас есть таблица дропа ресурсов и счетчик попыток, равный 3-м. Это означает, что мы три раза пойдем в одну и ту же таблицу и попытаемся извлечь награду, добавив ее в итоговый дроп. Так, например, работает алгоритм подбора лута в Diablo II.
- Плюсы: можно сэкономить на объеме заводимых данных.
- Минусы: необходимо понимать, что новая попытка может выбрать предмет, ранее выбранный в предыдущей итерации. Иногда такое поведение может быть нежелательным.
Шанс выпадения
Буквально. Указывается число от 0 до N, где 0 означает, что предмет не выпадет никогда, а N — что выпадет в 100% случаев. Для экономии размерности часто используются целочисленные значения от 0 до 100.
- Плюсы: наглядно для дизайнера, удобно для описания случаев, когда «ничего не выпало».
- Минусы: неудобен, если среди нескольких вещей с разным шансом необходимо выбрать одну, сумма шансов может превысить 100% и тогда будет непонятно, что именно должно упасть.
Вес
Вес — это некая абстрактная мера частоты, с которой должен встречаться предмет. Если вес предмета высокий — он будет выпадать чаще остальных. Итоговый процент шанса выпадения здесь напрямую зависит от веса всех предметов из выборки. Например, у вас есть три вещи, с весами 10, 15 и 25. Шанс выпадения каждой из них считается по формуле и составляет 20%, 30% и 50% соответственно.
Для удобства: Иногда удобнее для обозначения веса указывать его, как , где value — это ценность предмета. Таким образом, более «дорогая» вещь будет автоматически иметь меньший шанс выпадения.
- Плюсы: можно добавить любое число предметов, не переживая за сумму их вероятностей.
- Минусы: для случаев, где нужно, чтобы не выпало ничего — потребуется либо ввод соответствующей заглушки с отдельным весом, либо поверх веса использовать вероятность.
Диапазон количества
Когда мы хотим, чтобы игрок получал произвольное количество предметов, но в допустимом диапазоне, — обычно указываются пограничные минимальные и максимальные значения, например от 3 до 9 включительно
- Плюсы: можно указывать точный диапазон количества предметов (вместо медианного значения и дельты, например)
- Минусы: Параметр сильно влияет на итоговую ценность выпадающих вещей, но при этом может быть не связан с вероятностью/весом самого предмета
Условия выпадения
Условия выпадения — это некие критерии, по которым мы можем решить, будет падать такой-то предмет или нет. Например, мы хотим указать, что одновременно в инвентаре игрока не может быть больше N вещей такого типа. Или что у игрока не выполнен такой-то квест и вещь ему падать не должна. Соответственно, наша логика простая: если проверка не пройдена — предмет падать не должен.
Нюанс: У подобного условия может быть два режима работы, в случае, если игрок не удовлетворяет условиям:
- Первый: проверка условия идет после того, как вещь выбрана в дроп (и тогда игрок не получает ничего).
- Второй: проверка идет перед выбором вещи, и тогда игрок взамен получит что-то другое, из оставшихся вещей в списке дропа.
Фильтры
Иногда для заведения дропа может потребоваться указать множество предметов с одинаковыми или похожими характеристиками. Чтобы не указывать каждую вещь по отдельности — можно группировать их по фильтрам. Например, за убийство босса игрок может получить любое оружие с требованиями по уровням с 10 по 15.
- Плюсы: меньше работ по обновлению дропов, если что-то добавили или удалили из игры
- Минусы: как и в случае с диапазоном количества — предметам из результатов работы фильтра может требоваться индивидуальное указание веса или вероятности.
Какая стояла задача?
В процессе работ над одним из проектов нам потребовалось создать систему динамического лута, которую мы бы могли использовать для выдачи псевдослучайных наград за выполнение квестов, открытие лутбоксов, а также за победы над противниками.
Требования у каждой из механик в большинстве случаев были общими, поэтому представлю их общим списком:
- В луте должны присутствовать ярко выраженные слоты.
- У слота может быть вероятность выпадения, отличная от 1.
- У слота, а также у каждой вещи, могут быть условия для выпадения.
- В каждом слоте может быть перечислен свой список вещей, которые могут падать.
- У каждой вещи из списка должен быть свой вес.
- Каждой вещи надо указывать точный диапазон количества min/max.
- Вещь в списке может быть описана набором фильтров, а не конкретным идентификатором.
- У вещи в списке могут быть аргументы, модифицирующие ее состояние (например — более редкое качество).
- Должна быть настройка, чтобы одна и та же вещь не могла быть сгенерирована в двух разных слотах.
- Должна быть возможность объединять вещи в группы, чтобы при наличии в сгенерированном дропе одной вещи из группы, — не падали все остальные.
Что в итоге получилось?
По итогам, вышло две структуры: список слотов динамического лута и листинг предметов.
Пример динамического дропа
"generated_drop": {
"drop_mode": "unrepeatable",
"reward_slots": [ {
"drop_requirements": [ { "foo": "bar" } ],
"drop_probability": 100.0,
"drop_list": "foo_list"
} ]
}
Пример листинга предметов
"foo_list": [ {
"item_type": "foo",
"drop_weight": 123,
"amount": [1, 2],
"tags": [ "foo" ],
"item_requirements": [ { "foo": "bar" } ],
"filters": { "foo": ["bar1", "bar2"] },
"args": { "foo": "bar"}
} ]
Алгоритм подбора лута при этом работает следующим образом:
- Берем элемент из массива reward_slots.
- Если игрок прошел условия из drop_requirements — переходим дальше, иначе — берем следующий элемент из reward_slots.
- Роллим дроп по drop_probability. Если успешно, то переходим дальше, иначе — берем следующий элемент из reward_slots.
- Перебираем все элементы связанного с слотом списка drop_list:
- Исключаем все предметы, не прошедшие проверку по item_requirements.
- Исключаем все предметы, которые уже есть в сгенерированном дропе в случае, если drop_mode выставлен как unrepeatable.
- Исключаем предметы, у которых в tags проставлен такой же тег, как в предметах, уже добавленных в текущий сгенерированный экземпляр дропа.
- Из оставшегося списка предметов выбираем один, воспользовавшись роллом по их весам из weight.
- Вносим финальные уточнения в выбранный предмет:
- Отбираем список предметов, подходящих нам по item_type и item_requirements, выбираем из них случайный.
- Модифицируем выбранный предмет через аргументы в args.
- Выбираем количество падающих предметов, взяв случайное число в интервале из amount.
- Готово. Добавляем предмет в итоговый дроп и переходим к следующему слоту из reward_slots.
Какие нюансы у решения?
Бюджет
В текущем формате записи дропа бюджет вообще не предусмотрен. Как следствие — дизайнер не может просто указать «выдай таких-то вещей, общей стоимостью N монет» и должен считать такой баланс вручную, отдельно. Учитывая, что в большинстве случаев так и делается, — это не проблема.
Переиспользование данных
В первой редакции системы дропа вообще не было отдельно вынесенных листингов предметов и фактически каждый дроп заводился отдельно. Используя листинги, можно добиться хоть какого-то переиспользования.
Примечание: Возможность указания сразу нескольких листингов предметов в рамках одного слота рассматривалась, но не была реализована, чтобы исключить конфликты мерджа листингов с разными показателями веса предметов.
Теги
Все теги действуют на дроп целиком, поэтому их наличие будет конфликтовать с попыткой использования слотов дропа в качестве попыток подбора предметов из одинакового списка. Т.е. мы должны выбрать что-то одно.
Количество выпадающих предметов и аргументы
Самое узкое место решения и источник компромиссов:
- Количество выпадающих предметов указывается в диапазоне [min, max] и его модификация извне пока остается не реализованной. В случае, если диапазон не устраивает, — придется в листинге предметов создавать еще одну запись предмета, но уже с другим количеством.
- Аргументы задают свойства предметов (например, что выпавший меч должен быть редкого качества) и также не могут быть изменены извне.
Так или иначе, данные минусы не стали серьезной проблемой для заведения дропов в игре, поэтому можно перейти к следующему пункту.
Как можно персонализировать лут?
Описанный выше механизм дропов обеспечивает честный лут, правила работы которого одинаковые для всех игроков. Давайте рассмотрим, как мы можем это изменить и сделать лут персонализированным под игрока.
Глобальный модификатор веса
Фактически, мы знаем, что ценные вещи имеют малый вес, а дешевые — большой. То есть, чтобы наградить игрока, — нам нужно увеличить вес ценных вещей и/или уменьшить вес дешевых вещей. Для этого можно ввести в игру коэффициент, привязанный к конкретному игроку и меняющийся по игровым триггерам. Проще всего его воздействие выразить через формулу , где:
- Если мы хотим дать игроку более ценные вещи — понижаем значение X в диапазоне 0 < x < 1
- Если наоборот, хотим наказать — повышаем значение X и стремимся к X > 1
- Если же хотим выдавать честно, то X = 1
Посмотрите, как в таких случаях меняется итоговый шанс выпадения вещи:
Точечный модификатор веса
Могут быть ситуации, когда требуется модификация отдельной группы предметов. В таком случае, в настройках заводится описание группы вещей (списком или фильтрами) и отдельный модификатор, равный 1 по умолчанию.
Далее, в зависимости от игровых событий, мы или увеличиваем, или уменьшаем этот модификатор.
В момент же, когда игроку будет требоваться выдать дроп, перед выбором предмета из листинга, — мы смотрим какие из вещей включены в нашу группу предметов и домножаем их вес на текущее значение модификатора:
Шаговое изменение шанса выпадения
Так как у слота дропа есть и шанс выпадения, и условия выпадения, — можно создать несколько слотов с одинаковым содержимым и разными вероятностями выпадения (и не пересекающимися условиями). Таким образом, в один момент времени, игрок может соответствовать условиям только одного из слотов, из чего и будет складываться его шанс получения награды.
Гарантированное выпадение
Иногда нам будет требоваться гарантированно выдавать какой-то предмет (или предмет из группы) на N-ное получение дропа в случае, если в интервале от 0 до N такой предмет еще не падал. Например для лутбоксов в Китае.
В таком случае, мы создаем две таблицы, 1 глобальную и 1 для каждого игрока. В них записывается вещь или группа вещей, а также число попыток получения. В глобальную — максимум попыток, а для игрока — это счетчик числа попыток без успеха получения.
Как следствие, если личный счетчик меньше максимума и предмет не выпал, — увеличиваем личный счетчик на 1. Если личный счетчик стал равен числу из глобальной таблицы — добавляем предмет в дроп, игнорируя обычные правила учета веса или вероятности. При этом, после выдачи вещи в дроп (в т.ч. обычным способом), — обнуляем счетчик у игрока.
Примечание: Таким образом можно, например, создать лутбокс, где каждое 50-е открытие будет гарантированно выдавать легендарное оружие, если предыдущие 49 открытий такого не выдали.
Заключение
В данной статье я попытался представить основные принцицы генераторов игрового лута и привел примеры способов воздействия на итоговые шансы выпадения вещей. Не стоит считать подобную схему генерации единственно верной, так как в зависимости от самой игры может меняться и подход к выдаче лута. Реализация из примера выше — лишь один из вариантов, который нас устроил в рамках имевшегося проекта. Скорее, данная статья годится в качестве шпаргалки для дизайнера, перед которым стоит задача по реализации какого-то своего механизма для динамического лута.
GL & HF!