Game Engine Architecture Game Engine Architecture Spring 2017 - - PDF document

game engine architecture game engine architecture spring
SMART_READER_LITE
LIVE PREVIEW

Game Engine Architecture Game Engine Architecture Spring 2017 - - PDF document

Game Engine Architecture Game Engine Architecture Spring 2017 Spring 2017 03. Event systems for game engines 03. Event systems for game engines 03. Event systems for game engines 03. Event systems for game engines Juha Vihavainen Juha


slide-1
SLIDE 1

Game Engine Architecture Game Engine Architecture Spring 2017 Spring 2017

  • 03. Event systems for game engines
  • 03. Event systems for game engines
  • 03. Event systems for game engines
  • 03. Event systems for game engines

Juha Vihavainen Juha Vihavainen University of Helsinki University of Helsinki

[ [ McShaffry McShaffry, Ch. 6: , Ch. 6: Game actors and component architecture Game actors and component architecture,

  • Ch. 11:
  • Ch. 11: Game event management

Game event management ]

  • Ch. 11:
  • Ch. 11: Game event management

Game event management ] [ Gregory, [ Gregory, Ch. 5.4:

  • Ch. 5.4: Strings, unique ids

Strings, unique ids, localization localization, , etc., 274, etc., 274, Ch.15.2: Ch.15.2: Runtime object model architectures Runtime object model architectures, 873 , 873

  • Ch. 15.7:
  • Ch. 15.7: Events and message passing, 933

Events and message passing, 933] [Meyers (2015): [Meyers (2015): Effective Modern C Effective Modern C++ ++]

Lecture outline Lecture outline

  • On events and event handling

On events and event handling using using Observer Observer (MVC) and (MVC) and Command Command design patterns design patterns

  • using

using Observer Observer (MVC) and (MVC) and Command Command design patterns design patterns

  • Updates

Updates are not enough for game software are not enough for game software

  • Typed

Typed Message Message design pattern (Vlissides, 1997) design pattern (Vlissides, 1997)

  • breaking dependencies between senders & receivers

breaking dependencies between senders & receivers Queueing of messages for later delivery Queueing of messages for later delivery

  • Queueing of messages for later delivery

Queueing of messages for later delivery

  • Using unique

Using unique object ids

  • bject ids for game entities and types

for game entities and types

24.2.2017 24.2.2017 2 Juha Vihavainen / University of Helsinki Juha Vihavainen / University of Helsinki

slide-2
SLIDE 2
  • Problem

Problem

  • certain objects need to be informed about the changes occurring

certain objects need to be informed about the changes occurring in in other objects

  • ther objects

a a subject subject has to be observed by one or has to be observed by one or more more observers

  • bservers

The The Observer Observer design pattern design pattern

  • a

a subject subject has to be observed by one or has to be observed by one or more more observers

  • bservers
  • decouple as much as possible

decouple as much as possible and reduce and reduce the dependencies the dependencies

  • Solution

Solution

  • define a one

define a one-to to-many dependency between objects so that when many dependency between objects so that when

  • ne object
  • ne object changes its state

changes its state, all its dependents , all its dependents are automatically are automatically notified and updated notified and updated

  • A cornerstone of the

A cornerstone of the Model Model-View View-Controller Controller architectural design, architectural design, where the Model implements the mechanics of the program, and the where the Model implements the mechanics of the program, and the Views are implemented as Observers that Views are implemented as Observers that are kept uncoupled from are kept uncoupled from the the Model components Model components

3 24.2.2017 24.2.2017

Modified from Modified from Joey Paquet, 2007 Joey Paquet, 2007-2014 2014

University of Helsinki University of Helsinki

"View View" classes classes

Observer Observer pattern: class design pattern: class design

Erich Gamma et al., Design Patterns (1994)

Updates multiple

( seems

"Model Model" classes classes "View View" classes classes

24.2.2017 24.2.2017 University of Helsinki University of Helsinki 4

Updates multiple

  • bservers on changes

( seems complicated..)

Notifies all its observers

slide-3
SLIDE 3
  • Subject

Subject - abstract class defining the operations for attaching and de abstract class defining the operations for attaching and de- attaching observers to the client attaching observers to the client; often ; often referred to referred to as " as "Observable Observable"

  • ConcreteSubject

ConcreteSubject - concrete Subject class. concrete Subject class. It maintains It maintains the state of the state of

The participants The participants

ConcreteSubject ConcreteSubject - concrete Subject class. concrete Subject class. It maintains It maintains the state of the state of the observed object and when a change in its state occurs it notifies the observed object and when a change in its state occurs it notifies the attached Observers. If used as part of MVC, the the attached Observers. If used as part of MVC, the ConcreteSubject ConcreteSubject classes are the Model classes that have Views attached classes are the Model classes that have Views attached to them to them

  • Observer

Observer - interface or abstract class defining the operations to be interface or abstract class defining the operations to be used to notify the registered used to notify the registered Observer objects Observer objects

  • ConcreteObserver

ConcreteObserver - concrete observer concrete observer subclasses that are attached subclasses that are attached

  • ConcreteObserver

ConcreteObserver - concrete observer concrete observer subclasses that are attached subclasses that are attached to a to a particular subject class particular subject class

  • There

There may may be multiple different be multiple different concrete observers attached to a concrete observers attached to a single subject single subject that will provide a different view of that will provide a different view of that subject that subject

5 24.2.2017 24.2.2017 University of Helsinki University of Helsinki

  • The

The client class instantiates the client class instantiates the ConcreteObservable ConcreteObservable object

  • bject
  • Then

Then it instantiates and attaches the concrete observers to it using it instantiates and attaches the concrete observers to it using the methods defined in the the methods defined in the Observable Observable interface interface

On behaviour of Observer pattern On behaviour of Observer pattern

the methods defined in the the methods defined in the Observable Observable interface interface

  • Each

Each time the (observable) state of the subject is changing, it time the (observable) state of the subject is changing, it notifies all the attached observers using the methods defined in notifies all the attached observers using the methods defined in the the Observer Observer interface interface

  • When

When a new a new Observer Observer is added to the application, all we need to is added to the application, all we need to do is to instantiate it in the client class and do is to instantiate it in the client class and to attach to attach it to the it to the do is to instantiate it in the client class and do is to instantiate it in the client class and to attach to attach it to the it to the Observable Observable object

  • bject
  • The

The classes already classes already created (framework) will created (framework) will remain mostly remain mostly unchanged unchanged

6 24.2.2017 24.2.2017 University of Helsinki University of Helsinki

slide-4
SLIDE 4

7 // Observable.h #pragma once // Visual Studio include guard class Observer; // name stub needed only // Observer.h

Observer Observer pattern: interface classes pattern: interface classes

class Observable { // an abstract class public: virtual void attach (Observer *); virtual void detach (Observer *); virtual void notify (); virtual ~Observable (); Observable (Observable const&) = delete; ... protected: Observable (); // called by subclass private: // Observer.h #pragma once class Observer { // an abstract class public: virtual void update () = 0; virtual void detach () = 0; // added virtual ~Observer (); Observer (Observer const&) = delete; ... protected: Observer (); // called by subclass private: struct Observers; // name stub only Observers * observers_; }; Observer (); // called by subclass };

Note

  • Note. If a

. If a destructor destructor is declared, the is declared, the generation generation of default

  • f default copy operations

copy operations is is deprecated deprecated (to be removed from standard). (to be removed from standard). The compiler The compiler may may give a warning. For now give a warning. For now we need we need delete delete .. (C++11) .. (C++11) Pimpl Pimpl idiom hides std::list <Observer idiom hides std::list <Observer *> >

On C++ technicalities On C++ technicalities

  • The

The special member functions special member functions are those compilers may generate on their are those compilers may generate on their

  • wn (if needed, i.e., called):
  • wn (if needed, i.e., called): default constructor

default constructor, , destructor destructor, , copy copy

  • perations
  • perations, and

, and move operations move operations

  • Move operations (T &&) are generated only for classes lacking explicitly

Move operations (T &&) are generated only for classes lacking explicitly

Meyers S. (2015): Effective Modern C++, p. 115

Move operations (T &&) are generated only for classes lacking explicitly Move operations (T &&) are generated only for classes lacking explicitly declared move operations, copy operations, and declared move operations, copy operations, and a destructor

a destructor

  • The copy constructor (T

The copy constructor (T const const&) is generated only for classes lacking an &) is generated only for classes lacking an explicitly declared copy constructor, and it’s deleted if a move operation explicitly declared copy constructor, and it’s deleted if a move operation is declared is declared

  • The copy assignment operator

The copy assignment operator = = (T (T const const&) ) is generated only for &) ) is generated only for classes lacking an explicitly declared copy assignment operator, and it’s classes lacking an explicitly declared copy assignment operator, and it’s deleted if a move operation is declared deleted if a move operation is declared deleted if a move operation is declared deleted if a move operation is declared

  • Generation

Generation of the copy operations in classes with an explicitly declared

  • f the copy operations in classes with an explicitly declared

destructor is deprecated (i.e., to be removed from C++ standard) destructor is deprecated (i.e., to be removed from C++ standard)

  • Member function templates never suppress generation of special member

Member function templates never suppress generation of special member functions (even if templates could produce these copy operations) functions (even if templates could produce these copy operations)

24.2.2017 24.2.2017 8 University of Helsinki University of Helsinki

slide-5
SLIDE 5

Observer Observer Pattern Pattern: : Concrete Subject Concrete Subject

9 // ClockTimer.cpp #include "ClockTimer.h" ClockTimer::ClockTimer () : hour_(0), minute_(0), second_(0) {} void ClockTimer::start (int time) { // ClockTimer.h #pragma once #include "Observable.h" for (int i = 1; i <= time; ++i) tick (); } void ClockTimer::tick () { // by one sec ++second_; if (second_>= 60) { ++minute_; second_= 0; if (minute_>= 60) { ++hour_; minute_= 0; if (hour_>= 24) hour_= 0; class ClockTimer : public Observable { public: ClockTimer (); int hour () const { return hour_; } int minute () const { return minute_; } int second () const { return second_; } void start (int time); void tick (); private: int hour_; hour_= 0; } } // the Observable object notifies // all its registered observers notify (); } int minute_; int second_; }; 10 10 // DigitalClock.cpp #include "DigitalClock.h" #include "ClockTimer.h" #include <iostream> DigitalClock::DigitalClock (ClockTimer * s) { if (s != nullptr) s->attach (this); subject_ = s; // DigitalClock.h #pragma once #include "Observer.h" class ClockTimer; // name stub

Observer Observer Pattern Pattern: : Concrete Observer Concrete Observer

subject_ = s; } DigitalClock::~DigitalClock () { if (subject_!= nullptr) subject_->detach (this); } void DigitalClock::update () { display (); } void DigitalClock::detach () { subject_= nullptr; class DigitalClock : public Observer { public: void update () override; void detach () override; void display () const; DigitalClock (ClockTimer * s = nullptr); ~DigitalClock () override; private: } void DigitalClock::display () const { int hour = subject_->hour (); int minute = subject_->minute (); int second = subject_->second (); std::cout << hour << ":" << minute << ":" << second << std::endl; } ClockTimer * subject_; };

  • Note. Strong dependencies: if one is

destroyed, must update other.

slide-6
SLIDE 6

Observer pattern Observer pattern: : driver program driver program

// ClockDriver.cpp #include "ClockTimer.h" #include "DigitalClock.h" #include <iostream> // standard IO streams int main () { // create a ClockTimer to be observed ClockTimer timer; // create a DigitalClock that is connected to the ClockTimer DigitalClock digitalClock (&timer); // advancing the ClockTimer updates the DigitalClock // as tick() calls Update() after it changed its state

Objects can be Objects can be local local

11 11 int secs; std::cout << "Enter number of seconds to count: "; std::cin >> secs; timer.start (secs); int j; std::cin >> j; // pause at end of program } 24.2.2017 24.2.2017 University of Helsinki University of Helsinki

Implementation of the Observer pattern ( Implementation of the Observer pattern (Solution Solution) )

// Observable.cpp #include "Observable.h" #include "Observer.h" #include <list> // only here, not in Observable.h file struct Observable::Observers : std::list <Observer *> {}; // define implementation class

Note

  • Note. Should

. Should additionally enclose additionally enclose everything in our everything in our custom namespace.. custom namespace..

// define implementation class Observable::Observable () : observers_(new Observers) { } Observable::~Observable () { for (auto& x : *observers_) detach (x); delete observers_; } void Observable::attach (Observer * o) {

  • bservers_->push_back (o);

} * // Observer.cpp #include "Observer.h" Observer::Observer () {} Observer::~Observer () {} void Observable::detach (Observer * o) {

  • bservers_->remove (o);
  • ->detach (); // will break link to subject

} void Observable::notify () { for (auto& x : *observers_) x->update (); }

