Каков способ внедрения гибкой системы buff / debuff?

<сильный> Обзор:

Множество игр с подобной RPG статистике позволяют использовать «баффы» персонажа, начиная от простого «Нанесения 25% дополнительного урона» на более сложные вещи, такие как «Нанести 15 урона обратно атакующим при попадании».

Специфика каждого типа баффа действительно не актуальна. Я ищу (предположительно объектно-ориентированный) способ обработки произвольных баффов.

<сильный> Детали:

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

Имеет ли смысл это решение? Я, конечно же, вижу, что нужны десятки типов событий, похоже, что создание нового подкласса для каждого баффа является излишним, и это, похоже, не позволяет использовать любые «взаимодействия» баффа. То есть, если бы я хотел реализовать кепку при увеличении урона, так что, даже если у вас было 10 разных баффов, которые дают 25% дополнительного урона, вы делаете 100% дополнительно вместо 250%.

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

Как относительно неопытный программист на C ++ (я обычно использовал C во встроенных системах), я чувствую, что мое решение упрощено и, вероятно, не в полной мере использует объектно-ориентированный язык.

Мысли? Кто-нибудь здесь разработал довольно надежную систему баффа?

Изменить: Относительно ответа (ответов):

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

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

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

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

В любом случае, я все еще надеюсь, что кто-то придет с волшебной магией «OO», которая позволит мне применить заклинание +2 на один оборот , a нанести 50% урона, полученного обратно атакующему , и автоматически телепортироваться в соседнюю плитку при атаке из 3 или более фрагментов в одной системе без поворота +5 силы в свой собственный подкласс.

Я думаю, что самое близкое - это ответ, который я обозначил, но пол по-прежнему открыт. Спасибо всем за вход.

64 голоса | спросил gkimsey 1 J0000006Europe/Moscow 2012, 22:45:48

9 ответов


31

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

  • модификаторы к атрибутам игрока
  • специальные эффекты, которые происходят на определенных событиях
  • комбинации выше.

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

Затем я обернуваю его функциями для доступа к измененным атрибутам. например:.

  def get_current_attribute_value (attribute_id, критерии):
    val = character.raw_attribute_value [attribute_id]
    # Накопить модификаторы
    для эффекта в character.all_effects:
        val = effect.apply_attribute_modifier (attribute_id, val, критерии)
    # Убедитесь, что он не превышает границы игрового дизайна
    val = apply_capping_to_final_value (val)
    return val

class Effect ():
    def apply_attribute_modifier (attribute_id, val, критерии):
        если attribute_id в self.modifier_list:
            modifier = self.modifier_list [attribute_id]
            # В настоящее время действует модификатор?
            if modifier.criteria == критерии:
                # Применить мультипликативный модификатор
                return val * modifier.amount
        еще:
            return val

class Modifier ():
    amount = 1.0 # default, который не имеет эффекта
    Критерии = Нет # применяется все время
 

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

Значение критерия - позволить вам реализовать «+ 20% против Undead» - установить значение UNDEAD в Эффекте и передать значение UNDEAD только get_current_attribute_value () , когда вы вычисляете ущерб бросьте против врага нежити.

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

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

  # Это метод персонажа, называемый во время боя
def on_receive_damage (damage_info):
    для эффекта в character.all_effects:
        effect.on_receive_damage (character, damage_info)

class Effect ():
    self.on_receive_damage_handler = DoNothing # функция по умолчанию, которая ничего не делает
    def on_receive_damage (character, damage_info):
        self.on_receive_damage_handler (character, damage_info)

def reflect_damage (character, damage_info):
    damage_info.attacker.receive_damage (15)

reflect_damage_effect = new Effect ()
reflect_damage_effect.on_receive_damage_handler = reflect_damage
my_character.all_effects.add (reflect_damage_effect)
 

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

ответил Kylotan 2 J0000006Europe/Moscow 2012, 16:41:18
21

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

Идея была простой, и пока мы применяли ее в Python, она была довольно эффективной.

