Я делаю простую игру и решил попробовать внедрить систему обмена сообщениями.
Система в основном выглядит так:
Объект генерирует сообщение -> сообщение отправляется в глобальную очередь сообщений -> messageManager уведомляет каждый объект о новом сообщении через onMessageReceived (Message msg) -> если объект хочет, он воздействует на сообщение.
То, как я создаю объекты сообщений, выглядит так:
//base message class, never actually instantiated
abstract class Message{
Entity sender;
}
PlayerDiedMessage extends Message{
int livesLeft;
}
Теперь мой SoundManagerEntity может сделать что-то подобное в своем методе onMessageReceived ()
public void messageReceived(Message msg){
if(msg instanceof PlayerDiedMessage){
PlayerDiedMessage diedMessage = (PlayerDiedMessage) msg;
if(diedMessage.livesLeft == 0)
playSound(SOUND_DEATH);
}
}
Плюсы к этому подходу:
- Очень просто и легко реализовать
- Сообщение может содержать столько информации, сколько вы хотите, потому что вы можете просто создать новый подкласс Message, в котором есть вся необходимая информация.
Минусы:
- Я не могу понять, как я могу переместить объекты Message в пул объектов , если у меня нет другого пула для каждого подкласса Message. Таким образом, у меня есть много-много объектов создания / выделения памяти с течением времени.
- Не могу отправить сообщение конкретному получателю, но мне это еще не нужно в моей игре, поэтому я не против этого.
Что мне здесь не хватает? Должна быть лучшая реализация или какая-то идея, которую мне не хватает.
Ответы:
Простой ответ: вы не хотите перерабатывать сообщения. Каждый перерабатывает объекты, которые либо создаются (и удаляются) тысячами в каждом кадре, например частицы, либо очень тяжелые (десятки свойств, длительный процесс инициализации).
Если у вас есть частица снега с каким-то растровым изображением, скоростями, физическими атрибутами, которые просто спустились ниже вашего вида, вы можете захотеть переработать ее вместо создания новой частицы и повторной инициализации растрового изображения и рандомизации атрибутов. В вашем примере нет ничего полезного в Message, чтобы сохранить его, и вы потратите больше ресурсов на удаление его специфичных для экземпляра свойств, чем просто на создание нового объекта Message.
источник
В моей Java-игре я придумал очень похожее решение, за исключением того, что мой код обработки сообщений выглядит следующим образом:
Я использую аннотации, чтобы пометить метод как обработчик событий. Затем, в начале игры, я использую отражение, чтобы вычислить отображение (или, как я это называю, «трубопровод») сообщений - какой метод вызывать для какого объекта при отправке события определенного класса. Это работает ... потрясающе.
Расчет трубопроводов может быть немного сложным, если вы хотите добавить интерфейсы и подклассы к миксу, но, тем не менее, это довольно просто. Во время загрузки игры происходит небольшое замедление, когда все классы нужно сканировать, но это делается только один раз (и, вероятно, может быть перемещено в отдельный поток). Вместо этого получается, что фактическая отправка сообщений обходится дешевле - мне не нужно вызывать каждый метод «messageReceived» в кодовой базе, просто чтобы он проверил, является ли событие хорошим классом.
источник
РЕДАКТИРОВАТЬ Ответ Лиосан является более сексуальным. Посмотри на это.
Как сказал Маркус, сообщения не являются обычным кандидатом в пулы объектов. Не беспокойтесь по причинам, которые он упомянул. Если ваша игра отправляет тонны сообщений определенных типов, то, возможно, это того стоит. Но тогда я бы предложил вам переключиться на прямые вызовы методов.
Говоря о которых,
Если вы знаете конкретного получателя, которому хотите отправить его, не будет ли довольно просто получить ссылку на этот объект и выполнить прямой вызов метода? Вы также можете скрыть определенные объекты за классами менеджера для более легкого доступа через системы.
В вашей реализации есть один недостаток, и есть вероятность того, что множество объектов получат сообщения, которые им не нужны. В идеале ваши классы будут получать только те сообщения, которые им действительно нужны.
Вы можете использовать
HashMap<Class, LinkedList<Messagable>>
для связывания типов сообщений со списком объектов, которые хотят получать сообщения этого типа.Подписка на тип сообщения будет выглядеть примерно так:
Вы могли бы реализовать это
subscribe
так (прости меня за некоторые ошибки, я не писал Java в течение нескольких лет):И тогда вы могли бы передать сообщение, как это:
И
broadcastMessage
может быть реализовано так:Вы также можете подписаться на сообщение таким образом, чтобы разделить обработку сообщений на различные анонимные функции:
Это немного многословно, но помогает с организацией. Вы также можете реализовать вторую функцию,
subscribeAll
которая будет хранить отдельный список,Messagable
который хочет услышать обо всем.источник
1 . Не.
Сообщения стоят недорого. Я согласен с Маркусом фон Броади. GC будет собирать их быстро. Старайтесь не прикреплять много дополнительной информации к сообщениям. Это просто сообщения. Поиск экземпляра - это информация сама по себе. Используйте это (как вы сделали в своем примере).
2 . Вы можете попробовать использовать MessageDispatcher .
В отличие от первого решения вы могли бы иметь централизованный диспетчер сообщений, который обрабатывает сообщения и передает их намеченным объектам.
источник