Это очень сложный вопрос, на который полезно ответить ИМО, в основном потому, что модель сильно упрощена.
Public Sub Fight(ByRef State As Object)
Debug.Print State.Name & " slashes at the foe!"
State.Stamina = State.Stamina - 1
End Sub
Если бы я сделал Варварского Воина , который сражался с массивным боевым молотом, "поразил врага!" будет звучать как забавное преуменьшение. Кто / что является врагом ? Я знаю, что все это теоретически и упрощенно (верно?), Но если мы говорим об игре, то враг должен умереть в один момент, не так ли?
Если мы посмотрим, как традиционная JRPG может пойти по этому поводу, метод Fight
должен знать состояние как бойца, так и его цели (давайте пока оставим цель в единственном числе), поэтому для начала, он может пойти так:
Public Sub Fight(ByVal fighterState As Object, ByVal targetState As Object)
'...
End Sub
По сути, роль метода Fight
заключается в оценке / реализации изменений, которые должны произойти в targetState
, на основе ряда факторов, включающих как fighterState
, так и targetState
. Таким образом, лучшим названием для этого может быть Attack
, и мы можем предположить, что fighterState
содержит информацию о том, какая часть оружия в настоящее время экипирована и является ли это оружие «рубящим», «пробивающим», «сокрушающим» или просто «поражает» цель. Точно так же можно предположить, что targetState
содержит информацию о том, какие части доспехов экипированы на цель, и может ли это оборудование отклонять / сводить на нет или уменьшать количество полученного урона. С такой механикой у нас может даже быть PoisonBlade
, сокращающий цель, чтобы нанести то, что было вычислено, урон 76 HP, плюс повторяющийся урон 8 HP яда каждый ход, если цель не потребляет (или не получает иное) предмет Antidote
для вылечить их отравленное состояние.
Теперь, независимо от того, является ли боец Fighter
или Paladin
, или BlackMage
, не имеет значения: для механики игры нужны разные свойства и члены в каждом классе персонажей. На самом деле, игровая механика не заботится о классах персонажей, механика одинакова для всех, независимо от того, Fight
- команда пользовательского интерфейса, такая же способность, как и у любого другого. Персонаж является BlackMage
и не имеет вооружения? Отбейте - и нанесите 1 HP урона, если есть. Персонаж Paladin
и может решить "драться" или"бросить"? Команды пользовательского интерфейса, а не дизайн класса персонажей.
То, как мы проектируем модули классов, не совсем то, что они делают в учебниках с Animal
и Cat
и Dog
, где Dog
идет "гав", а Cat
- "мяу" и весь код did был вызывать Animal.Talk
в обоих случаях и poof, блестящий полиморфизм через наследование!
Я понял, что реальный код не выполняет классы Cat
и Dog
, не более чем реальная игра JRPG будет определять различные типы для каждого возможного класса персонажей в игре. - Enum
, может быть, и различные активы и ресурсы, безусловно; добавление нового класса персонажа в вашу игру должно добавлять данные , а не код. Но игровая механика не должна беспокоиться о том, насколько может отличаться Paladdin
от BlackMage
или RedWizard
, потому что различные умения и способности Paladin
по сравнению с Fighter
или BlackBelt
- это , где композиция должна войти в игру.
Видите, они не разные методы , они разные объекты .
A Fighter
не имеет «никакого понятия маны», это PlayableCharacter
экземпляр, который может быть составлен из CharacterStats
объекта, где и MP
, и MaxMP
свойства начинают игру в 0
.
Таким образом, мы делаем шаг назад и смотрим на общую картину, и без написания единственной строки кода , мы визуализируем, как вещи должны сосуществовать и что должно нести ответственность за что, для того, чтобы игра, способная сделать Paladin
удар в Dragon
: когда мы разбиваем необходимые компоненты и выясняем, как они все связаны друг с другом, мы быстро понимаем, что нет необходимости форсировать композиция может произойти где угодно, это просто случается , по необходимости!
В языке, который поддерживает наследование классов, вы можете иметь CharacterAbility
в качестве базового / абстрактного класса для таких вещей, как FightAbility
, CastSpellAbility
, UseItemAbility
и других классов, каждый из которых имеет совершенно разные реализации для своих Execute
метод. В VBA вы не можете этого сделать, поэтому вместо этого у вас может быть интерфейс ICharacterAbilityCommand
и классы FightAbility
, CastSpellAbility
, UseItemAbility
, которые его реализуют.
Теперь мы можем изобразить класс CombatController
, который знает все о каждом актере: есть экземпляр KillableGameCharacter
с именем Red Dragon
, который дает 380 XP и 1200 золота, имеет BiteAbility
, ClawAbility
, WingSpikeAbility
и, конечно, FireBreathAbility
- его CharacterStats
таковы, что его FireBreathAbility
нанесет где-то между 600 и 800 уронами от стихийных огней нашему Паладину.
Ха! Заметил это? Просто излагая, как вещи взаимодействуют друг с другом, мы знаем, что ICharacterAbilityCommand.Execute
должен взять CharacterStats
исполняющего персонажа, чтобы вычислить, насколько яростен этот драконий огонь. Таким образом, мы можем позже использовать FireBreathAbility
для более слабого монстра Wyvern
. И поскольку мы берем объект CharacterStats
, будь то характеристики Паладина , характеристики Черного Мага , характеристики Красного Дракон или Слизь , не имеет никакого значения.
И , что звучит очень точно так же, как проблема, которую вы пытались решить в первую очередь - чуть более абстрактно, так что вы не пишете код, который читается как Воин Дракона стенограмма боя; -)
Имея CharacterEquipment
, влияющую на CharacterStats
персонажа на экипировку, и любые переходные навыки, влияющие на характеристики, выпекаемые в статистику, как только они приобретены / экипированы / активированы, мы убираем необходимость в ICharacterAbilityCommand.Execute
для нужно что-нибудь кроме CharacterStats
доблестного рыцаря / игрока и CharacterStats
дракона / монстра.