Pull to refresh

Comments 4

На самом деле кода там намного больше. Потому как везде шансы, а если есть шансы значит должны быть коэффициенты, история и все это надо хранить.
и лучше уточнить про законодательства разных стран, по поводу лут боксов,
могут быть свои нюансы.
но точно знаю, что сейчас нужно писать шанс, в процентах, выпадения лута.
Интересный вопрос. Время от времени меня тоже посещают разные мысли по поводу (и без) о геймдеве: как все устроено, как сбалансировано, как это правильно реализовать.
Лично мне для понимания нужно ответить на следующие вопросы:
1. Какие типы предметов мы можем получить из ящика/награды? (пусть это будет золото, кристаллы, что-то из оружия, что-то из снаряжения, расходуемые предметы. Думаю с этим у вас проблем нет, т.к. это все как-то реализовано в вашей игре);
2. Можем ли мы сохранить ящик и открыть его когда захотим? (ну, предположим, что да, такое возможно. захотел собрать много-много и устроить себе взрыв серотонина в одном случае или депрессию на неделю в другом );
3. Влияет ли время открытия награды на ее содержимое? (вроде как в ФИФА, если сохранить пакет карточек с игроками недели, то и его содержимое будет меняться с происшествием времени (новая неделя — новые игроки), но это неточно. Проще, логичнее и справедливее сделать, чтобы не влияло, пусть и не влияет)

Ну, собственно, и все. Я бы создал таблицу «Конфигурация наград» с полями «номер награды» (идентификатор), «тип награды» (сундук, колесо фортуны, казино и пр.), «всякая прочая информация» (сколько предметов содержит, какие акции есть, что там по времени открытия (запрещать открывать или позволять открывать в определенные периоды времени и пр.). Еще нам понадобится таблица многие-ко-многим «Награды-Предметы», где будут поля «номер награды», «номер предмета» (из справочника), «тип предмета» (если у нас может быть такое, что номер предмета для разных типов предмета может повторяться), «шанс на выпадение».
Ну и логика обработки этих наград:
1. Произошло событие (игрок 1 победил босса, кстати, босс тоже может быть оформлен как «награда» с выпадением ништяков);
2. Запускаем скрипт формирования награды (должен 100% выпасть сундук в инвентарь героя);
3. Формируем этот сундук по определенным правилам (запускаем скрипт с идентификатором из конфигурации наград, у пользователя хранится «черный ящик» с идентификатором);
4. Когда приходит время, открываем сундук (обращаемся по хранимому идентификатору, забираем все, что нужно знать об этом сундуке и работаем с ним).

Как-то так я вижу это…

Тут сходятся несколько вопросов

- Как описывать и хранить награды (это по сути конфигурация для ГД) + вероятности

- Как выдавать награды визуально

- Как выдавать награды игроку

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

Первое - стоит отделить награды как награды от непосредственно ресурсов/предметов.
Можно обобщить награду (на самом деле любую) до "id": count/
Так система будет простой, понятной и плоской.

То есть вариант минимум таков
{
"gold": { "count": 3},
"diamonds": { "count": 1, "chance": 0.5},
"sword_5_3": {"count": 1}
}

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

В любом случае проблему типов можно решить 1 раз, поэтому она не так страшна, как может показаться.

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

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

Так или иначе сервис выдачи наград должен знать обо всех выдаваемых типах наград. Это может быть большой switch case, это может быть обобщение, но как то знать должен.
Единственное, что можно сделать с точки зрения open-close - инвертировать зависимость. То есть сервис умеет выдавать обобщенные награды и в нем можно зарегистрировать интерфейс "выдавальщик наград".
Выдавальщик наград умеет сказать по обобщенной награде, может ли он ее выдать, и, если может - собственно выдать (разные награды по разному - ресурсный выдавальщик - отсыпать золота, предметный - добавить мечь в инвентарь)

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

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

interface IRewardHandler {
public bool CanHandle(id: string);
public void ApplyReward(id: string, count: int);
// предполагается, что вероятности разрешает сервис наград
}


Можно сделать enum с типами, тогда выдавальщики регистрируются по нему.

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


interface IRewardHandler {
public string RewardType {get;}
public void ApplyReward(id: string, count: int);
}

public abstract class GoldRewardHandler: IRewardHandler {
public sealed override string RewardType => "gold";
public virtual void ApplyReward(id: string, count: int) {...}
}

Sign up to leave a comment.

Articles