Такие характеристики, как Сила, Интеллект, Ловкость, стали основными во многих играх. И когда мы получаем +5 силы от предмета, -10% от дебаффа и + 15% от таланта, хорошо иметь систему, которая систематизированно отслеживает все эти модификаторы.
Прежде чем начать, нужно отдать должное, моя реализация была в значительной степени вдохновлена этим постом. Это руководство по Flash, в котором весь код написан на ActionScript, но в любом случае концепция великолепна.
Для основ этой системы нам нужен класс, представляющий Стат, с переменной для Базового значения Стат и список для хранения всех индивидуальных Модификаторов, которые были применены к этому Стат:
Нам также нужен класс для представления модификаторов характеристик:
Что такое
readonly
?
Когда переменная объявлена какreadonly
, вы можете присвоить ей значение только во время ее объявления или внутри конструктора ее класса. В противном случае компилятор выдаст ошибку. Это предотвращает непреднамеренное выполнение таких действий, как:
statModifiers = null;statModifiers = new List<StatModifier>();
Если в любой момент нам понадобится свежий пустой список, мы всегда можем просто вызватьstatModifiers.Clear()
.
Обратите внимание, что ни один из этих классов не является производным от MonoBehaviour
. Мы не будем прикреплять их к игровым объектам.
Хотя статистика обычно представляет собой целые числа, я использую float
вместо int
, потому что при применении процентных бонусов мы можем легко получить десятичные числа. Таким образом, статистика более точно отображает значение, и тогда мы можем делать любое округление, которое сочтем нужным. Кроме того, если нам действительно нужна статистика, которая не является целым числом, она уже учтена.
Теперь нам нужно иметь возможность добавлять и удалять модификаторы в / из статистики, вычислять окончательное значение статистики (с учетом всех этих модификаторов), а также получать окончательное значение. Добавим в класс CharacterStat
следующее:
Если вы похожи на меня, то, вероятно, вас беспокоит тот факт, что мы звоним CalculateFinalValue()
каждый раз, когда нам нужно значение статистики. Чтобы этого избежать, внесем следующие изменения:
Отлично, мы можем добавить к нашей статистике «плоские» модификаторы, но как насчет процентов? Хорошо, это означает, что существует как минимум 2 типа модификаторов статистики, давайте создадим enum
для определения этих типов:
Нам нужно изменить наш StatModifier
класс, чтобы учесть эти типы:
И измените наш CalculateFinaValue()
method в классе CharacterStat
, чтобы работать с каждым типом по-разному:
Почему такое странное вычисление процентов?
Допустим, у нас есть значение 20, и мы хотим добавить к нему + 10%.
Сначала мы можем вычислить много 10% умножается на 0,1:
20 * 0,1 = 2
Затем мы добавляем результат этого к исходному значению:
20 + 2 = 22
Имея это в виду, эту странную строку кода можно было бы записать так:
finalValue += finalValue * mod.Value;
Однако, поскольку исходное значение всегда равно 100%, и мы хотим добавить к нему 10%, легко увидеть, что это сделает его 110%.
Это означает, что мы можем сначала сложить проценты:
1 + 0,1 = 1,1
А затем умножьте исходное значение на результат:
20 * 1,1 = 22
Это работает и для отрицательных чисел: если мы хотим изменить на -10%, это означает, что у нас останется 90%, поэтому мы умножаем на 0,9.
Теперь мы можем работать с процентами, но наши модификаторы всегда применяются в том порядке, в котором они добавляются в список. Если у нас есть навык или талант, увеличивающие нашу силу на 15%, а затем мы экипируем предмет с силой +20 (после получения этого навыка), эти + 15% не будут применяться к только что экипированному предмету.
Наверное, мы не этого хотим. Нам нужен способ сообщить стату порядок, в котором действуют модификаторы.
Для этого внесем следующие изменения в класс StatModifier
:
Что, черт возьми, с этим конструктором?
В C # для вызова конструктора из другого конструктора вы, по сути, «расширяете» конструктор, который хотите вызвать.
В этом случае мы определили конструктор, которому нужны толькоvalue
иtype
, затем он вызывает конструктор, которому также требуетсяorder
, но передаетint
представлениеtype
в качестве порядка по умолчанию.
Как работает
(int)type
?
В C # каждому элементуenum
автоматически назначается индекс. По умолчанию первый элемент равен 0, второй - 1 и т. Д. Вы можете назначить собственный индекс, если хотите, но нам это не нужно… пока. Если вы наведете указатель мыши на элементenum
, вы увидите индекс этого элемента во всплывающей подсказке (по крайней мере, в Visual Studio).
Чтобы получить индекс элемента
enum
, мы просто приводим его кint
.
С этими изменениями мы можем установить order
для каждого модификатора, но если мы этого не сделаем, модификаторы flat будут применяться перед модификаторами процент. Таким образом, по умолчанию мы получаем наиболее распространенное поведение, но мы также можем делать другие вещи, например, принудительно применять специальный модификатор flat после всего остального.
Теперь нам нужен способ применения модификаторов в соответствии с их order
при вычислении окончательного значения статистики. Самый простой способ сделать это - отсортировать список statModifiers
всякий раз, когда мы добавляем новый модификатор. Таким образом, нам не нужно менять метод CalculateFinalValue()
, потому что все уже будет в правильном порядке.
Как работают
Sort()
иCompareModifierOrder()
?
Sort()
- это метод C # для всех списков, который, как следует из названия, сортирует список. Критерии, которые он использует для сортировки списка, должны быть предоставлены нами в форме функции сравнения. Если мы не предоставляем функцию сравнения, она использует компаратор по умолчанию (что бы он ни делал).
Функция сравнения будет использоваться методом
Sort()
для сравнения пар объектов в списке. Для каждой пары объектов возможны 3 ситуации:
1. Первый объект
a
должен располагаться перед вторым объектомb
. Функция возвращает отрицательное значение (-1).
2. Первый объектa
должен располагаться после второго объектаb
. Функция возвращает положительное значение (1).
3. Оба объекта равны по «приоритету». Функция возвращает 0.
Часть 1 на этом заканчивается, надеюсь, это было полезно. Вы можете перейти к части 2 здесь:
Я буду читать и отвечать на каждый комментарий, поэтому не стесняйтесь оставлять комментарии, если у вас есть какие-либо вопросы, предложения или отзывы.
До свидания!