Game Programming Patterns Event Queue From the book by Robert Nystrom .

10
Game Programming Patterns Event Queue From the book by Robert Nystrom http://gameprogrammingpatterns.com

Transcript of Game Programming Patterns Event Queue From the book by Robert Nystrom .

Page 1: Game Programming Patterns Event Queue From the book by Robert Nystrom .

Game Programming PatternsEvent Queue

From the book byRobert Nystrom

http://gameprogrammingpatterns.com

Page 2: Game Programming Patterns Event Queue From the book by Robert Nystrom .

Event Queue pattern

• The problem: how do game systems communicate, and yet stay decoupled?

• The pattern: A queue stores a series of notifications or requests in first-in, first-out order. The request processor later processes items from the queue.

Page 3: Game Programming Patterns Event Queue From the book by Robert Nystrom .

The Event Loop

• Operating Systems and GUIs use events.• We’ve seen mouse events, keyboard events,

window closing events, etc.• The events have been put into a queue by the OS.

while (running) { Event event = getNextEvent(); // Handle event... }

Page 4: Game Programming Patterns Event Queue From the book by Robert Nystrom .

An example

• Your game has a tutorial system to display help boxes after specific in-game events.

• The first time the player kills a monster, you want to show a “Press X to grab the loot!” message.

• But you don’t want the tutorial code mixed into the already complex gameplay code.

• So, combat code can add an “enemy died” event every time a monster is slayed.

• The tutorial code listens for “enemy died” events.

Page 5: Game Programming Patterns Event Queue From the book by Robert Nystrom .

Another example - sound

class Audio { public: static void playSound(SoundId id, int volume); };

void Audio::playSound(SoundId id, int volume) { ResourceId resource = loadSound(id); int channel = findOpenChannel(); if (channel == -1) return; startSound(resource, channel, volume); }

Page 6: Game Programming Patterns Event Queue From the book by Robert Nystrom .

Suppose we use playSound like this, to play a bloop when the selected menu item changes:

class Menu { public: void onSelect(int index) { Audio::playSound(SOUND_BLOOP, VOL_MAX); // Other stuff... } };

Problem 1: The API blocks the caller until the audio engine has completely processed the request.

playSound is synchronous – it doesn’t return back to the caller until bloops are coming out of the speaker.

Page 7: Game Programming Patterns Event Queue From the book by Robert Nystrom .

In the AI system, we use playSound() to let out a wail of anguish when an enemy takes damage from the player.

But sometimes the mighty player hits two or more enemies at exactly the same time. Double playSound()s may be too loud, or jarring.

Problem 2: Requests cannot be processed in aggregate.

Hey – we’re running on a multicore processor. Audio should run in a separate thread.

Problem 3: Requests are processed in the wrong thread. (The same one as the caller.)

Page 8: Game Programming Patterns Event Queue From the book by Robert Nystrom .

Solution: decouple making a request for sound and processing the request.

Problem 1: The API blocks the caller until the audio engine has completely processed the request.

Let’s make playSound() defer the work and return almost immediately.void Audio::playSound(SoundId id, int volume) { pending_[numPending_] = {id, volume}; numPending_++; }

And add to the Audio class’s private area: static PlayMessage pending_[MAX_PENDING]; static int numPending_;

And define the struct PlayMessage:struct PlayMessage { SoundId id; int volume; };

The event queue!

Page 9: Game Programming Patterns Event Queue From the book by Robert Nystrom .

Of course, the sound still has to be played somewhere – in an update() method:

class Audio { public: static void update() { for (int i = 0; i < numPending_; i++) { ResourceId resource = loadSound(pending_[i].id); int channel = findOpenChannel(); if (channel == -1) return; startSound(resource, channel, pending_[i].volume); } numPending_ = 0; } };

Call update() from the main game loop, or from a dedicated audio thread.

Page 10: Game Programming Patterns Event Queue From the book by Robert Nystrom .

Once we have the event queue – so far just a simple array – many improvements and variations are possible.

1. Use a ring buffer to efficiently remove single items from the beginning of the queue. (See the book for details.)

2. Aggregate multiple instances of the same request (problem 2).

3. Ensure that playSound() and update() are thread safe, so that multiple cores can be utilized (problem 3).

4. In this example, only the Audio class could read from the “single-cast” queue. A multi-reader “broadcast” queue is another option.

5. The items in the queue can be• messages: requests for actions to be performed in the future• events: notifications of something that already happened