slide-7
SLIDE 7

Background: Background: updates updates are not enough are not enough

  • Games are inherently very much event

Games are inherently very much event-driven driven

  • an explosion goes off

an explosion goes off

  • the player is sighted by an enemy, or enters a trigger area

the player is sighted by an enemy, or enters a trigger area a health pack is getting picked up a health pack is getting picked up

  • a health pack is getting picked up

a health pack is getting picked up

  • Games need to

Games need to

  • notify interested game objects when an event occurs

notify interested game objects when an event occurs

  • arrange those objects to respond to significant events

arrange those objects to respond to significant events

  • Different kinds of game objects will respond in different ways to an

Different kinds of game objects will respond in different ways to an event; for example, a event; for example, a Pong Pong ball is governed/affected both by ball is governed/affected both by event; for example, a event; for example, a Pong Pong ball is governed/affected both by ball is governed/affected both by

  • updates in its physical "state", determined by its velocity

updates in its physical "state", determined by its velocity

  • hitting another object (paddle/wall) and changing its velocity

hitting another object (paddle/wall) and changing its velocity (rate and direction of change rate and direction of change)

  • being missed by one of the players

being missed by one of the players - and hitting the back wall and hitting the back wall

24.2.2017 24.2.2017 13 13 Juha Vihavainen / University of Helsinki Juha Vihavainen / University of Helsinki

