Что я должен учитывать при разработке системы Event Manager?

9

Я разбираюсь с основами игрового движка Java и достиг той точки, когда я готов добавить систему Event Manager.

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

Я не смог ничего найти в Интернете о внедрении системы событий с нуля, поэтому я ищу информацию о том, каковы лучшие практики в этом случае - что я должен и не должен делать.

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

Полагаю, мне нужно было бы поддерживать какую-то коллекцию для каждого возможного подписанного события - добавлять и удалять игровые объекты из списка по мере необходимости. Я думаю, что должна быть возможность создать очередь событий, которые должны быть переданы, и в этом случае я мог бы просто добавить EventManager.Update () в основной цикл игры, и чтобы Update()метод транслировал события, которые произошли в конце каждого кадра. Наконец, у каждого объекта должен быть HandleEvent(Event e)метод, который они могли бы затем проанализировать и ответить соответствующим образом.


Похоже ли это на правильное направление реализации такой системы, или я не в курсе и / или упускаю что-то совершенно очевидное?

Ворон Мечтатель
источник
2
gamedev.stackexchange.com/questions/7718/… Это Q / AI, переданный ранее, который может помочь вам начать.
Джеймс
@ Джеймс Ах - похоже на хорошую ссылку. Спасибо! Я думаю, что я пропустил это раньше.
Raven Dreamer
4
Некоторые дополнительные советы: решите, должны ли некоторые события вступать в силу немедленно, а не в конце кадра, особенно если обработчик событий вызывает дополнительные события; подумайте, что происходит, когда вы получаете циклы событий; если вы идете с очередью, возможно, вам понадобится система приоритетов или способ обойти очередь.
Сэм Хоцевар

Ответы:

6

Это может быть так просто, как вы хотите.

for each object in the subscriber list:
    object.notify(this event) // (or 'HandleEvent' if you prefer)

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

Kylotan
источник
3
+1 за «Не пытайся понять, что должен делать Х» - выясни, что тебе нужно ». На самом деле, не добавляйте менеджер событий вообще, пока не почувствуете, что вам нужен какой-то разъединенный, но централизованный способ вызова функций.
@JoeWreschnig О, боже, да =) "Пойми, что тебе нужно сделать". Это один из трех главных советов, которые любой разработчик должен принять близко к сердцу.
Патрик Хьюз
Сначала я хотел не согласиться с вашим вторым предложением о том, чтобы не добавлять менеджер событий, потому что большинство игр могут извлечь выгоду из разрозненного, но все же централизованного способа вызова функций, но потом я подумал, сколько маленьких игр, которые я сделал, не есть какой-либо менеджер событий. так что, да
джоккинг
3

Три основных метода, которые необходимы системе событий, - это метод addListener (), метод removeListener () и метод dispatchEvent (). То есть объекты метода используют для регистрации события, объекты метода используют для отмены регистрации и метод для фактической трансляции события всем слушателям. Все остальное - соус.

Как вы заметили, вам, безусловно, понадобится какая-то структура данных для отслеживания зарегистрированных слушателей. Самый простой подход - это ассоциативный массив (словарь или объект в JavaScript), который связывает вектор (или просто массив в зависимости от языка) с событием. Этот вектор является списком всех зарегистрированных слушателей; добавить слушателей в список или удалить их в методах add / removeListener ().

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

В dispatchEvent () есть небольшой нюанс, когда вы рассматриваете, какие данные передать вместе с событием. На самом базовом уровне вы можете не передавать никаких дополнительных данных; Все, что слушатель должен знать, - это то, что произошло событие. Однако большинство событий имеют дополнительные данные, которые идут вместе с ними (например, где произошло событие), и вы захотите, чтобы dispatchEvent () принимал и передавал некоторые параметры.

Например, действительно ли необходимо, чтобы у каждого из моих GameObjects было поле «EventManagner»? Поскольку все мои объекты GameObject наследуются от одного абстрактного родительского класса, я думаю, что я должен иметь возможность использовать статическую ссылку, чтобы существовал только один экземпляр EventManager, общий для всех игровых объектов.

Есть много способов дать всем вашим игровым объектам ссылку на класс EventManager. Статическая ссылка, безусловно, является одним из способов; другой синглтон. Однако оба эти подхода довольно негибкие, поэтому большинство людей здесь рекомендуют либо указатель службы, либо внедрение зависимости. Я делал инъекцию зависимости; это означает отдельное поле EventManager для каждого объекта, чего вы, похоже, хотите избежать, но я не уверен, почему это проблема. Это не значит, что хранение множества указателей слишком дорого.

jhocking
источник
да, я впервые понял и внедрил внедрение зависимостей для объекта EventDispatcher. Как только я получил это за пояс, мне не терпелось много чего сделать с этим паттерном.
Джоккинг
0

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ

Я не программист на Java, но я написал статью, объясняющую, как создать систему событий в C89, одном из самых простых языков (IMHO), посмотрите его

https://prdeving.wordpress.com/2017/04/03/event-driven-programming-with-c-89/

Основы обработки событий довольно просты.

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

Зная это (но не зная, как реализовать это в Java), легко понять, что вам нужны некоторые обработчики (функции для обработки событий) и добавить их в массив (с указателями в C), связанный с именем события. Как только вы запускаете событие, вы сохраняете имя сработавшего события и его аргументы в стеке, это называется пулом событий.

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

Конечно, этот дайджест события может быть запущен в любое время, например, один раз за кадр после вызова вашей Update()функции.

PRDeving
источник
-2

Для поля EventManager используйте статическую переменную. Синглтон для EventManager тоже будет отличной идеей.

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

Хорошо выполнять IO-события до или после «GameEvent», поэтому в одном кадре каждый «GameEvent» обрабатывает одни и те же данные.

Sibbo
источник
"Thread-сохранить"? Хммм
U62
-1 за совершенно ненужное и неоправданное однозначное предложение.