Насколько объектно-ориентированы видеоигры? [закрыто]

18

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

vemv
источник
2
Как можно количественно (или даже объективно определить) объектную ориентацию? Вы хотите получить ответ в Meyers или Dahl-Nygaards?
Очевидно, они настолько тяжелы, что язык, основанный на ОО, является отраслевым стандартом.
Коммунистическая утка
4
Если вы имеете в виду C ++, он был выбран потому, что это хороший язык для изучения типового безопасного программирования с учетом скорости; некоторые функции ООП, очевидно, представляют собой неудачный дизайн, сохраняемый только для обратной совместимости. ;)

Ответы:

20

Чтобы прийти к вашему примеру, я объясню свои мысли об Entites в видеоиграх. Я не буду обсуждать общее преимущество и недостатки объектов и классов, а также их роль в видеоиграх.

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

Расширение : в вашем примере Trollи Werewolfбудет расширяться Enemy, что расширяет Entity. Entityзаботится об основных вещах, таких как движение, здоровье и т. д., в то Enemyвремя как подклассы определяют как врагов и делают другие вещи (Minecraft следует этому подходу).

Основанный на компонентах : Второй подход (и мой любимый) - один Entityкласс, у которого есть компоненты. Компонент может быть Moveableили Enemy. Эти компоненты заботятся об одной вещи, как движение или физика. Этот метод разрывает длинные цепочки расширений и сводит к минимуму риск возникновения конфликта. Например: как вы можете создавать неподвижных врагов, если они наследуются от подвижного персонажа? ,

В больших видеоиграх, таких как World of Warcraft или Starcraft 2, сущности - это не классы, а наборы данных, которые определяют всю сущность. Сценарии также важны, потому что вы определяете, что делает сущность не в классе, а в скрипте (если он уникален, а не в таких вещах, как перемещение).

В настоящее время я программирую RTS, и я использую компонентный подход с файлами дескрипторов и скриптов для Entites, Skills, Items и всего остального, динамического в игре.

Marco
источник
2
Звучит интересно. Однако я не буду лгать, я не знаю, что componentв этом контексте. Ссылка будет высоко ценится.
vemv
Компонент - это объект, который отвечает за единственную функциональность. Компонент на основе компонента будет владеть компонентом (если это так), который не будет наследоваться от него. Право собственности подразумевает, что субъект может деактивировать один из своих компонентов, изменяя свое поведение. В классической парадигме Расширения поведение обусловлено «принадлежностью» к суперклассу. Сущность может быть компонентом, а компонент может быть смоделирован как класс или структура, если вы предпочитаете.
FxIII
Компонент (иногда называемый Entity systems) - вы можете искать идеи в этом блоге. T-machine.org Существуют варианты, но основная идея состоит в том, что компонент является объектом специального назначения, и ваш субъект связывает воедино кучу компонентов, таких как построение модели. из Легоса.
Патрик Хьюз
2
Я начал блог о разработке игр и написал свою первую запись в блоге об этом. Ничего нового, но с небольшим количеством кода: jagamedev.wordpress.com/2011/06/26/…
Марко
23

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

К сожалению, такой подход к полиморфизму, популярный в 90-х годах, на практике оказался плохой идеей. Представьте, что вы добавили «волка» в список врагов - ну, он разделяет некоторые атрибуты с оборотнем, поэтому вы хотите объединить их в общий базовый класс, например. 'WolfLike. Теперь вы добавляете «Человека» в список врагов, но оборотни иногда являются людьми, поэтому они также имеют общие черты, такие как ходьба на двух ногах, способность говорить и т. Д. Вы создаете общую базу «Гуманоидов» для людей и оборотней? и нужно ли вам тогда останавливать оборотней, происходящих от WolfLike? Или вы умножаете наследование от обоих - в этом случае какой атрибут имеет приоритет, когда что-то в Humanoid конфликтует с чем-то в WolfLike?

Проблема в том, что попытка моделировать реальный мир как дерево классов неэффективна. Возьмите любые 3 вещи, и вы, вероятно, найдете 4 различных способа разделить их поведение на общие и не общие. Вот почему многие вместо этого обратились к модульной или основанной на компонентах модели, где объект состоит из нескольких меньших объектов, составляющих единое целое, и меньшие объекты можно поменять местами или изменить, чтобы создать желаемое поведение. Здесь у вас может быть только 1 класс Enemy, и он содержит подобъекты или компоненты для того, как он ходит (например, Bipedal против Quadrupedal), его методы атаки (например, укус, коготь, оружие, кулак), его кожа (зеленый для троллей, пушистых, для оборотней и волков) и т. д.

