Recent games i played rely a lot on event-driven programming and scripts. This post is about the idea i got from playing games like Fallout, Morrowind and Oblivion about their event-driven systems in a very light perspective but i hope it can still be of some use.
Every game engine relies on several components or sub-systems: graphics engine, controller, ai, sound, database, etc. A complex game will most of the time drive the graphics engine step by step and provide at least a sort of task manager or scheduller that will drive the game in indendent tasks (possibly with their own distinct schedulling times), which are the rendering task, the input pooling task and the logic update task.
This “task” manager should be flexible enough to either work in a single-thread or multiple threads in a transparent way freeing the engine programmer from the burden of having to choose how many threads and how these should be used if the game is going to run in different platforms.
The game logic is responsable for determining what game actors will be updated (AI loding), when to load/unload a scene, when to add/delete actors to the scene, when to activate/deactivate actors, when to suspend/resume the game (doesn’t suspend render or input), when to transform input and output from the game guis and convert basic events to logical events. Input transformation is the part where the game translates primitive input events using the keyboard and mouse bindings table into logical events like jump, run forward, fire, use, heartbeat and stuff like that.
Once the translation process is complete and there are new logical events they are dispatched to the active actors in the game. An event filtering criteria determines which actors receive which events. The player sends events from the use of the keyboard and mouse that are excluvly addressed to the player avatar or what the player controls at a certain moment.
In a simple event dispatching scheme we can consider an event has a piece of info having an event timestamp, event origin and event destination. Event data is exchanged only between the controler and the active actors and never directly bewteen actors. The controler acts as an intermediary for all comunications.
In a typical, non-optimized, game logic cycle:
while(1) {
receive input
translate input
schedulle logical events from logical input
analize current scenes
schedulle logical events from collisions and other scene events
dispatch events to actors
actors schedulle their own events requesting some action or do nothing
change the scene based on requested actions
}
This scheme implies two things. One is that actors don’t modify themselves or the game state directly but instead announce what they intend to do, which is very convinient because that may not be what realy is supposed to happen. Second actors must interpret what event patterns they receive (as well as inspect the scene directly) and update their decision variables acordingly.
This is tipical event-driven programming and it is a completely different programming paradigm from imperative programming. Execution takes place during the ocurrence of certain events and unlike imperative programming we cannot predict with 100% certainty which events will occur and in what order.
So in event-driven programming we have another element that is a pattern of events. Each actor decides what to do next in relation to the patterns he has stored in his local state. When a new event is received its patterns are updated or if no event is received for a long time old patterns may be forgotten.
A pattern may be encoded with something as simple as a finite set of boolean variables for example to represent the state of a gui button, or something as complex as a prolog theory.
The actor reaction maybe a simple state-machine that decides what to do based on the values encoded in its pattern boolean variables, or something that involves AI search on its theory and in this case an actor is called an agent.