Событийное общение в игровом движке: да или нет?

62

Я читаю Game Coding Complete, и автор рекомендует Event-Driven-коммуникацию между игровыми объектами и модулями.

По сути, все живые игровые актеры должны общаться с ключевыми модулями (физика, искусственный интеллект, игровая логика, игровой вид и т. Д.) Через внутреннюю систему обмена сообщениями о событиях. Это означает необходимость разработки эффективного менеджера событий. Плохо спроектированная система пожирает циклы процессора, особенно это касается мобильных платформ.

Это проверенный и рекомендуемый подход? Как я должен решить, использовать ли это?

Bunkai.Satori
источник
Привет Джеймс, спасибо за ваш ответ. Мне это нравится, хотя книга описывает Event-Driven-коммуникацию как действительно сложную систему, которая, по-видимому, потребляет значительно больше ресурсов процессора.
Bunkai.Satori
Поместите его в ответ и добавьте в ссылку на общий обзор системы сообщений о событиях, которую я дал в ответ на переполнение стека. Наслаждайтесь! Просто помните, что то, что некоторые люди считают сложным, не должно быть :)
Джеймс
Вы также можете проверить этот ответ, используя c ++ 11: stackoverflow.com/a/22703242/2929762
user2826084
libuvнедавно стала успешной библиотекой событий. (Это в C. Node.js - его самый известный вариант использования.)
Anko

Ответы:

46

Это расширение моего комментария к полному ответу, как предложено.

Да , просто и понятно. Коммуникация должна происходить, и в то время как есть ситуации, когда «Мы уже там?» опрос типа требуется, чтобы вещи проверяли, должны ли они делать что-то еще, что обычно тратит время. Вместо этого вы могли бы заставить их реагировать на то, что им велено делать. Кроме того, четко определенный путь связи между объектами / системами / модулями значительно ускоряет параллельные установки.

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

РЕДАКТИРОВАТЬ : Чтобы ответить на вопрос комментария о том, как вы знаете, какие объекты должны получить сообщение : сами объекты должны Requestбыть уведомлены о событиях. Ваш EventMessagingSystem(EMS) будет нуждаться в Register(int iEventId, IEventMessagingSystem * pOjbect, (EMSCallback)fpMethodToUseForACallback)сопоставлении Unregister(создание уникальной записи для iEventIdуказателя объекта и обратного вызова). Таким образом, когда объект хочет узнать о сообщении, он может это сделать Register()с системой. Когда ему больше не нужно знать о событиях, он можетUnregister(), Очевидно, что вам нужен пул этих объектов регистрации обратного вызова и эффективный способ добавления / удаления их из списков. (Я обычно использовал самоупорядоченные массивы; причудливый способ сказать, что они отслеживают свои собственные распределения между стеком пула неиспользуемых объектов и массивами, которые смещают свои размеры при необходимости).

РЕДАКТИРОВАТЬ : Для полностью работающей игры с циклом обновления и системой обмена сообщениями о событиях, вы можете проверить мой старый школьный проект . Связанный выше пост переполнения стека также относится к нему.