Это все еще полностью объектно-ориентированный, но понятие того, что является полезным объектом, отличается от того, что обычно преподавалось в учебниках. Вместо того, чтобы иметь большое дерево наследования различных абстрактных классов и несколько конкретных классов на кончиках дерева, у вас обычно есть только 1 конкретный класс, представляющий абстрактное понятие (например, «враг»), но который содержит более конкретные классы, представляющие более абстрактные понятия (например, приступы, тип кожи). Иногда эти вторые классы лучше всего реализовать через 1 уровень наследования (например, базовый класс Attack и несколько производных классов для конкретных атак), но глубокое дерево наследования пропало.

Но, может быть, занятия слишком тяжелы, чтобы быть практичными, я не знаю ...

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

Kylotan
источник
3
Люблю этот ответ :)
Czarek Tomczak
8

Я не уверен, что вы подразумеваете под «слишком тяжелым», но C ++ / OOP является языком разработки игр по тем же причинам, что и в других местах.

Разговор о взрывах кешей - это проблема дизайна, а не языковая особенность. C ++ не может быть слишком плохим, поскольку он использовался в играх последние 15-20 лет. Java не может быть слишком плохой, так как она используется в очень жестких условиях работы смартфонов.

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

Это все ООП и все еще используется в качестве основы при разработке нового программного обеспечения, просто другой фокус на том, что воплощают классы.

Патрик Хьюз
источник
2

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

DeadMG
источник
Диспетчеризация между различными полиморфными типами осуществляется таким образом, чтобы обеспечить плохую локальность памяти. Даже в самых легких реализациях, таких как C ++ vtables или Objective-C IMP-кэширование, если вы на самом деле используете полиморфизм для работы с объектами разных типов, вы будете взрывать свои кэши. Конечно, если вы не используете полиморфизм, тогда vtable остается в вашем кэше или сообщение в кэше IMP приводит к правильному месту, но тогда зачем беспокоиться? И в Java или C # стоимость выше, а в JavaScript или Python стоимость еще выше.
1
@Joe Wreschnig: Если вы используете эти более медленные языки, то стоимость ОО тривиальна по сравнению с другими затратами, такими как перевод / JIT. Что еще более важно, вы можете теоретически построить то, что вы хотите от плохой локальности памяти, но факт заключается в том, что прогнозирование ветвлений, выполнение не по порядку и аналогичные функции в ЦП практически полностью сводят на нет затраты. Иначе, почему так много высокопроизводительного программного обеспечения написано с использованием объектно-ориентированной ориентации?
DeadMG
1

Следует помнить одну вещь: концепции объектно-ориентированного кода часто более ценны, чем их реализация в языках. В частности, необходимость выполнения 1000-х вызовов виртуального обновления для объектов в основном цикле может привести к путанице в кеше в C ++ на платформах, таких как 360 или PS3 - поэтому реализация может избежать использования «виртуальных» или даже «классных» ключевых слов ради скорости.

Хотя часто он все равно будет следовать ОО-концепциям наследования и инкапсуляции - просто реализуя их иначе, чем сам язык (например, любопытно рекурсивные шаблоны, композиция вместо наследования (метод на основе компонентов, описанный Марко)). Если наследование всегда может быть разрешено во время компиляции, тогда проблемы с производительностью исчезают, но код может стать немного проблематичным с помощью шаблонов, пользовательских реализаций RTTI (опять же, встроенные обычно непрактично медленны) или логики времени компиляции в макросы и т. д.

В Objective-C для iOS / Mac есть похожие, но более серьезные проблемы со схемой обмена сообщениями (даже с модным кешированием) ...

jheriko
источник
0

Метод, который я буду пытаться использовать, смешивает наследование классов и компоненты. (Во-первых, я не делаю Enemy классом - я делаю это компонентом.) Мне не нравится идея использовать один универсальный класс Entity для всего, а затем добавлять необходимые компоненты, чтобы конкретизировать сущность. Вместо этого я предпочитаю, чтобы класс автоматически добавлял компоненты (и значения по умолчанию) в конструктор. Затем подклассы добавляют свои дополнительные компоненты в свой конструктор, чтобы у подкласса были свои компоненты и компоненты родительского класса. Например, класс Weapon добавляет базовые компоненты, общие для всех видов оружия, а затем подкласс Sword добавляет любые дополнительные компоненты, специфичные для мечей. Я до сих пор спорю о том, использовать ли активы text / xml для определения значений для сущностей (или конкретных мечей, например), или сделать все это в коде, или каков правильный микс. Это по-прежнему позволяет игре использовать информацию о типах и полиморфизме языка программирования и значительно упрощает ObjectFactories.

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