Признаюсь, я совершил грех, злоупотребляя и даже злоупотребляя наследством. Первый (текстовый) игровой проект, который я сделал, когда проходил курс обучения по ООП, дошел до «Запертой двери» и «Открытой двери» от «Дверь» и «Комната с одной дверью», «Комната с двумя дверями» и так далее из "Комнаты".
В (графической) игре, над которой я недавно работал, я подумал, что усвоил урок и наложил ограничения на использование наследования. Однако я заметил, что проблемы скоро начинают появляться. Мой корневой класс начал раздуваться все больше и больше, и мои классы листьев были полны дублирующих кодов.
Я думал, что все еще делаю что-то не так, и, просмотрев его в Интернете, я обнаружил, что я не единственный, кто столкнулся с этой проблемой. Именно так я и обнаружил системы Entity после тщательного исследования (читай: googlefu)
Когда я начал читать его, я смог увидеть, насколько ясно он смог решить проблемы, которые у меня были с традиционной иерархией ООП с компонентами. Это было в первых чтениях однако. Когда я наткнулся на более… «радикальные» подходы ES, такие как на T-machine .
Я начал не соглашаться с методами, которые они использовали. Чистая система компонентов казалась либо излишней, либо, скорее, неинтуитивной, что, вероятно, является сильной стороной ООП. Автор доходит до того, что говорит, что система ES является противоположностью ООП, и, хотя она может использоваться в ООП, она действительно не должна. Я не говорю, что это неправильно, но я просто не чувствовал себя таким решением, которое хотел бы реализовать.
Поэтому для меня и для решения проблем, которые у меня были в начале поста, не противореча моей интуиции, все равно нужно использовать иерархию, однако это будет не монолитная иерархия, подобная той, которую я использовал раньше, а скорее полилитичный (я не мог найти слово, противоположное монолитному), который состоит из нескольких меньших деревьев.
Следующий пример показывает, что я имею в виду (это вдохновлено примером, который я нашел в Game Engine Architecture, глава 14).
У меня было бы небольшое дерево для транспортных средств. Корневой класс транспортного средства будет иметь компонент рендеринга, компонент столкновения, компонент положения и т. Д.
Тогда танк, подкласс транспортного средства, унаследует эти компоненты от него и получит свой собственный компонент «пушка».
То же самое касается персонажей. Персонаж будет иметь свои собственные компоненты, тогда Класс Игрока унаследует его и получит контроллер ввода, в то время как другие классы врага наследуют от класса Персонажа и получат контроллер AI.
Я действительно не вижу никаких проблем с этим дизайном. Несмотря на то, что система Entity Controller не используется, проблема с эффектом всплытия и большим корневым классом решается с помощью иерархии из нескольких деревьев, а проблема тяжелых дублирующих листы кода исчезла, поскольку листы не есть код для начала, только компоненты. Если необходимо внести изменения на конечный уровень, это так же просто, как изменить один компонент, вместо того, чтобы копировать код везде.
Конечно, будучи таким же неопытным, как и я, я не увидел никаких проблем, когда впервые начал использовать единую иерархию, модель с наследованием, поэтому, если возникнут проблемы с моделью, которую я сейчас собираюсь реализовать, я бы не стал быть в состоянии увидеть это.
Ваши мнения?
PS: я использую Java, поэтому использование множественного наследования вместо обычных компонентов невозможно.
PPS: межкомпонентная связь будет осуществляться путем связывания зависимых компонентов друг с другом. Это приведет к соединению, но я думаю, что это хороший компромисс.
источник
Ответы:
Рассмотрим этот пример:
Вы делаете RTS. В полной логике вы решили создать базовый класс
GameObject
, а затем два подклассаBuilding
иUnit
. Конечно, это прекрасно работает, и в итоге получается что-то похожее на это:GameObject
ModelComponent
CollisionComponent
Building
ProductionComponent
Unit
AimingAIComponent
WeaponComponent
ParticleEmitterComponent
AnimationComponent
Теперь каждый
Unit
ваш подкласс уже имеет все необходимые компоненты. И в качестве бонуса, у вас есть одно место , чтобы поместить все , что утомительный код настройки , чтоnew
S вверхWeaponComponent
, соединяет его кAimingAIComponent
иParticleEmitterComponent
-wonderful!И, конечно же, потому что он все еще разбивает логику на компоненты, когда вы в конечном итоге решаете, что хотите добавить
Tower
класс, который является зданием, но имеет оружие, вы можете управлять им. Вы можете добавитьAimingAIComponent
, aWeaponComponent
и aParticleEmitterComponent
к вашему подклассуBuilding
. Конечно, тогда вам нужно пройти и выкопать код инициализации изUnit
класса и поместить его в другое место, но это не большая потеря.И теперь, в зависимости от того, сколько у вас было предвидения, вы можете столкнуться с тонкими ошибками. Оказывается, в другом месте игры мог быть какой-то код, который выглядел так:
Это молча не работает для вашего
Tower
здания, даже если вы действительно хотели, чтобы оно работало на что-либо с оружием. Теперь это должно выглядеть примерно так:Намного лучше! После изменения этого вам приходит в голову, что любой из методов доступа, которые вы написали, может оказаться в этой ситуации. Поэтому самое безопасное, что нужно сделать, это раздеть их всех и получить доступ к каждому компоненту с помощью этого единственного
getComponent
метода, который может работать с любым подклассом. (В качестве альтернативы вы можете добавить каждыйget*Component()
суперкласс к суперклассуGameObject
и переопределить их в подклассах для возврата компонента. Впрочем, я приму первый.)Теперь ваш
Building
иUnit
классы в значительной степени только пакеты компонентов, даже не аксессоры на них. И никакой необычной инициализации тоже, потому что большую часть этого нужно было перетаскивать, чтобы избежать дублирования кода с вашим другим объектом особого случая где-нибудь.Если вы будете следовать этому до крайности, вы всегда делать из своих подклассов полностью инертные пакеты с компонентами, и в этот момент нет даже смысла иметь подклассы вокруг. Вы можете получить почти все преимущества наличия иерархии классов с иерархией методов, которая создает надлежащие компоненты. Вместо того, чтобы иметь
Unit
класс, у вас естьaddUnitComponents
метод, который делаетAnimationComponent
и также вызываетaddWeaponComponents
, который делаетAimingAIComponent
, аWeaponComponent
и аParticleEmitterComponent
. В результате у вас есть все преимущества системы сущностей, все возможности повторного использования кода в иерархии и отсутствие соблазна проверитьinstanceof
или привести к типу вашего класса.источник
Композиция над наследованием - это терминология ООП (по крайней мере, так, как я ее выучил / научил). Чтобы выбрать между композицией и наследованием, вы произносите вслух «Автомобиль - это тип колеса» (наследство) и «Автомобиль имеет колеса» (композиция). Один должен звучать более правильно, чем другой (в данном случае - композиция), и если вы не можете решить, по умолчанию используйте композицию, пока не решите иначе.
источник
Когда дело доходит до интеграции систем на основе компонентов в существующие игры на основе игровых объектов, в ковбойском программировании есть статья http://cowboyprogramming.com/2007/01/05/evolve-your-heirachy/, которая может дать вам несколько советов по способ перехода может быть сделан.
Что касается проблем, вашей модели все равно нужно будет обращаться с игроком иначе, чем с другим врагом. Вы не сможете переключать статус (т.е. игрок становится под контролем AI), когда игрок отключается или в особых случаях, не обращаясь с ним иначе, чем с другими персонажами.
Помимо интенсивного повторного использования компонентов в разных объектах, идея игр на основе компонентов - это единообразие всей системы. Каждая сущность имеет компоненты, которые могут быть присоединены и отсоединены по желанию. Все компоненты одного вида обрабатываются одинаково с помощью набора вызовов, устанавливаемых интерфейсом (базовым компонентом) каждого вида (позиция, визуализация, сценарий ...)
Смешивание наследования игровых объектов с компонентным подходом дает вам некоторые преимущества компонентного подхода с недостатками обоих подходов.
Это может работать на вас. Но я не буду смешивать оба за пределами переходного периода от одной архитектуры к другой.
PS написано на телефоне, поэтому мне сложно связываться с внешними ресурсами.
источник