Джеймс
источник
Здравствуйте, Джеймс, поскольку я больше думаю о внутренней системе обмена сообщениями о событиях, я думаю, что не нужно увеличивать стоимость движка. Например, если на экране 50 объектов, но только 5 из них должны изменить свое поведение; при традиционной системе все 50 объектов должны были бы проверить все свои возможные действия, чтобы увидеть, должны ли они что-то делать. Однако с использованием сообщений о событиях, только 5 сообщений будут отправлены этим 5 объектам с определенным изменением действия. Это похоже на экономию времени.
Bunkai.Satori
Система, на которую ссылается мой ответ, работает так, что объекты регистрируются только для того, чтобы слышать о сообщениях, которые они хотят. Мы будем использовать это в наших интересах в видеоиграх, где может быть 100-200 объектов на уровне, но вы только «активируйте» те, с которыми игрок может напрямую взаимодействовать, сохраняя количество прослушиваемых вещей до 10 или около того. В целом, этот тип системы должен быть «умнее», чем «мы уже там?» система опроса и должна уменьшить нагрузку на двигатель, по крайней мере, в том, что касается связи.
Джеймс
Есть одна вещь, связанная с системой, управляемой событиями, которая смущает меня: в традиционной системе, где каждый объект имеет предварительно определенный набор методов (OnUpdate (), OnMove (), OnAI (), OnCollisionCheck () ...), каждый объект отвечает за проверку и управление своим состоянием. В системе, управляемой событиями, должно быть некоторое условие проверки главной системы каждого объекта, а затем отправка сообщений тем, где какое-то событие было обнаружено. Для меня это стандартизирует возможные состояния объекта и ограничивает свободу создания уникального поведения объекта. Это правда?
Bunkai.Satori
Шаблон Observer является типичной альтернативой опросу. ru.wikipedia.org/wiki/Observer_pattern
Рикет
1
@ hamlin11 Рад, что эта информация все еще помогает людям :) Что касается регистрации, если это случится совсем немного, помните, что вы захотите оптимизировать ее для скорости, иметь пул объектов регистрации, из которых можно извлечь, и т. д.
Джеймс
22

Мое мнение таково, что вы должны просто начать создавать свою игру и реализовать то, что вам удобно. Когда вы сделаете это, вы обнаружите, что используете MVC в некоторых местах, но не в других; события одних мест, но не других; компоненты одних мест, но наследование других; чистый дизайн одних мест и грубый дизайн одних.

И это нормально, потому что, когда вы закончите, у вас действительно будет игра, и игра намного круче, чем система передачи сообщений.

user744
источник
2
Привет, Джо, спасибо за ответ, мне это нравится. Вы верите, что нет необходимости искусственно выдвигать какой-либо подход. Я должен разрабатывать приложения так, как я думаю, будет работать, и если что-то не получится позже, я просто переделаю это.
Bunkai.Satori
Вот почему система существует. Многие люди сделали то, что вы сказали, стали свидетелями кошмара и сказали, что должен быть лучший путь. Вы можете либо повторять свои ошибки, либо учиться на них и, возможно, продвигать их дальше.
user441521
16

Лучший вопрос, какие есть альтернативы? В такой сложной системе с правильно разделенными модулями для физики, ИИ и т. Д. Как еще вы можете организовать эти системы?

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

Сообщения также обычно используются в том же смысле, что и средство межпроцессного взаимодействия; другой распространенный метод - это потоки или каналы, в которых вместо этого данные отправляются в виде последовательности элементарных элементов данных (версия виртуального канала более высокого уровня).

(Межпроцессное взаимодействие - это взаимодействие между процессами (т.е. запущенными экземплярами программ) в среде операционной системы)

Так что, если он достаточно хорош для операционной системы, то, вероятно, достаточно хорош для игры, верно? Есть и другие преимущества, но я позволю статье объяснять в статье в Википедии о передаче сообщений .

Я также задал вопрос о переполнении стека: «Структуры данных для передачи сообщений в программе?» , который вы можете прочитать.