В принципе, вот как это получилось:

  • Пользователь имел список применяемых в настоящее время баффов и дебаффов (обратите внимание, что бафф и дебафф относительно одинаков, это просто эффект, который имеет другой результат).
  • Buffs имеют множество атрибутов, таких как продолжительность, имя и текст для отображения информации, а также время жизни. Важными являются время жизни, продолжительность и ссылка на актера, к которому применяется этот бафф.
  • Для Buff, когда он подключен к плееру через player.apply (buff /debuff), он будет вызывать метод start (), это применит критические изменения к игроку, такие как увеличение скорости или замедление.
  • Затем мы будем перебирать каждый бафф в цикле обновления и обновлять баффы, это увеличит их время жизни. Подклассы будут реализовывать такие вещи, как отравление игрока, предоставление игроку HP с течением времени и т. Д.
  • Когда бафф был выполнен для значения timeAlive> = duration, логика обновления удалит бафф и вызовет метод finish (), который будет отличаться от удаления ограничений скорости для игрока, чтобы вызвать небольшой радиус (подумайте эффекта бомбы после DoT)

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

ответил Ross 1 J0000006Europe/Moscow 2012, 23:37:30
11

Я не уверен, что вы читаете это все еще, но я долго боролся с этой проблемой.

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


Статические модификаторы

Этот тип системы в основном полагается на простые целые числа для определения любых модификаций. Например, от +100 до Max HP, +10 для атаки и т. Д. Эта система также может обрабатывать проценты. Вам просто нужно убедиться, что укладка не выходит из-под контроля.

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

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

В целом, он отлично работает с просто устарелыми статическими модификаторами. Хотя код должен существовать в соответствующих местах для используемых модификаторов: getAttack, getMaxHP, getMeleeDamage и т. Д. И т. Д.

Если этот метод терпит неудачу (для меня), это очень сложное взаимодействие между баффами. Существует не очень простой способ взаимодействия, за исключением того, что он немного портит его. У этого есть некоторые простые возможности взаимодействия. Чтобы сделать это, вы должны внести изменения в способ хранения статических модификаторов. Вместо использования перечисления в качестве ключа вы используете String. Эта строка будет Enum name + дополнительная переменная. 9 раз из 10, дополнительная переменная не используется, поэтому вы все равно сохраняете имя перечисления в качестве ключа.

Давайте сделаем быстрый пример: Если вы хотите иметь возможность изменять урон от существ нежити, вы можете иметь упорядоченную пару, например: (DAMAGE_Undead, 10) ПОВРЕЖДЕНИЕ - это Enum, а Undead - дополнительная переменная. Поэтому во время боя вы можете сделать что-то вроде:

  dam + = attacker.getMod (Mod.DAMAGE + npc.getRaceFamily ()); //в этом случае семья расы будет нежитью
 

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

  1. Определите, имеет ли плеер этот мод.
  2. Где-то у вас есть код для выполнения телепортации, если он будет успешным. Расположение этого кода - само обсуждение!
  3. Получить правильные данные с карты Mod. Что означает значение? Это комната, в которой они телепортируются? Что делать, если у игрока есть два режима телепорта? Разве сумма не будет добавлена ​​вместе ?????? ПРОВАЛ!

Итак, это подводит меня к следующему:


Конечная комплексная буферная система

Я однажды попытался написать 2D MMORPG сам. Это была ужасная ошибка, но я многому научился!

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

Эта система имела ряд классов для каждой модификации, поэтому такие вещи, как: ChangeHP, ChangeMaxHP, ChangeHPByPercent, ChangeMaxByPercent. У меня был миллион этих ребят - даже такие вещи, как TeleportOnDeath.

У моих классов были вещи, которые бы сделали следующее:

  • applyAffect
  • removeAffect
  • checkForInteraction <--- important

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

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

  • Buff 1: Наносит 10 урона от огня при атаке.
  • Buff 2: Увеличивает урон от огня на 25%.
  • Buff 3: Увеличивает урон от огня на 15.