Common game events [McShaffry, p. 325 Common game events [McShaffry, p. 325-

  • 326]

326]

1 1 General game events General game events

  • a game object has moved

a game object has moved

  • a collision has occurred

a collision has occurred

  • a character has changed states

a character has changed states

  • player character has changed

player character has changed

  • the event system is ready

the event system is ready

  • the sound system is ready

the sound system is ready

  • the network system is ready

the network system is ready

  • a human view has been attached

a human view has been attached

  • a new game object is created

a new game object is created

  • a game object is destroyed

a game object is destroyed

  • player character is dead

player character is dead

  • player

player death animation death animation is over (game over) is over (game over) 2 2 Map/mission events Map/mission events

  • a new level is about to be loaded (preload)

a new level is about to be loaded (preload)

  • a new level has finished loading (loaded)

a new level has finished loading (loaded)

  • the game is paused

the game is paused

  • the game is resumed

the game is resumed

  • the game is about to be saved (presave)

the game is about to be saved (presave)

  • the game has been saved (postsave)

the game has been saved (postsave) 4 4 Animation and sound effects Animation and sound effects

  • an animation has begun

an animation has begun

  • an animation has looped to beginning

an animation has looped to beginning

  • a character entered a trigger volume

a character entered a trigger volume

  • a character exited a trigger volume

a character exited a trigger volume

  • the player has been teleported

the player has been teleported 3 3 Game startup events Game startup events

  • the graphics system is ready

the graphics system is ready

  • the physics system is ready

the physics system is ready

  • a timing signal from animation to sound

a timing signal from animation to sound

  • an animation has ended

an animation has ended

  • a new sound effect has started

a new sound effect has started

  • a sound effect has looped back

a sound effect has looped back

  • a sound effect has completed

a sound effect has completed

  • a cinematic (video) has started

a cinematic (video) has started

  • a cinematic has ended

a cinematic has ended

24.2.2017 24.2.2017 14 14

slide-8
SLIDE 8

The normal object interaction doesn't work The normal object interaction doesn't work

  • Consider

Consider calling a method calling a method of an object to handle an event

  • f an object to handle an event

void void Explosion Explosion::doDamage() { ::doDamage() { // // this is pseudocode this is pseudocode var damagedObjects = var damagedObjects = var damagedObjects = var damagedObjects = theGame. theGame.getObjects getObjects (getDamagesSphere ()); (getDamagesSphere ()); for each object in damagedObjects do for each object in damagedObjects do

  • bject.
  • bject.onExplosion
  • nExplosion (*this)

this) }

  • This kind of object interaction

This kind of object interaction style does not scale up style does not scale up

Gregory, p.934

  • Here