Ricket
источник
Привет Рикет, спасибо за твой ответ. Вы спросили, какие еще способы можно использовать, чтобы игровые объекты могли общаться друг с другом. Что приходит мне в голову - это прямые вызовы методов. Основным является то, что он не дает вам такой гибкости, но, с другой стороны, он избегает генерации сообщений о событиях, их передачи, чтения и т. Д. Мы по-прежнему говорим о мобильных платформах, а не о высококачественных играх. Операционная система активно использует внутреннюю систему обмена сообщениями. Однако он не предназначен для работы в режиме реального времени, когда даже небольшие задержки вызывают сбои.
Bunkai.Satori
Позвольте мне оставить этот вопрос открытым некоторое время. Я хотел бы собрать больше мнений, прежде чем закрыть его.
Bunkai.Satori
Прямые вызовы методов обычно приводят к соединению классов, которые вызывают друг друга. Это не хорошо для системы, разделенной на компоненты, которые должны быть взаимозаменяемыми. Существуют некоторые шаблоны, которые могут облегчить прямые вызовы методов с меньшей связностью (например, часть «контроллер» MVC в основном служит этой цели для облегчения запросов представления модели, если вы не соедините их), но в целом передача сообщений является единственным способом для система, имеющая нулевую связь между подсистемами (или, по крайней мере, единственный известный мне способ).
Рикет
Ах, теперь мы начали обсуждать закономерности. Я немного слышал об этом подходе программирования. Теперь я начинаю понимать, что такое шаблоны. Это совершенно другой взгляд на дизайн приложения. Вы контролируете объекты, используя один и тот же код для проверки каждого объекта. После проверки объекта вы устанавливаете его атрибуты соответственно.
Bunkai.Satori
1
На вопрос «Структуры данных ...» есть УДИВИТЕЛЬНЫЙ ответ. Я бы сказал, что выбранный вами ответ и сопровождающая статья Nebula3 являются лучшим описанием архитектуры игрового движка такого размера, который я когда-либо видел.
deft_code
15

Сообщения обычно работают хорошо, когда:

  1. Отправителю сообщения не важно, получено ли оно.
  2. Отправителю не нужно получать немедленный ответ от получателя.
  3. Может быть несколько получателей, слушающих одного отправителя.
  4. Сообщения будут отправляться нечасто или непредсказуемо. (Другими словами, если каждый объект должен получать сообщение «update» в каждом отдельном кадре, сообщения на самом деле не очень-то вас покупают.)

Чем больше ваши потребности соответствуют этому списку, тем лучше будут подходящие сообщения. На очень общем уровне они довольно хороши. Они делают хорошую работу, не тратя циклы ЦП просто для того, чтобы ничего не делать в отличие от опроса или других более глобальных решений. Они отлично подходят для отделения частей кодовой базы, что всегда полезно.

необычайно щедрый
источник
4

Отличные ответы от Джеймса и Рикета, я отвечаю только, чтобы добавить предостережение. Передача сообщений / коммуникация, управляемая событиями, безусловно, является важным инструментом в арсенале разработчика, но его можно легко использовать слишком много. Когда у тебя молоток, все выглядит как гвоздь и так далее.

Вы абсолютно на 100% должны думать о потоке данных вокруг вашего заголовка и полностью осознавать, как информация передается из одной подсистемы в другую. В некоторых случаях передача сообщений явно лучше; в других может быть более целесообразно, чтобы одна подсистема работала над списком совместно используемых объектов, позволяя другим подсистемам также работать с тем же общим списком; и многие другие методы.

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

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

MrCranky
источник
+1 за хорошую дополнительную информацию. Привет, мистер Крэнки, спасибо. В соответствии с тем, что вы говорите, стоит рассмотреть систему событийных сообщений даже для мобильных игр. Тем не менее, планирование не следует упускать из виду, и следует очень хорошо продумать, где будет использоваться система обмена сообщениями.
Bunkai.Satori
4

Ну, я знаю, что этот пост довольно старый, но я не удержался.

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

Двигатель, безусловно, следует традиционному подходу. Существует основной цикл обновления, который вызывает функцию обновления для всех объектов. О столкновениях напрямую сообщается обратным вызовом на объектах. Связь между объектами осуществляется с использованием интеллектуальных указателей, которыми обмениваются объекты.

Существует примитивная система сообщений, которая обрабатывает сообщения только небольшой группы объектов. Эти сообщения предпочтительно обрабатывать в конце игрового взаимодействия (например, создание или уничтожение сущности), поскольку они могут связываться со списком обновлений. Таким образом, в конце каждого игрового цикла используется небольшой список сообщений.

Несмотря на примитивную систему сообщений, я бы сказал, что система в значительной степени основана на цикле обновления.

Что ж. После использования этой системы, я думаю, что это очень просто, быстро и хорошо организовано. Логика игры является видимой и самодостаточной внутри сущностей, а не динамической, как сообщение quewe. Я действительно не хотел бы, чтобы это было обусловлено событиями, потому что, по моему мнению, системы событий вносят ненужную сложность в игровую логику и делают код игры очень сложным для понимания и отладки.