Метод checkForInteraction будет обрабатывать все эти воздействия. Чтобы сделать это, каждый аффект на ВСЕХ игроков рядом был проверен! Это связано с тем, что тип аффектов, которые я имел дело с несколькими игроками в течение диапазона области. Это означает, что у кода НИКОГДА не было никаких специальных заявлений, подобных выше - «если бы мы только что умерли, мы должны проверить телепорт на смерть». Этасистема будет автоматически обрабатывать его правильно в нужное время.

Пытаясь написать эту систему, мне потребовалось 2 месяца и несколько раз взорвали голову. ОДНАКО, он был ДЕЙСТВИТЕЛЬНО мощным и мог делать безумное количество вещей - особенно, когда вы принимаете во внимание следующие два факта для способностей в моей игре: 1. У них были целевые диапазоны (т. Е. Одиночные, self, group only, PB AE self, PB AE target, target AE и т. Д.). 2. Способности могут иметь более чем 1 влияние на них.

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

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

Итак, мы переходим к моей третьей версии (и еще одному типу бафф-системы):


Сложный класс Affect с обработчиками

Итак, это в значительной степени комбинация первых двух: Мы можем иметь статические переменные в классе Affect, который содержит много функциональности и дополнительных данных. Затем просто вызовите обработчики (для меня, в основном, некоторые статические утилиты вместо подклассов для определенных действий. Но я уверен, что вы могли бы пойти с подклассами для действий, если вы тоже захотите), когда мы хотим что-то сделать.

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

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

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

