EDIT: Ниже я описываю базовую систему обмена сообщениями, которую я использовал снова и снова. И мне казалось, что оба школьных проекта имеют открытый исходный код и в Интернете. Вы можете найти вторую версию этой системы обмена сообщениями (и немного больше) на http://sourceforge.net/projects/bpfat/ .. Наслаждайтесь и читайте ниже для более подробного описания системы!
Я написал общую систему обмена сообщениями и представил ее в несколько игр, выпущенных на PSP, а также на прикладном программном обеспечении уровня предприятия. Точка системы обмена сообщениями должна передавать только данные, которые необходимы для обработки сообщения или события, в зависимости от терминологии, которую вы хотите использовать, чтобы объекты не знали друг о друге.
A быстренько из списка объектов, используемых для достижения этой цели является то, что вдоль линий:
struct TEventMessage
{
int _iMessageID;
}
class IEventMessagingSystem
{
Post(int iMessageId);
Post(int iMessageId, float fData);
Post(int iMessageId, int iData);
// ...
Post(TMessageEvent * pMessage);
Post(int iMessageId, void * pData);
}
typedef float(*IEventMessagingSystem::Callback)(TEventMessage * pMessage);
class CEventMessagingSystem
{
Init ();
DNit ();
Exec (float fElapsedTime);
Post (TEventMessage * oMessage);
Register (int iMessageId, IEventMessagingSystem* pObject, FObjectCallback* fpMethod);
Unregister (int iMessageId, IEventMessagingSystem* pObject, FObjectCallback * fpMethod);
}
#define MSG_Startup (1)
#define MSG_Shutdown (2)
#define MSG_PlaySound (3)
#define MSG_HandlePlayerInput (4)
#define MSG_NetworkMessage (5)
#define MSG_PlayerDied (6)
#define MSG_BeginCombat (7)
#define MSG_EndCombat (8)
А теперь немного объяснений. Первый объект, TEventMessage, является базовым объектом для представления данных, отправленных системой обмена сообщениями. По умолчанию он всегда будет иметь идентификатор отправляемого сообщения, поэтому, если вы хотите удостовериться, что получили сообщение, которое ожидаете, вы можете (обычно я делаю это только при отладке).
Следующий класс - это интерфейс, который дает общий объект для системы обмена сообщениями, используемой для каста при выполнении обратных вызовов. Кроме того, это также обеспечивает «простой в использовании» интерфейс для Post() для разных типов данных для системы обмена сообщениями.
После этого у нас есть наш callback typedef, просто предположим, что объект типа класса интерфейса будет проходить вдоль указателя TEventMessage ... При желании вы можете сделать параметр const, но Ive использовал обработку подкачки, прежде чем такие вещи, как отладка стека и такая система обмена сообщениями.
Последнее, а в основе лежит объект CEventMessagingSystem. Этот объект содержит массив стеков объектов обратного вызова (или связанных списков или очередей или, тем не менее, вы хотите хранить данные). Объекты обратного вызова, не показанные выше, должны поддерживать (и однозначно определяться) указателем на объект, а также метод вызова этого объекта. Когда вы Регистрируете(), вы добавляете запись в стек объекта под позицией массива идентификатора сообщения. Когда вы отмените регистрацию(), вы удалите эту запись.
Это в основном это .. Теперь у этого есть условие, что все должно знать об IEventMessagingSystem и объекте TEventMessage ...но этот объект не должен меняться, что часто и только передает части информации, которые жизненно важны для логики, продиктованной вызываемым событием. Таким образом, игроку не нужно знать о карте или враге непосредственно для отправки событий на него. Управляемый объект может также вызвать API для более крупной системы, не требуя ничего знать об этом.
Например: Когда враг умирает, вы хотите, чтобы он воспроизводил звуковой эффект. Предполагая, что у вас есть диспетчер звуков, который наследует интерфейс IEventMessagingSystem, вы должны настроить обратную связь для системы обмена сообщениями, которая будет принимать TEventMessagePlaySoundEffect или что-то подобное. Затем Sound Manager зарегистрирует этот обратный вызов, когда звуковые эффекты будут включены (или отмените регистрацию, когда вы хотите отключить все звуковые эффекты для удобства включения/выключения). Затем вы также должны наследовать объект-противник из IEventMessagingSystem, объединив объект TEventMessagePlaySoundEffect (для его идентификатора сообщения потребуется MSG_PlaySound, а затем идентификатор звукового эффекта для воспроизведения, будь то int ID или имя звука эффект) и просто вызвать сообщение (& oEventMessagePlaySoundEffect).
Теперь это просто простой дизайн без реализации. Если у вас есть немедленное выполнение, вам не нужно буферизовать объекты TEventMessage (что я использовал в основном в консольных играх). Если вы находитесь в многопоточной среде, то это очень четко определенный способ для объектов и систем, работающих в отдельных потоках, разговаривать друг с другом, но вы захотите сохранить объекты TEventMessage, чтобы данные были доступны при обработке.
Другие изменения для объектов, которые когда-либо нужны только для данных Post(), вы можете создать статический набор методов в IEventMessagingSystem, чтобы им не было наследовать от них (это используется для удобства доступа и возможностей обратного вызова , непрямо- необходимый для вызовов Post()).
Для всех людей, которые упоминают MVC, это очень хороший образец, но вы можете реализовать его так много разных манер и на разных уровнях. Текущий проект, над которым я работаю профессионально, - это установка MVC примерно в 3 раза, есть глобальный MVC всего приложения, а затем дизайн мудрый каждый MV и C также является самодостаточным шаблоном MVC. Так что я пытался сделать здесь объясняется, как сделать C, который является достаточно общим для обработки практически любого типа M без необходимости входить в представление ...
Например, объект, когда он «умирает», может захотеть воспроизвести звуковой эффект. Вы создали бы структуру для звуковой системы, например TEventMessageSoundEffect, которая наследуется от TEventMessage и добавляет в идентификатор звукового эффекта (будь то предварительно загруженный Int или имя файла sfx, однако они отслеживаются в вашей системе). Затем всему объекту просто нужно собрать объект TEventMessageSoundEffect с соответствующим шумом смерти и вызвать сообщение (& oEventMessageSoundEffect); объект .. Предполагая, что звук не отключен (то, что вы хотели бы отменить регистрацию менеджеров звука.
EDIT: Прояснить это немного относительно комментария ниже: Любой объект для отправки или получения сообщения просто необходим знать об интерфейсе IEventMessagingSystem, и это единственный объект, который EventMessagingSystem должен знать обо всех других объектах. Это то, что дает вам отрыв. Любой объект, который хочет получить сообщение, просто зарегистрируется (MSG, Object, Callback) s для Затем, когда объект вызывает Post (MSG, Data), он отправляет это в EventMessagingSystem через интерфейс, о котором он знает, затем EMS уведомляет каждый зарегистрированный объект события. Вы можете сделать MSG_PlayerDied, который обрабатывают другие системы, или плеер может вызывать MSG_PlaySound, MSG_Respawn и т. д., чтобы позволить вещам слушать эти сообщения, чтобы воздействовать на них. Post (MSG, Data) в качестве абстрактного API для разных систем в игровом движке.
Oh! Еще одна вещь, которая была указана мне. Система, описанная выше, соответствует шаблону Observer в другом ответе. Поэтому, если вы хотите, чтобы более общее описание сделало мой способ более понятным, это короткая статья, которая дает хорошее описание.
Надеюсь, что это поможет и понравится!
+1 за подробное объяснение, но у меня также есть замечание: вы заявили, что * игроку не нужно знать о карте *, чтобы отправлять ему события, но ваш пример означает, что умирающий враг должен знать обо всех других часть программы, которая должна быть уведомлена. Я ожидал, что он просто отправит сообщение «Я только что умер», а затем пусть ваша система обмена сообщениями сообщит слушателям, которые интересуются этим событием (воспроизведение звука, оценка обновления и т. Д.). Таким образом, похоже, что любой объект должен отправить кучу сообщений для одного события (воспроизведение звука, увеличение балла). Или я ошибаюсь? – Groo
@Groo Мне не удалось сократить свой ответ, поэтому я отредактировал его в своем ответе выше. – James
Привет, человек, прошло более 5 лет с момента вашего ответа, но этот пост появился, когда я искал простую идею pubsub, и я должен сказать, что я загрузил источники, и кроме стандартов кодирования i ' m не используется и тот факт, что C++ немного продвинулся с 2005 года, код очень интересен для исследования, и я использовал некоторый скелет EMS для моей игры на C#. Это действительно потрясающе и сложно, что вы, три парня, сделали, и я надеюсь, что я узнаю больше от него! –