Я делаю 2D-игру сверху вниз и хочу иметь много разных типов атак. Я хотел бы сделать атаки очень гибкими и совместимыми, как работает «Привязка Исаака». Вот список всех предметов коллекционирования в игре . Чтобы найти хороший пример, давайте посмотрим на предмет Spoon Bender .
Ложка Бендера дает Исааку возможность стрелять в слезы.
Если вы посмотрите на раздел «Синергия», то увидите, что его можно комбинировать с другими предметами коллекционирования для получения интересных, но интуитивно понятных эффектов. Например, если он сочетается с «Внутренним глазом» , это «позволит Исааку сделать несколько выстрелов в самонаводящихся местах одновременно». Это имеет смысл, потому что Внутренний Глаз
Дает Исааку тройной выстрел
Какая хорошая архитектура для дизайна таких вещей? Вот решение грубой силы:
if not spoon bender and not the inner eye then ...
if spoon bender and not the inner eye then ...
if not spoon bender and the inner eye then ...
if spoon bender and the inner eye then ...
Но это очень быстро выйдет из-под контроля. Какой лучший способ спроектировать такую систему?
источник
Ответы:
Вам совершенно не нужно вручную кодировать комбинации. Вместо этого вы можете сосредоточиться на свойствах, которые дает вам каждый элемент. Например, пункт А устанавливает
Projectile=Fireball,Targetting=Homing
. Предмет B устанавливаетFireMode=ArcShot,Count=3
.ArcShot
Логика отвечает за отправку изCount
числаProjectile
элементов в дуге.Эти два элемента можно комбинировать с любыми другими элементами, которые свободно изменяют эти (или другие) свойства. Если вы добавите новый тип снаряда, он будет автоматически работать с ним
ArcShot
, а если вы добавите новый режим стрельбы, он автоматически будет работать сFireball
снарядами. Аналогично,Targetting
это свойство, которое устанавливает контроллер для снарядов, в то же времяFireMode
создает снаряды, поэтому их можно легко и тривиально комбинировать в любой комбинации, что имеет смысл.Вы также можете установить зависимости свойств и тому подобное. Например,
ArcShot
требует, чтобы у вас был поставщикProjectile
(который может быть просто по умолчанию). Вы можете установить приоритеты так, чтобы, если у вас есть два активных элемента, которые предоставляютProjectile
код, знал, какой из них использовать. Или вы можете предоставить пользовательский интерфейс, чтобы позволить пользователю выбирать тип используемого снаряда, или просто потребовать от игрока снять высокоприоритетные предметы, которые ему не нужны, или использовать самый последний предмет и т. Д. Вы можете дополнительно разрешить систему несовместимостей. Например, так, что два предмета, которые просто модифицируются,Projectile
не могут быть оснащены одновременно.В общем случае , когда это возможно, предпочитают любой вид данных ориентированных на подход (или декларативного ) по процедурным подходам (большой если- то еще мур) , когда дело доходит до предметов и таких в вашей игре. Универсальная логика более высокого уровня, которую можно настроить с помощью простых данных, гораздо предпочтительнее жестко закодированных списков правил в специальном регистре.
источник
Если вы используете язык ООП, это звучит как хорошее место для использования шаблона Decorator . Если вы хотите изменить способ атаки, просто украсьте ее соответствующим дополнением.
Crude c ++ Пример:
Этот метод был бы наилучшим, если у вас очень большое количество атак, и вам нужно, чтобы все они вели себя примерно одинаково. Если вы хотите существенно изменить способ атаки с помощью модификатора (например, новая анимация с модификатором), тогда этот метод не для вас.
источник
Attack
метод объекта, который он агрегирует.TripleAttack
Класс не должен знать оTearAttack
классе. Если бы это было правдой, это привело бы к таким же головным болям, как и кelse-if
блоку. Это означает, что любые анимации слез должны находиться внутриTearAttackBehaviour
объекта. Этот объект не знает (и не должен знать), что он был украшенTripleAttack
объектом. Результатом является то, что 3 анимации слезы идут независимо, потому что они независимы.Как фанат Биндинга Исаака, я тоже задавался вопросом, как сделать что-то подобное. Система в игре достаточно надежна, когда возникающее поведение возникает из-за комбинации эффектов (один, который приходит мне в голову, это получение зеркала, изгиб ложки, а некоторые усилители дальности приводят к закрученной, самонаводящейся слезе вокруг Исаака, стиль Магнето ). Огромное количество из них сделает блок «если» нецелесообразным.
Мой вывод заключается в том, что Исаак и его слезы - это две сущности в центре массивной Структуры Компонент-сущность . У сущностей есть некоторые базовые характеристики (скорость движения, жизнь, радиус действия, урон, спрайт и т. Д.), И каждый компонент должен иметь модификатор статистики и глагол.
В коде у Исаака и его слез будет каждый список, который будет содержать элементы интерфейса. Исаак будет иметь список вещей, которые подписываются на интерфейс IsaacMutator, и его слезы tearMutator. IsaacMutator будет иметь функции для изменения здоровья, скорости, диапазона, внешности и некоторых специальных глаголов Айзека. TearMutator был бы похожим. Один раз за игровой цикл Исаак будет проходить через все имеющиеся у него IsaacMutators, и все живые слезы тоже. Если следовать вашему английскому примеру, это будет выглядеть так:
и так далее. Поскольку типы являются аддитивными, вы можете складывать, добавлять и удалять содержимое вашего сердца.
источник
Я думаю, что ваш путь работает лучше всего. Каждый из этих элементов дает условие: если их использовать вместе, они создают разные условия, тогда вам фактически потребуются все 3 возможных условия.
Вы также можете сделать это, создав новый тип определения, когда присутствуют оба элемента, но это фактически добавляет к свертке:
источник