Но я также думаю, что у чистой системы, основанной на цикле обновления, как у меня, тоже есть некоторые проблемы.

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

Итак, в моем следующем игровом движке я собираюсь принять другой подход. Объекты будут регистрироваться для операций двигателя, таких как обновление, рисование, обнаружение столкновений и так далее. Каждое из этих событий будет иметь отдельные списки интерфейсов сущностей для реальных сущностей.

Артур Корреа
источник
Вы обнаружите, что сущности становятся базами кодов монстров, с которыми трудно справиться при любой сложности в игре. Вы в конечном итоге соедините их вместе, и это будет отстой, чтобы добавить или изменить функции. Разбивая ваши функции на небольшие компоненты, вам будет проще поддерживать и добавлять функции. Тогда возникает вопрос, как эти компоненты взаимодействуют? Ответ - события от одного компонента, вызывающие функции другого при передаче примитивных данных вместе в аргументах.
user441521
3

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

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

Еще одно преимущество: у вас может быть несколько подсистем, прослушивающих одно и то же событие. Например, все ваши удаленные представления (игроки) могут автоматически подписаться на событие создания сущностей и порождать сущности на каждом клиенте, не занимая при этом много работы. Представьте, сколько еще работы было бы, если бы вы не использовали события: вам нужно было бы совершать Update()вызовы где-нибудь или, возможно, вызывать view->CreateEntityиз логики игры (где знание о представлении и какая информация ему требуется, не принадлежит). Трудно решить эту проблему без событий.

С событиями вы получаете элегантное и отделенное решение, которое поддерживает бесконечное количество объектов неограниченного типа, которые могут просто подписаться на события и делать свое дело, когда что-то происходит в вашей игре. Вот почему события великолепны.

Более подробная информация и реализация здесь .

user2826084
источник
Замечательные моменты, хотя и односторонние. Я всегда с подозрением отношусь к общности: есть ли случаи против использования событий?
Анко
1
Очки против использования: когда вы абсолютно должны иметь прямой ответ. Когда все, что вы хотите сделать для какого-либо события, всегда выполняется напрямую и в одном и том же потоке. Когда нет абсолютно никакой внешней задержки, которая может задержать завершение звонков. Когда у вас есть только линейные зависимости. Когда вы редко делаете несколько вещей одновременно. Если вы посмотрите на node.js, все вызовы io основаны на событиях. Node.js - это место, где события реализованы на 100% правильно.
user2826084
Система событий не должна быть асинхронной. Вы можете использовать сопрограммы для имитации асинхронности, получая при этом синхронизацию, когда вам это нужно.
user441521
2

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

  • Игровые движки работают с большими объемами данных.
  • Игры должны быть быстрыми (минимум 20-30 FPS).
  • Часто необходимо знать, было ли что-то сделано или когда это будет сделано.

Таким образом, при использовании подхода общего разработчика игр «он должен быть максимально эффективным», такая система обмена сообщениями встречается не так часто.

Тем не менее, я согласен, что вы должны просто пойти дальше и попробовать. Такая система, безусловно, имеет множество преимуществ, и сегодня вычислительные мощности доступны дешево.

mrbinary
источник
Я не знаю, согласен ли я с этим. Альтернативой является опрос, который является расточительным. Реакция только на события является наиболее эффективной. Да, все еще будут некоторые опросы, которые будут происходить и вызывать события, но есть множество вещей, которые также ориентированы на события.
user441521
Я тоже не согласен с этим. Количество игровых движков для данных в значительной степени не имеет значения, так как обмен сообщениями о событиях предназначен для взаимодействия подсистем и подсистем. Игровые объекты не должны напрямую взаимодействовать с другими игровыми объектами (хотя пользовательские обработчики событий в объектах могут быть исключением). Из-за этого относительно небольшого количества подсистем и областей их «интереса» затраты производительности хорошо спроектированной магистрали обмена сообщениями в многопоточном движке незначительны.
Ян Янг