Here, , game objects need to be inherited from a base class that defines game objects need to be inherited from a base class that defines

  • nExplosion()
  • nExplosion() - even if not all objects respond to explosions

even if not all objects respond to explosions

  • Such virtual functions would be needed for

Such virtual functions would be needed for all events all events in the game in the game

  • Better to register for only interesting events and handle those ones

Better to register for only interesting events and handle those ones

24.2.2017 24.2.2017 15 15 Juha Vihavainen / University of Helsinki Juha Vihavainen / University of Helsinki

Registering interest in events Registering interest in events

  • We don't want to

We don't want to unnecessarily unnecessarily send events / call event handlers send events / call event handlers

  • can maintain a list of interested listeners (

can maintain a list of interested listeners (observers

  • bservers) for each

) for each

  • can maintain a list of interested listeners (

can maintain a list of interested listeners (observers

  • bservers) for each

) for each distinct type of event, and/or distinct type of event, and/or

  • each "potential" receiver could keep data to show the particular

each "potential" receiver could keep data to show the particular events this receiver is interested in (say, as a bit array).. events this receiver is interested in (say, as a bit array)..

  • The

The classic classic version is The version is The Observer Observer design pattern: define design pattern: define 1-

  • to

to-many many dependency dependency (Gamma et al., 1994), described earlier

(Gamma et al., 1994), described earlier

  • Instead, we could use callbacks (function pointers), or

Instead, we could use callbacks (function pointers), or

  • Build up special event records to send around

Build up special event records to send around

24.2.2017 24.2.2017 16 16

slide-9
SLIDE 9

Use of an event system Use of an event system

  • E.g., a subsystem (say, physics) cannot keep track of all other subsystems

E.g., a subsystem (say, physics) cannot keep track of all other subsystems that need to know about moving objects (e.g., the game renderer) that need to know about moving objects (e.g., the game renderer)

  • they can have different APIs and different parameter lists (messy)

they can have different APIs and different parameter lists (messy)

  • An event system centralizes and uniforms all interactions

An event system centralizes and uniforms all interactions

  • supports modularization and separation of concerns

supports modularization and separation of concerns

  • enables central tracing and debugging of all activities

enables central tracing and debugging of all activities

  • In a well

In a well-designed game engine designed game engine

  • an event system is a part of the higher support layer

an event system is a part of the higher support layer

  • each subsystem is responsible for subscribing to and handling game

each subsystem is responsible for subscribing to and handling game events as they pass through the system events as they pass through the system

  • an event system manages all communications between the game logic

an event system manages all communications between the game logic and game views (e.g., display, network interactions) and game views (e.g., display, network interactions)

24.2.2017 24.2.2017 17 17 Juha Vihavainen / University of Helsinki Juha Vihavainen / University of Helsinki

Encapsulating an event in an object Encapsulating an event in an object

  • An

An event event ~ a ~ a command command ~ a ~ a message message

  • event signaling is similar to sending a message or a command

event signaling is similar to sending a message or a command

  • An event may consist of two parts

An event may consist of two parts An event may consist of two parts An event may consist of two parts

  • type

type: : explosion explosion, , picking up a thing picking up a thing, , player being spotted player being spotted, etc. , etc.

  • arguments