Таким образом, он имеет большую производительность первой системы и все еще много сложнее, чем второй (но не AS много). В Java, по крайней мере, вы можете сделать несколько сложных вещей, чтобы получить производительность почти первой в случаях MOST (например: наличие карты enum ( http://docs.oracle.com/javase/6/docs/api/java/util/EnumMap.html ) с Enums в качестве ключей и ArrayList аффектов в качестве значений.Это позволяет вам увидеть, если вы быстро повлияли [поскольку список будет равен 0 или карта не будет перечислять] и не будет постоянно перебирать списки влияний игрока без каких-либо причин. Если вы хотите, чтобы это произошло, я буду оптимизировать позже, если это станет проблемой.

В настоящее время я снова открываю (переписывая игру на Java вместо базы кода FastROM, изначально изначально) моей MUD, которая закончилась в 2005 году, и я недавно столкнулся с тем, как я хочу реализовать свою систему баффов? Я буду использовать эту систему, потому что она хорошо работала в моей предыдущей неудачной игре.

Ну, надеюсь, кто-то найдет некоторые из этих идей полезными.

ответил dayrinni 20 ThuEurope/Moscow2012-12-20T08:30:10+04:00Europe/Moscow12bEurope/MoscowThu, 20 Dec 2012 08:30:10 +0400 2012, 08:30:10
5

Другой класс (или адресуемая функция) для каждого баффа не переполняет, если поведение этих баффов отличается друг от друга. У одной вещи было бы + 10% или + 20% баффов (что, конечно, было бы лучше представлено как два объекта одного и того же класса), другие будут реализовывать совершенно разные эффекты, которые в любом случае потребуют настраиваемый код. Тем не менее, я считаю, что лучше иметь стандартные способы настройки игровой логики вместо того, чтобы позволить каждому баффу делать все, что угодно (и, возможно, мешать друг другу непредвиденными способами, нарушая баланс игры).

Я предлагаю разделить каждый «цикл атаки» на шаги, где каждый шаг имеет базовое значение, упорядоченный список модификаций, который может быть применен к этому значению (возможно, ограничен), и конечный кеп. Каждая модификация имеет преобразование идентичности по умолчанию и на нее могут влиять нулевые или более баффы /дебаффы. Специфика каждой модификации будет зависеть от применяемого шага. Как реализован цикл, зависит от вас (включая возможность архитектуры, управляемой событиями, как вы обсуждали).

Одним из примеров цикла атаки может быть:

  • рассчитать атаку игрока (base + mods);
  • вычислить защиту противника (base + mods);
  • выполните разницу (и примените моды) и определите базовый урон;
  • рассчитать любые эффекты парирования /доспеха (моды на базовом уроне) и нанести урон;
  • вычислить любой эффект отдачи (моды на базовый урон) и применить к атакующему.

Важно отметить, что ранее в цикле buff применяется больше эффекта, который он будет иметь в результате . Таким образом, если вы хотите более «тактический» бой (когда умение игрока более важно, чем уровень персонажа), создайте много баффов /дебаффов по базовой статистике. Если вам нужен более «сбалансированный» бой (где уровень важнее - важно в MMOG для ограничения скорости прогресса), используйте только баффы /дебаффы позже в цикле.

Различие между «Модификациями» и «Баффами», о которых я упоминал ранее, имеет цель: решения о правилах и балансе могут быть реализованы на первом, поэтому любые изменения в них не должны отражать изменения в каждом классе последний. OTOH, числа и виды баффов ограничены только вашим воображением, поскольку каждый из них может выразить свое желаемое поведение, не принимая во внимание любое возможное взаимодействие между ними и другими (или даже существование других вообще).

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

ответил mgibsonbr 1 J0000006Europe/Moscow 2012, 23:40:45
3

Я не знаю, читаете ли вы его, но вот как я это делаю сейчас (код основан на UE4 и C ++). Обдумав проблему более двух недель (!!), я наконец нашел это:

http://gamedevelopment.tutsplus.com/tutorials/using-the-composite-design-pattern-for-an-rpg-attributes-system--gamedev-243

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

В любом случае, я начал с атрибута wrapping в single struct:

  USTRUCT (BlueprintType)
struct GAMEATTRIBUTES_API FGAAttributeBase
{
    GENERATED_USTRUCT_BODY ()
общественности:
    UPROPERTY ()
        FName AttributeName;
    UPROPERTY (EditAnywhere, BlueprintReadOnly, Category = "Value")
        float BaseValue;
    /*
        Это значение maxmum этого атрибута.
    * /
    UPROPERTY (EditAnywhere, BlueprintReadOnly, Category = "Value")
        float ClampValue;
защищенный:
    float BonusValue;
    //float OldCurrentValue;
    float CurrentValue;
    float ChangedValue;

    //карта модификаторов.
    //Это может быть TArray, но карта выглядит проще в этом случае
    //нам нужно отслеживать добавленные /удаленные эффекты и видеть
    //если этот эффект повлиял на этот атрибут.
    TMap <FGAEffectHandle, FGAModifier> Модификаторы;

общественности:

    inline float GetFinalValue () {return BaseValue + BonusValue; };
    inline float GetCurrentValue () {return CurrentValue; };
    void UpdateAttribute ();

    void Добавить (float ValueIn);
    void Subtract (float ValueIn);

    //inline float GetCurrentValue ()
    //{
    //возвращаем FMath :: Clamp <float> (BaseValue + BonusValue + AccumulatedBonus, 0, GetFinalValue ()) ;;
    //}

    void AddBonus (const FGAModifier & amp; ModifiersIn, const FGAEffectHandle & amp; Handle);
    void RemoveBonus (const FGAEffectHandle & amp; Handle);

    void InitializeAttribute ();

    void CalculateBonus ();

    inline bool operator == (const FGAAttributeBase & amp; OtherAttribute) const
    {
        return (OtherAttribute.AttributeName == AttributeName);
    }

    inline bool operator! = (const FGAAttributeBase & amp; OtherAttribute) const
    {
        return (OtherAttribute.AttributeName! = AttributeName);
    }

    inline bool IsValid () const
    {
        return! AttributeName.IsNone ();
    }
    friend uint32 GetTypeHash (const FGAAttributeBase & amp; AttributeIn)
    {
        return AttributeIn.AttributeName.GetComparisonIndex ();
    }
};
 

Это еще не закончено, но основная идея состоит в том, что эта структура отслеживает его внутреннее состояние. Атрибуты могут быть изменены только эффектами. Попытка изменить их напрямую небезопасна и не подвергается дизайнерам. Я предполагаю, что все, что может взаимодействовать с атрибутами, - это Эффект. Включая плоские бонусы от предметов. Когда новый предмет оборудован, создается новый эффект (вместе с дескриптором), который добавляется к выделенной карте, которая обрабатывает бесконечные бонусы (те, которые должны быть удалены вручную игроком). Когда применяется новый Эффект, создается новая Ручка для него (дескриптор - это просто int, завернутый в struct), а затем этот дескриптор передается вокруг как средство для взаимодействия с этим эффектом, а также отслеживает, если эффект все еще активны. Когда эффект удаляется, его ручка передается всем заинтересованным объектам для очистки.

Важной частью этого является TMap (TMap - хешированная карта). FGAModifier - очень простая структура:

  struct FGAModifier
{
    EGAAttributeOp AttributeMod;
    float Value;
};
 

Он содержит тип модификации:

  UENUM ()
enum class EGAAttributeOp: uint8
{
    Добавить,
    Вычесть,
    Умножить,
    Делить,
    Задавать,
    PRECENTAGE,

    Инвалид
};
 

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

Мы добавляем новый эффект с помощью простой функции, а затем вызываем:

  void FGAAttributeBase :: CalculateBonus ()
{
    float AdditiveBonus = 0;
    auto ModIt = Modifiers.CreateConstIterator ();
    для (ModIt; ModIt; ++ ModIt)
    {
        switch (ModIt-> Value.AttributeMod)
        {
        case EGAAttributeOp :: Добавить:
            AdditiveBonus + = ModIt-> Value.Value;
                ломать;
            по умолчанию:
                ломать;
        }
    }
    float OldBonus = BonusValue;
    //вычисляем окончательный бонус от значений модификаторов.
    //мы не обрабатываем укладку здесь. Он проверяется и обрабатывается до того, как эффект будет добавлен.
    BonusValue = AdditiveBonus;
    //это абсолютный максимум (не зажатый прямо сейчас).
    float addValue = BonusValue - OldBonus;
    //сброс до max = 200
    CurrentValue = CurrentValue + addValue;
}
 

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

Теперь моя самая большая проблема заключается в обработке атрибута Damaging /Healing (без учета пересчета всего стека), я думаю, что я несколько решил, но для этого все еще требуется более 100%.

В любом случае Atttributes определяются следующим образом (+ Unreal macros, опущены здесь):

  FGAAttributeBase Health;
FGAAttributeBase Energy;
 

и др.

Кроме того, я не уверен в 100% об обработке CurrentValue атрибута, но он должен работать. Они теперь так.

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

ответил Łukasz Baran 10 FebruaryEurope/MoscowbTue, 10 Feb 2015 16:27:45 +0300000000pmTue, 10 Feb 2015 16:27:45 +030015 2015, 16:27:45
2

Я работал над небольшой MMO, и все элементы, полномочия, баффы и т. д. имели «эффекты». Эффект был классом, который имел переменные для «AddDefense», «InstantDamage», «HealHP» и т. Д. Силы, элементы и т. Д. Обрабатывали бы продолжительность для этого эффекта.

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

Например, у вас есть бафф, который добавляет защиту. Для этого баффа будет минимум ActionID и Duration. При его использовании он будет применять EffectID к символу в течение указанной продолжительности.

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

Этот метод позволяет вам перебирать список применяемых в настоящее время эффектов.

Надеюсь, я достаточно четко объяснил этот метод.

ответил Degree 2 J0000006Europe/Moscow 2012, 03:16:47
2
  1. Если вы являетесь пользователем единства, вот что начать: http: //www.stevegargolinski.com/armory-a-free-and-unfinished-stat-inventory-and-buffdebuff-framework-for-unity/

Я использую ScriptableOjects как buffs /spells /talents

  public class Spell: ScriptableObject
{
    public SpellType SpellType = SpellType.Ability;
    public SpellTargetType SpellTargetType = SpellTargetType.SingleTarget;
    public SpellCategory SpellCategory = SpellCategory.Ability;
    общеобразовательные MagicSchools MagicSchool = MagicSchools.Physical;
    public CharacterClass CharacterClass = CharacterClass.None;
    public string Описание = "no description available";
    public SpellDragType DragType = SpellDragType.Active;
    public bool Active = false;
    public int TargetCount = 1;
    public float CastTime = 0;
    public uint EffectRange = 3;
    public int RequiredLevel = 1;
    public virtual void OnGUI ()
    {
    }
}
 

с помощью UnityEngine; используя System.Collections.Generic;

public enum BuffType {     Buff,     Debuff } [System.Serializable] открытый класс BuffStat {     public Stat Stat = Stat.Strength;     public float ModValueInPercent = 0.1f; }

  public class Buff: Spell
{
    public BuffType BuffType = BuffType.Buff;
    public BuffStat [] ModStats;
    public bool PersistsThroughDeath = false;
    public int AmountPerTick = 3;
    public bool UseTickTimer = false;
    public float TickTime = 1.5f;
    [HideInInspector]
    public float Ticktimer = 0;
    public float Продолжительность = 360; //в секундах
    public float ModifierPerStack = 1.1f;
    [HideInInspector]
    public float Timer = 0;
    public int Stack = 1;
    public int MaxStack = 1;
}
 

BuffModul:

  с использованием System;
использование RPGCore;
использование UnityEngine;

публичный класс Buff_Modul: MonoBehaviour
{
    частная единица _unit;

    //Используйте это для инициализации
    private void Awake ()
    {
        _unit = GetComponent <Unit> ();
    }

    #region BUFF MODUL

    public virtual void RUN_BUFF_MODUL ()
    {
        пытаться
        {
            foreach (var buff в _unit.Attr.Buffs)
            {
                CeckBuff (положительный эффект);
            }
        }
        catch (Исключение e) {throw new Exception (e.ToString ());}
    }

    #endregion BUFF MODUL

    public void ClearBuffs ()
    {
        _unit.Attr.Buffs.Clear ();
    }

    public void AddBuff (string buffName)
    {
        var buff = Instantiate (Resources.Load («Scriptable /Buff /» + buffName, typeof (Buff))) как Buff;
        if (buff == null) return;
        buff.name = buffName;
        buff.Timer = buff.Duration;
        _unit.Attr.Buffs.Add (положительный эффект);
        foreach (var buffStat в buff.ModStats)
        {
            switch (buff.BuffType)
            {
                case BuffType.Buff:
                    _unit.Attr.AddBuffStatValue (buffStat.Stat, Mathf.RoundToInt ((_ unit.Attr.StatsBase [buffStat.Stat] + _unit.Attr.StatsItem [buffStat.Stat]) * buffStat.ModValueInPercent));
                    ломать;
                case BuffType.Debuff:
                    _unit.Attr.RemoveBuffStatValue (buffStat.Stat, Mathf.RoundToInt ((_ unit.Attr.StatsBase [buffStat.Stat] /* + unit.character.StatsItem [_stat.stat] * /) * buffStat.ModValueInPercent));
                    ломать;
            }
            Core.StatController (_unit.Attr, buffStat.Stat);
        }
    }

    public void RemoveBuff (Buff buff)
    {
        foreach (var buffStat в buff.ModStats)
        {
            switch (buff.BuffType)
            {
                case BuffType.Buff:
                    _unit.Attr.RemoveBuffStatValue (buffStat.Stat, Mathf.RoundToInt ((_ unit.Attr.StatsBase [buffStat.Stat] + _unit.Attr.StatsItem [buffStat.Stat]) * buffStat.ModValueInPercent));
                    ломать;
                case BuffType.Debuff:
                    _unit.Attr.AddBuffStatValue (buffStat.Stat, Mathf.RoundToInt ((_ unit.Attr.StatsBase [buffStat.Stat] /* + unit.character.StatsItem [_stat.stat] * /) * buffStat.ModValueInPercent));
                    ломать;
            }
            Core.StatController (_unit.Attr, buffStat.Stat);
        }
        _unit.Attr.Buffs.Remove (положительный эффект);
    }

    void CeckBuff (Buff buff)
    {
        buff.Timer - = Time.deltaTime;
        if (! _unit.IsAlive & amp;! buff.PersistsThroughDeath)
        {
            if (buff.ModStats! = null)
                foreach (var stat в buff.ModStats)
                {
                    _unit.Attr.StatsBuff [stat.Stat] = 0;
                }

            RemoveBuff (положительный эффект);
        }
        if (_unit.IsAlive & amp; & amp; buff.Timer <= 0)
        {
            RemoveBuff (положительный эффект);
        }
    }
}
 
ответил user22475 7 FebruaryEurope/MoscowbThu, 07 Feb 2013 04:04:19 +0400000000amThu, 07 Feb 2013 04:04:19 +040013 2013, 04:04:19
0

Это был реальный вопрос для меня. У меня есть идея об этом.

  1. Как уже говорилось, нам нужно реализовать список Buff и логический модуль обновления для баффов.
  2. Затем нам нужно изменить все настройки каждого игрока в подклассах класса Buff .
  3. Затем мы получаем текущие настройки проигрывателя из поля изменяемых настроек.

  class Player {
  настройки: AllPlayerStats;

  частные баффы: Array <Buff> знак равно
  private baseSettings: AllPlayerStats;

  конструктор (настройки: AllPlayerStats) {
    this.baseSettings = настройки;
    this.resetSettings ();
  }

  addBuff (buff: Buff): void {
    this.buffs.push (положительный эффект);
    buff.start (это);
  }

  findBuff (predcate (buff: Buff) => boolean): Buff {...}

  removeBuff (buff: Buff): void {...}

  update (dt: number): void {
    this.resetSettings ();
    this.buffs.forEach ((item) => item.update (dt));
  }

  private resetSettings (): void {
    //некоторый способ скопировать базу в настройки
    this.settings = this.baseSettings.copy ();
  }
}

класс Buff {
    частный владелец: игрок;

    start (владелец: Player) {this.owner = owner; }

    update (dt: number): void {
      //здесь мы меняем все, что хотим, в подклассах
      this.owner.settings.hp + = 15;
      //если нам нужно базовое значение, просто сделайте owner.baseSettings общедоступным, но не меняйте его! только читать

      //также здесь логика для удаления buff по времени или чему-то
    }
}
 

Таким образом, легко добавить новую статистику игрока без изменения логики подклассов Buff .

ответил DantaliaN 28 WedEurope/Moscow2016-12-28T18:11:40+03:00Europe/Moscow12bEurope/MoscowWed, 28 Dec 2016 18:11:40 +0300 2016, 18:11:40
0

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

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

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

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

Если бы я разрабатывал систему buff /debuff, вот некоторые вещи, которые я бы подумал:

  • Класс buff /debuff для представления самого эффекта.
  • Класс типа buff /debuff, содержащий информацию о том, что влияет на бафф и как.
  • Персонажи, элементы и, возможно, местоположения, все должны иметь свойство списка или коллекции, чтобы содержать баффы и дебаффы.

Некоторые особенности, которые должны содержать типы buff /debuff:

  • Кому /к чему это можно применить, IE: игрок, монстр, местоположение, элемент и т. д.
  • Какой тип эффекта (положительный, отрицательный), будь то мультипликативный или аддитивный, и какой тип статистики он влияет, IE: атака, защита, движение и т. д.
  • Когда он должен быть проверен (бой, время суток и т. д.).
  • Можно ли его удалить, и если да, то как его можно удалить.

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

Пока я устанавливаю подходящие типы, просто создать запись о баффе, в которой говорится:

  • Тип: Проклятие
  • ObjectType: Item
  • StatCategory: Утилита
  • StatAffected: MovementSpeed ​​
  • Продолжительность: Бесконечность
  • Триггер: OnEquip

И так далее, и когда я создаю бафф, я просто назначаю его BuffType Curse, а все остальное зависит от движка ...

ответил Aithos 24 FebruaryEurope/MoscowbFri, 24 Feb 2017 01:33:40 +0300000000amFri, 24 Feb 2017 01:33:40 +030017 2017, 01:33:40

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132