Это вторая часть туториала по характеристикам персонажа. Смотрите часть 1 здесь:



Есть также видеоверсия этого урока, если вы предпочитаете это:

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

Но что, если мы хотим, чтобы определенные модификаторы складывались аддитивно? Это означает, что предыдущий пример приведет к бонусу 200% вместо 400%.

Давайте добавим третий тип модификатора, изменив наш StatModType enum на это:

Не забудьте изменить Percent на PercentMult в методе CalculateFinalValue() (внутри класса CharacterStat). Или просто используйте функции переименования Visual Studio, чтобы сделать это за вас ^^.

Внутри метода CalculateFinalValue() нам нужно добавить пару вещей для работы с новым типом модификатора. Теперь это должно выглядеть так:

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

Для следующего бита давайте добавим переменную Source в наш класс StatModifier. Таким образом, позже, когда в нашей игре появятся вещи, добавляющие модификаторы (например, предметы и заклинания), мы сможем сказать, откуда взялись те или иные модификаторы.

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

Класс StatModifier должен выглядеть так:

Допустим, мы экипировали предмет, который дает как фиксированный +10, так и +10% бонус к Силе. Как система работает в настоящее время, мы должны сделать что-то вроде этого:

Но теперь, когда у наших модификаторов есть Source, мы можем сделать кое-что полезное в CharacterStat — мы можем удалить сразу все модификаторы, которые были применены определенным Source. Добавим для этого метод:

Почему цикл for работает в обратном порядке?
Чтобы объяснить это, давайте посмотрим, что происходит, когда мы удаляем первый объект из списка по сравнению с когда мы удаляем последний объект:

Давайте представим, что у нас есть список с 10 объектами, когда мы удаляем первый, остальные 9 объектов будут смещены вверх. Это происходит потому, что индекс 0 теперь пуст, поэтому объект с индексом 1 переместится в индекс 0, объект с индексом 2 переместится в индекс 1. сильный> и так далее. Как вы понимаете, это довольно неэффективно.

Однако, если мы удалим последний объект, то ничего смещать не придется. Мы просто удаляемобъект с индексом 9, а все остальное остается прежним.

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

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

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

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

Внесите следующие изменения в CharacterStat:

ReadOnlyCollection хранит ссылку на исходный List и запрещает его изменение. Однако, если вы измените исходное statModifers (строчная буква «s»), то StatModifiers (верхняя буква «S») также изменится.

Чтобы закончить часть 2, я хотел бы добавить еще две вещи. Мы оставили BaseValue общедоступным, но если мы его изменим, это не приведет к пересчету свойства Value. Давайте это исправим.

Другое дело в StatModType enum. Давайте переопределим «индексы» значений enum по умолчанию, например:

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

Если мы хотим добавить модификатор Flat, который применяется между PercentAdd и PercentMult, мы можем просто назначить Order где-нибудь между 201 и 299.
Прежде чем мы внесем это изменение, нам нужно будет присвоить пользовательские значения Order всем PercentAdd и PercentMult тоже модификаторы.

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

Я надеюсь, что это было полезно, и пока до свидания!