arguments: specifics about the event (where, how large, who/what) : specifics about the event (where, how large, who/what) struct Event { struct Event { // // often a base class

  • ften a base class

EventKind kind; EventKind kind; // // meaning of this message meaning of this message EventArgs EventArgs arguments arguments; // // linked list or array, giving . . linked list or array, giving . . . . . . . . // // . . . . arguments of various types arguments of various types }; };

  • Usually, different kinds of events can be derived from an base event class

Usually, different kinds of events can be derived from an base event class

  • depending on the platform and its restrictions (memory, etc.)

depending on the platform and its restrictions (memory, etc.)

24.2.2017 24.2.2017 18 18 Juha Vihavainen / University of Helsinki Juha Vihavainen / University of Helsinki

Gregory, p.935

slide-10
SLIDE 10

Event handlers (one version) (Gregory, p. 940) Event handlers (one version) (Gregory, p. 940)

  • An event handler is some piece of code that handles a received event:

An event handler is some piece of code that handles a received event: virtual void SomeObject::onEvent (Event& event) { virtual void SomeObject::onEvent (Event& event) { switch (event.kind) { switch (event.kind) { // // illustrative only illustrative only case EventKind::Attack: case EventKind::Attack: case EventKind::Attack: case EventKind::Attack: respondToAttack (event.getInfo ()); respondToAttack (event.getInfo ()); break; break; case EventKind::HealthPack: case EventKind::HealthPack: addHealth (event.getInfo ()); addHealth (event.getInfo ()); break; break; . . . . . . } }

May ay need to downcast (for need to downcast (for "type recovery") "type recovery")

}

  • One single method

One single method vs

  • vs. many handlers:

. many handlers: OnAttack (), OnAttack (), etc. ?

  • etc. ?
  • For strings or

For strings or hashed string hashed string IDs, use cascaded IDs, use cascaded if if/else else-if if statements .. statements ..

  • Other approach:

Other approach: function pointers function pointers (" ("delegates delegates") to ") to virtual virtual or non

  • r non-

virtual virtual functions, mapped from event types (used, e.g. with old functions, mapped from event types (used, e.g. with old MFC MFC)

24.2.2017 24.2.2017 19 19 Juha Vihavainen / University of Helsinki Juha Vihavainen / University of Helsinki

Notes about an implementation (McShaffry, 2009) Notes about an implementation (McShaffry, 2009)

  • (McShaffry) uses a separate

(McShaffry) uses a separate EventManager EventManager class ( class (- really necessary?) really necessary?)

  • Again, smart pointers

Again, smart pointers can automate can automate garbage collection (mostly) garbage collection (mostly)

  • Note the "loosely typed" events: handlers need to inspect

Note the "loosely typed" events: handlers need to inspect type info type info

  • vs

vs. . Typed Message Typed Message that uses clean type that uses clean type-specific event handlers specific event handlers

  • but static typing not always possible: e.g., msgs from network..

but static typing not always possible: e.g., msgs from network..

  • The implemention

The implemention can use can use custom meta custom meta-objects (

  • bjects (EventType

EventType)

  • identify different event kinds for specific processing

identify different event kinds for specific processing

  • in a simple prototype system could well use a C++

in a simple prototype system could well use a C++ enumeration enumeration enum Kind class { ObjMove, ObjCollision, PlayerDeath.. }; enum Kind class { ObjMove, ObjCollision, PlayerDeath.. }; enum Kind class { ObjMove, ObjCollision, PlayerDeath.. }; enum Kind class { ObjMove, ObjCollision, PlayerDeath.. };

  • however, this strategy scales up badly in practice (recompilations)

however, this strategy scales up badly in practice (recompilations)

  • better to use

better to use unique hashed ID unique hashed IDs or something similar ( s or something similar (GUID GUID)

  • possibly could augment/integrate with the C++ built

possibly could augment/integrate with the C++ built-in RTTI (..?) in RTTI (..?)

24.2.2017 24.2.2017 20 20 Juha Vihavainen / University of Helsinki Juha Vihavainen / University of Helsinki

slide-11
SLIDE 11

Typed Message Typed Message design pattern design pattern

  • In the

In the Observer Observer design pattern, the participants are still tightly coupled design pattern, the participants are still tightly coupled

  • the subject "

the subject "knows" its observers (any number of knows" its observers (any number of them) them)

  • an observer

an observer maintains a ref to the (concrete) subject to get its state maintains a ref to the (concrete) subject to get its state

  • John Vlissides, "

John Vlissides, "Multicast Multicast - Observer = Typed Message Observer = Typed Message", 1997 ", 1997

  • in

in C++ Report C++ Report, Nov/Dec 1997; also later described in the book by , Nov/Dec 1997; also later described in the book by

  • J. Vlissides:
  • J. Vlissides: Pattern Hatching

Pattern Hatching. Addison . Addison-Wesley, 1998 Wesley, 1998

  • A minimal

A minimal type type-safe safe solution; can be used to implement other versions.. solution; can be used to implement other versions..

Described also in the master's thesis of M. Väänänen: Described also in the master's thesis of M. Väänänen: Olioarkkitehtuurit Olioarkkitehtuurit

  • Described also in the master's thesis of M. Väänänen:

Described also in the master's thesis of M. Väänänen: Olioarkkitehtuurit Olioarkkitehtuurit peliohjelmistoissa peliohjelmistoissa (Univ. of Helsinki, Dept. of CS, 2008) (Univ. of Helsinki, Dept. of CS, 2008)

  • the original version uses immediate dispatching (as usual for GUI)

the original version uses immediate dispatching (as usual for GUI)

  • M.V. added a

M.V. added a queue queue per handler; per handler; handleBufferedEvents handleBufferedEvents () delivered () delivered messages that were earlier sent to a specific handler messages that were earlier sent to a specific handler

24.2.2017 24.2.2017 21 21 Juha Vihavainen / University of Helsinki Juha Vihavainen / University of Helsinki

Features Features of

  • f Typed Message

Typed Message

  • Actually

Actually minimizes coupling minimizes coupling: senders don't know about handlers, and : senders don't know about handlers, and handlers don't know about senders handlers don't know about senders

  • handlers are associated with events

handlers are associated with events T, not with subjects/senders , not with subjects/senders

  • A

A T handler is derived from handler is derived from TypedMsgHandler TypedMsgHandler <T>

  • A

A T handler is derived from handler is derived from TypedMsgHandler TypedMsgHandler <T>

  • a kind of mixin class (providing selected limited capabilities)

a kind of mixin class (providing selected limited capabilities)

  • no extra registration is required (enabled by default)

no extra registration is required (enabled by default)

  • a

a T message message is received by an operation " is received by an operation "handle (T const& msg) handle (T const& msg)"

  • can later remove a handler from the handler list (to disable it)

can later remove a handler from the handler list (to disable it)

  • and later insert the handler back into the handler list (to enable it)

and later insert the handler back into the handler list (to enable it)

  • A

A T message is sent by a separate: " message is sent by a separate: "DeliverTypedMsg (myMsg) DeliverTypedMsg (myMsg)" template <typename T> void DeliverTypedMsg (T const& msg) { template <typename T> void DeliverTypedMsg (T const& msg) { TypedMsgHandler <T>::Deliver (msg); TypedMsgHandler <T>::Deliver (msg); } } // // infers infers T from from msg msg and just hides the more complicated call and just hides the more complicated call

24.2.2017 24.2.2017 22 22 Juha Vihavainen / University of Helsinki Juha Vihavainen / University of Helsinki

slide-12
SLIDE 12

template <typename T> template <typename T> // // the message type the message type T class TypedMsgHandler { class TypedMsgHandler { // // works for all and any works for all and any T public: public: // // (well almost well almost) ) virtual void handle (T const& msg) = 0; virtual void handle (T const& msg) = 0; // // handle a message handle a message

TypedMsgHandler TypedMsgHandler < T > (one version) < T > (one version)

virtual void handle (T const& msg) = 0; virtual void handle (T const& msg) = 0; // // handle a message handle a message void attach (); void attach (); // // insert into handlers insert into handlers void detach (); void detach (); // // remove from handlers remove from handlers TypedMsgHandler () { attach (); } TypedMsgHandler () { attach (); } // // listening by default listening by default virtual ~TypedMsgHandler () { detach (); } // virtual ~TypedMsgHandler () { detach (); } // but not any more but not any more private: private:

Raw pointers OK Raw pointers OK - or need

  • r need smart ptrs

smart ptrs? same as if same as if typedef typedef

private: private: using Handlers = std::list <TypedMsgHandlerPtr>; // using Handlers = std::list <TypedMsgHandlerPtr>; // alias def. alias def. static Handlers GetHandlers (); static Handlers GetHandlers (); // // get a get a copy copy of handlers

  • f handlers

static void Deliver (T const&); static void Deliver (T const&); // // do the actual delivery do the actual delivery }; };

24.2.2017 24.2.2017 23 23 Juha Vihavainen / University of Helsinki Juha Vihavainen / University of Helsinki

No manager used here No manager used here

Use of Use of Typed Message Typed Message

  • To receive a message need only to derive a subclass:

To receive a message need only to derive a subclass: class GameEntity: class GameEntity: public TypedMsgHandler public TypedMsgHandler <std::string std::string> . . . { > . . . { public: . . . public: . . . void handle ( void handle (std::string std::string const& msg) override { const& msg) override { void handle ( void handle (std::string std::string const& msg) override { const& msg) override { std::cout << "GameEntity received: " + msg << std::endl; std::cout << "GameEntity received: " + msg << std::endl; } . . . } . . . }; };

  • A message is simply sent by calling the routine:

A message is simply sent by calling the routine: std::string myMsg = "Hello, handler!"; std::string myMsg = "Hello, handler!"; // // create local data create local data

W Works

  • rks for

for built built-in or in or user types/classes user types/classes

DeliverTypedMsg ( DeliverTypedMsg (myMsg myMsg); ); // // send it to all send it to all

  • Now, prints out:

Now, prints out: " GameEntity GameEntity received received: Hello, handler Hello, handler!"; !";

  • Note that message binding is

Note that message binding is safely typed safely typed (at signature level) (at signature level)

  • C++ provides

C++ provides multiple inheritance multiple inheritance to handle different message kinds to handle different message kinds

24.2.2017 24.2.2017 24 24 Juha Vihavainen / University of Helsinki Juha Vihavainen / University of Helsinki

slide-13
SLIDE 13

Implementing delivery of typed messages Implementing delivery of typed messages

  • A list of message handlers is kept

A list of message handlers is kept per per a message type a message type

  • To send a message, get its handlers and call their

To send a message, get its handlers and call their handle handle function function template <typename T> // template <typename T> // private utility function private utility function template <typename T> // template <typename T> // private utility function private utility function void TypedMsgHandler <T>::Deliver (T const& msg) { void TypedMsgHandler <T>::Deliver (T const& msg) { for (auto& handler : GetHandlers ()) // for (auto& handler : GetHandlers ()) // traverse traverse copy copy safely safely handler handler-

  • >

>handle (msg); handle (msg); // // virtual call virtual call }

  • Efficient: the message data can be kept on the call stack

Efficient: the message data can be kept on the call stack

  • the

the queued queued version needs to allocate ( version needs to allocate (new new) msgs (of course) ) msgs (of course)

  • Note that handlers register and unregister themselves automatically

Note that handlers register and unregister themselves automatically

  • but still, handler objects need to be destroyed in proper manner

but still, handler objects need to be destroyed in proper manner

  • e.g., first mark then as dead, then clear them out later

e.g., first mark then as dead, then clear them out later

24.2.2017 24.2.2017 25 25 Juha Vihavainen / University of Helsinki Juha Vihavainen / University of Helsinki

Event sending Event sending

  • In GUI system, events are (usually) immediately processed

In GUI system, events are (usually) immediately processed

  • sometimes events cause sending

sometimes events cause sending new events new events to other objects/parts to other objects/parts

  • immediate event sending can cause deep call chains

immediate event sending can cause deep call chains

  • immediate event sending can cause deep call chains

immediate event sending can cause deep call chains

  • bject
  • bject A sends to object

sends to object B that sends to object that sends to object C (that sends to (that sends to A..) ..)

  • in the worst case, immediate sending may cause an eternal loop

in the worst case, immediate sending may cause an eternal loop

  • Often, event handlers need to be

Often, event handlers need to be re re-entrant entrant (= no harmful extra global (= no harmful extra global updates/side updates/side-effects) effects) - if they can be called if they can be called multiple times.. multiple times..

  • In games, events can be handled

In games, events can be handled In games, events can be handled In games, events can be handled

  • immediately, or later during the same frame (animation blending),

immediately, or later during the same frame (animation blending),

  • during the next frame

during the next frame (game loop (game loop cycle), or cycle), or

  • even at some arbitrary future time, say, after n seconds (or frames)

even at some arbitrary future time, say, after n seconds (or frames)

24.2.2017 24.2.2017 26 26 Juha Vihavainen / University of Helsinki Juha Vihavainen / University of Helsinki

slide-14
SLIDE 14

Event queuing Event queuing

  • Queuing helps to manage when events are handled

Queuing helps to manage when events are handled

  • Can post events into the future: each

Can post events into the future: each Event Event would have a would have a delivery time delivery time to indicate the dispatch time; consider how to process such events to indicate the dispatch time; consider how to process such events void EventQueue::dispatchTimedEvents (float currTime) { void EventQueue::dispatchTimedEvents (float currTime) { void EventQueue::dispatchTimedEvents (float currTime) { void EventQueue::dispatchTimedEvents (float currTime) { EventPtr pEvent = peekNextEvent (); EventPtr pEvent = peekNextEvent (); while (pEvent && pEvent while (pEvent && pEvent->deliveryTime deliveryTime <= <= currTime) { currTime) { removeNextEvent (); removeNextEvent (); // // got one to be triggered got one to be triggered pEvent pEvent->dispatch (); dispatch (); // DeliverTypedMsg ( // DeliverTypedMsg (pEvent pEvent) ) pEvent = peekNextEvent (); pEvent = peekNextEvent (); pEvent = peekNextEvent (); pEvent = peekNextEvent (); } }

  • also, could prioritize events to control the order during a frame

also, could prioritize events to control the order during a frame

  • drawbacks: complexity,

drawbacks: complexity, and need for dyn. mem. allocations

and need for dyn. mem. allocations

19.2.2013 19.2.2013 27 27 Juha Vihavainen / University of Helsinki Juha Vihavainen / University of Helsinki

(GEA, p. 945)

Extra services Extra services

  • The

he queued queued events events need need to to be be dynamically dynamically allocated allocated: should : should ensure ensure this this

  • E

Event vent kinds kinds could could be be validated validated by by requiring requiring that that event event types types must must be be registered registered before before use use => => could could guard guard against against mismatches mismatches and and typos typos If f event event processing processing loop loop takes takes too too much much time time (given given a a system system-dependent dependent

  • If

f event event processing processing loop loop takes takes too too much much time time (given given a a system system-dependent dependent time time limit limit), ), can can try try to to push push spilled spilled-over

  • ver events

events onto

  • nto the other queue

the other queue

  • but

but what what if if queues queues just keep growing and staying full..? just keep growing and staying full..?

  • perhaps

perhaps need need some some optimization

  • ptimization and/or

and/or down down-grade grade effect effects

  • (McShaffry)

(McShaffry) proposes proposes a a special special event event kind kind (" ("Any Any") ")

  • such

such handler handler could could conveniently conveniently trace trace all all events and activities events and activities

  • A handler

handler could could somehow somehow indicate indicate that that the the message message has has been been processed processed (to implement (to implement Chain Chain of

  • f Command

Command: input : input is not propagated is not propagated any any more more)

  • Timed

imed events events (kept in a separate queue) resemble scheduling (kept in a separate queue) resemble scheduling coroutines coroutines

24.2.2017 24.2.2017 28 28 Juha Vihavainen / University of Helsinki Juha Vihavainen / University of Helsinki

slide-15
SLIDE 15

Summary: distinguishing Summary: distinguishing events events from from tasks tasks

  • A task

task is an is an activity activity that that goes goes on

  • n over
  • ver multiple

multiple frames frames

  • e.g

e.g., ., animations animations and and sound sound effects effects, , control control of

  • f game

game flow, and so on flow, and so on

  • An

n event event is a is a notification notification of a

  • f a relevant

relevant change change in the in the game game world world that that requires requires processing processing requires requires processing processing

  • an AI

an AI character character is is spawn spawn, a , a game game trigger trigger fires fires, etc. , etc.

  • ften
  • ften causes

causes changes changes in in on

  • n-going

going activities activities (tasks tasks)

  • Further study

Further study: how to receive with : how to receive with any any functions / to use functions / to use closures closures?

  • (Gregory) implies that events can be send to and are processed by

(Gregory) implies that events can be send to and are processed by almost any game entities, to specify all possible interactions.. almost any game entities, to specify all possible interactions.. almost any game entities, to specify all possible interactions.. almost any game entities, to specify all possible interactions..

  • In constrast, (McShaffry) advises that

In constrast, (McShaffry) advises that

  • we don't want giant lists of listeners to go through for each event

we don't want giant lists of listeners to go through for each event

  • events should be mostly used between subsystems (managers)

events should be mostly used between subsystems (managers)

  • game objects are in turn handled by these subsystems

game objects are in turn handled by these subsystems

24.2.2017 24.2.2017 29 29 Juha Vihavainen / University of Helsinki Juha Vihavainen / University of Helsinki

Data Data-

  • driven event systems (Gregory, p. 950

driven event systems (Gregory, p. 950-954) 954)

  • Event types may need to be defined externally, e.g., in a textual

Event types may need to be defined externally, e.g., in a textual scripting language (or similar) scripting language (or similar)

  • to generate, send, and handle events

to generate, send, and handle events

  • need some symbolic form to name them uniformly

need some symbolic form to name them uniformly

  • the whole game logic could then be expressed by scripting only

the whole game logic could then be expressed by scripting only

  • Event objects may need to be saved and loaded

Event objects may need to be saved and loaded

  • must support

must support serialization serialization of events (

  • f events (object IO
  • bject IO)

)

  • in

in online multiplayer games

  • nline multiplayer games, need to send them out there anyway

, need to send them out there anyway

  • Note that we are kind of

Note that we are kind of wiring up wiring up a game with events and handlers a game with events and handlers At extreme, At extreme, graphical scripting systems graphical scripting systems provide components provide components

  • At extreme,

At extreme, graphical scripting systems graphical scripting systems provide components provide components (operations/blocks) with "ports" to be manually connected (wired) (operations/blocks) with "ports" to be manually connected (wired)

  • can be configured and combined to express new interactions

can be configured and combined to express new interactions

  • events are used to trigger (start) sequences of actions

events are used to trigger (start) sequences of actions

  • Unreal Engine

Unreal Engine and and Blender Game Engine Blender Game Engine provide such facilities provide such facilities

24.2.2017 24.2.2017 30 30 Juha Vihavainen / University of Helsinki Juha Vihavainen / University of Helsinki

slide-16
SLIDE 16

How to define types ( How to define types (tags tags) for events ) for events

A simple and efficient A simple and efficient - but does not scale well for large games but does not scale well for large games enum class EventKind { enum class EventKind { LevelStarted, LevelStarted,

(Gregory) suggests using hashed strings ids as global identifiers (names)

LevelStarted, LevelStarted, EnemySpawned, EnemySpawned, PlayerSpotted, PlayerSpotted, BulletHit . . BulletHit . . }; }; Drawbacks of Drawbacks of enum class enum class: : info about all events kinds is centralized and hard info about all events kinds is centralized and hard-coded coded

In C++, we have built-in RTTI (Run-Time Type Info) but its implementation is partly undefined.. strings ids as global identifiers (names)

  • info about all events kinds is centralized and hard

info about all events kinds is centralized and hard-coded coded => changes cause system => changes cause system-wide recompilations wide recompilations

  • event codes are implementation

event codes are implementation-dependent ordered integers dependent ordered integers => does not support data => does not support data-driven event definitions (in scripts) driven event definitions (in scripts)

31 31 24.2.2017 24.2.2017 Juha Vihavainen / University of Helsinki Juha Vihavainen / University of Helsinki

Encoding ncoding event types via event types via string name string names

  • A very free

A very free-form and easy to use: just name a new event type to add it form and easy to use: just name a new event type to add it

  • Some problems:

Some problems:

  • increased

increased memory memory requirements (need requirements (need char char arrays/buffers) arrays/buffers)

  • increased

increased memory memory requirements (need requirements (need char char arrays/buffers) arrays/buffers)

  • high

high-cost of cost of comparing comparing actual strings (takes linear time) actual strings (takes linear time)

  • can have name

can have name conflicts conflicts (e.g., when introducing new event types) (e.g., when introducing new event types)

  • possibility for

possibility for aliases aliases, and , and mismatches mismatches or typographical errors

  • r typographical errors
  • Some remedies:

Some remedies:

  • use a

use a central repository central repository to register ("declare") event type names to register ("declare") event type names

  • can store additional info: documentation, use of arguments..

can store additional info: documentation, use of arguments..

  • use

use hashed string IDs hashed string IDs: generate unique integer IDs for strings : generate unique integer IDs for strings

  • faster comparison, uses less memory (to pass around)

faster comparison, uses less memory (to pass around)

  • when needed can (easily) recover the original name

when needed can (easily) recover the original name

24.2.2017 24.2.2017 32 32 Juha Vihavainen / University of Helsinki Juha Vihavainen / University of Helsinki

slide-17
SLIDE 17

Unique ids Unique ids for game entities [Greg, Ch.5.4, 277 for game entities [Greg, Ch.5.4, 277-279] 279]

  • Say, in PacMan we might encounter game entities named "pacman",

Say, in PacMan we might encounter game entities named "pacman", "blinky", "pinky", "inky", and "clyde" "blinky", "pinky", "inky", and "clyde"

  • Also, the assets from which our game objects are constructed

Also, the assets from which our game objects are constructed - meshes, meshes, materials, textures, audio clips, animations, etc. materials, textures, audio clips, animations, etc. - need unique need unique materials, textures, audio clips, animations, etc. materials, textures, audio clips, animations, etc. - need unique need unique identifiers of their own identifiers of their own

  • We want to hash strings into

We want to hash strings into string ids string ids (~ unique integer codes) (~ unique integer codes)

  • Interning

Interning a string means hashing it and adding it to a global string a string means hashing it and adding it to a global string table; the original string can be recovered from the hash code later table; the original string can be recovered from the hash code later

  • Another idea used by the

Another idea used by the Unreal Engine Unreal Engine is to wrap the is to wrap the string id string id and a and a

  • Another idea used by the

Another idea used by the Unreal Engine Unreal Engine is to wrap the is to wrap the string id string id and a and a pointer to the corresponding C pointer to the corresponding C-style style char char array in a tiny class array in a tiny class

  • in the

in the Unreal Engine Unreal Engine, a "name table" of , a "name table" of FNames FNames maps unique maps unique strings to indices; provides a lightweight system for strings, where strings to indices; provides a lightweight system for strings, where a given string is stored once only in a table a given string is stored once only in a table --

  • - even if reused

even if reused

24.2.2017 24.2.2017 Juha Vihavainen / University of Helsinki Juha Vihavainen / University of Helsinki 33 33

using SId = unsigned32; using SId = unsigned32; // // modified from modified from (Gregory, 278) (Gregory, 278) using StringIdTable = std::unordered_map <SId, char const using StringIdTable = std::unordered_map <SId, char const *>; >; static StringIdTable stringIdTable; . . . static StringIdTable stringIdTable; . . . // // a singleton, again a singleton, again SId internString (char const SId internString (char const * str) { str) {

Must ensure uniqueness.. Must ensure uniqueness..

SId internString (char const SId internString (char const * str) { str) { SId sid = SId sid = hashCrc32 hashCrc32 (str); (str); // // custom hash creates unique ids custom hash creates unique ids if (stringIdTable.find (sid) == stringIdTable.end ()) // if (stringIdTable.find (sid) == stringIdTable.end ()) // not found not found stringIdTable [sid] = std::strdup (str); stringIdTable [sid] = std::strdup (str); return sid; return sid; } } . . . . . .

See also: Gregory, Section 5.4.3.2 See also: Gregory, Section 5.4.3.2 Some implementation ideas, p.277 Some implementation ideas, p.277

static SId sidFoo = internString ("foo"); // static SId sidFoo = internString ("foo"); // to use to use SId SId as a name as a name static SId sidBar = internString ("bar"); static SId sidBar = internString ("bar"); . . . . . . if (sid == sidFoo) . . . // if (sid == sidFoo) . . . // then handle the then handle the "foo foo" case .. case ..

24.2.2017 24.2.2017 Juha Vihavainen / University of Helsinki Juha Vihavainen / University of Helsinki 34 34