Рассмотрим карточную игру, такую как Hearthstone .
Существуют сотни карт, которые делают самые разные вещи, некоторые из которых уникальны даже для одной карты! Например, есть карта (называемая Ноздорму), которая уменьшает ход игрока до 15 секунд!
Когда у вас есть такое большое количество потенциальных эффектов, как вы избегаете магических чисел и одноразовых проверок всего кода? Как избежать метода "Check_Nozdormu_In_Play" в классе PlayerTurnTime? И как можно организовать код таким образом, чтобы, когда вы добавляете еще больше эффектов, вам не нужно реорганизовывать базовые системы для поддержки того, что им никогда раньше не приходилось поддерживать?
architecture
software-engineering
scripting
Соболь Мечтатель
источник
источник
Ответы:
Вы изучили системы компонентов сущностей и стратегии обмена сообщениями о событиях?
Эффекты состояния должны быть компонентами некоторого рода, которые могут применять свои постоянные эффекты в методе OnCreate (), истекать их эффекты в OnRemoved () и подписываться на сообщения об игровых событиях, чтобы применять эффекты, возникающие как реакция на что-то происходящее.
Если эффект является постоянно условным (длится в течение X поворотов, но применяется только при определенных обстоятельствах), вам может потребоваться проверить эти условия на различных этапах.
Затем вы просто убедитесь, что в вашей игре нет магических чисел по умолчанию. Убедитесь, что все, что можно изменить, является переменной, управляемой данными, а не жестко заданными значениями по умолчанию с переменными, используемыми для любых исключений.
Таким образом, вы никогда не предполагаете, какой будет длина поворота. Это всегда постоянно проверяемая переменная, которая может быть изменена любым эффектом и, возможно, позже отменена эффектом, когда он истекает. Вы никогда не проверяете наличие исключений перед тем, как установить по умолчанию свой магический номер.
источник
RobStone находится на правильном пути, но я хотел уточнить, поскольку именно это я и сделал, когда писал «Dungeon Ho!», Roguelike, в котором была очень сложная система эффектов для оружия и заклинаний.
К каждой карте должен быть прикреплен набор эффектов, определенных таким образом, чтобы она могла указывать, что это за эффект, на что он нацелен, как и на какой срок. Например, эффект «урона противника» может выглядеть примерно так;
Затем, когда эффект срабатывает, имейте общую процедуру обработки эффекта. Как идиот, я использовал огромный оператор case / switch:
Но гораздо лучший и более модульный способ сделать это - через полиморфизм. Создайте класс Effect, который охватывает все эти данные, создайте подкласс для каждого типа эффекта, а затем переопределите этот класс для метода onExecute (), специфичного для этого класса.
Таким образом, у нас будет базовый класс Effect, а затем класс DamageEffect с методом onExecute (), поэтому в нашем коде обработки мы просто перейдем;
Чтобы понять, что находится в игре, нужно создать вектор / массив / связанный список / и т. Д. активных эффектов (типа Effect, базовый класс), прикрепленных к любому объекту (включая игровое поле / «игру»), поэтому вместо того, чтобы проверять, находится ли конкретный эффект в игре, вы просто просматриваете все эффекты, прикрепленные к объект (ы), и пусть они выполняются. Если эффект не прикреплен к объекту, он не находится в игре.
источник
Я предложу несколько предложений. Некоторые из них противоречат друг другу. Но, возможно, некоторые из них полезны.
Рассмотрите списки против флагов
Вы можете перебирать мир и проверять флаг на каждом предмете, чтобы решить, стоит ли делать флаг-вещь. Или вы можете сохранить список только тех предметов, которые должны делать пометку.
Рассмотрим списки и перечисления
Вы можете продолжать добавлять логические поля в свой класс элементов isAThis и isAThat. Или вы можете иметь список строк или перечислимых элементов, таких как {«isAThis», «isAThat»} или {IS_A_THIS, IS_A_THAT}. Таким образом, вы можете добавлять новые в перечисление (или строковые значения) без добавления полей. Не то чтобы с добавлением полей что-то было не так ...
Рассмотрим функциональные указатели
Вместо списка флагов или перечислений может быть список действий, выполняемых для этого элемента в разных контекстах. (Entity-иш ...)
Рассмотреть объекты
Некоторые люди предпочитают подходы на основе данных, сценариев или компонентов. Но стоит рассмотреть и старомодную иерархию объектов. Базовый класс должен принять такие действия, как «разыграть эту карту для фазы B хода» или что-то еще. Тогда каждый вид карты может переопределить и ответить соответствующим образом. Возможно, есть и объект игрока, и игровой объект, поэтому игра может делать что-то вроде if (player-> isAllowedToPlay ()) {do the play…}.
Рассмотреть возможность отладки
Еще одна приятная вещь о куче полей флагов в том, что вы можете одинаково проверять и распечатывать состояние каждого элемента. Если состояние представлено различными типами, или пакетами компонентов, или указателями на функции, или находится в разных списках, может быть недостаточно просто просмотреть поля элемента. Это все компромиссы.
В конце концов, рефакторинг: рассмотрим модульные тесты
Независимо от того, насколько вы обобщаете свою архитектуру, вы сможете представить вещи, которые она не охватывает. Тогда вам придется рефакторинг. Может немного, может много.
Способ сделать это более безопасным - с помощью модульных тестов. Таким образом, вы можете быть уверены, что даже если вы переставили вещи под (может быть, намного!), Существующая функциональность все еще работает. Каждый юнит-тест выглядит, как правило, так:
Как видите, поддержание стабильности этих вызовов API верхнего уровня для игры (или игрока, карты и т. Д.) Является ключом к стратегии модульного тестирования.
источник
Вместо того, чтобы думать о каждой карте в отдельности, начните думать в терминах категорий эффектов, и карты содержат одну или несколько из этих категорий. Например, чтобы вычислить количество времени в ходу, вы можете просмотреть все карты в игре и проверить категорию «манипулировать продолжительностью хода» каждой карты, которая содержит эту категорию. Затем каждая карта увеличивает или уменьшает длительность хода в соответствии с выбранными вами правилами.
По сути, это мини-компонентная система, где каждый «карточный» объект является просто контейнером для набора компонентов эффектов.
источник