I feel particularly verbose tonight. So while I'm busy preserving my 1.44 floppy disk collection, I'll write a piece on an interesting way of architecting games that I've been playing around with. I know these concepts are already used in the industry, but I rarely see them mentioned in the publications I read. They stem from some very old roots in the SIMULA programming language (which some consider the first OO language).
Games As Simulators
Most video games can be loosely defined as simulators. For instance, Quake is a tactical combat simulator, chess is a very abstract simulation of war, Lemmings is ... well, you get the picture. At their roots, all of these games share a similar feature: they model a system that changes over time.
Why am I getting all metaphysical about simulation and stuff? Well, it's because I want to talk about a different way to write a game than most are accustomed to. What I want to talk about is the main loop. It's the thing that usually looks like this (pseudo-code, of course):
This technique worked for the classic arcade games, and is used to good effect in many modern games. However, it can be inefficient when large number of objects are modeled. Consider an online RPG with 1,000,000 separate interacting objects. Unless every object performs some action at every time step, it it very wasteful to poll each of them in turn N times a second.
The best way to handle games with large numbers of objects is to use an event-driven simulation. If you've done any GUI programming, you probably understand the concept of events. You probably handle even handle events in your main loop, to process input from the keyboard/mouse/whatever. But most people don't use events as the basis of their entire game. (If you do, lemme know!)
Events & Event Queues
What does an event look like? In our world, let's make it look like this:
There is also a 'World' object which looks like this:
consume_events() works kinda like this:
The Main Event Loop
So here is the main loop pseudo-code for an event-driven game:
So have you had your epiphany yet? Do you feel the love that events provide? Ok, if not ... consider this event class, intended for a tile-based RTS game:
"But wait," you cry, "In this model, units will appear to jump from tile to tile, like in those old-fashioned CGA strategy games! I want them to move smoothly like in Starcraft, and I can't do that with this model! Do you want me to be the laughing stock of the RTS community?"
Just hold on, cowpolk -- you can do smooth motion in an event model. Just call upon your old friend, interpolation. Keep track of when you last moved a unit, and when the next move is due. Then when you go to draw that unit, interpolate between the last known location and the current location, using this ratio:
What's The Big Deal?
This is all great, but ... why are we supposed to use events? Oh yes, for performance reasons! Now why is it faster than the typical way of doing things ...?
It's faster because the game only computes things that are actually happening. What if you've been playing the game for umpteen hours and there are 1,000's of dead units strewn around the playing field? With an event-based system, you don't have to ask each one "are you alive?" or "are you moving?" every frame. Live units keep on moving and firing, churning out events; dead ones just sit there. This may not be a big issue for a 50-unit game, but it is critical to building next-generation online worlds with millions of objects.
Even if you aren't building the next massively-online game, event modeling can still be useful. Since every interaction in the game takes place in terms of an event, you can just design the entire game in terms of events. It makes sense from a design point of view, and it'll be easier to explain the game to co-developers. Instead of a barely-commented method called "updateSpiralingDeathRayState" in your Spaceship class, you have a totally separate "SpiralingDeathRayEvent" class. How nice!
Also, an event model is a perfect fit for a networked multiplayer game. If everything that happens is an event, it's easy to construct a client/server model for a game.
"But wait," you wail, "Everything you've been talking about has been applied to tile-based games... But my game ISN'T tile-based at all, it's a space simulation with real orbital mechanics, and it's gotta move on a fixed time step, it CAN'T be event-ized! What are you trying to do, man, destroy me?"
There, there ... you can make an event-driven simulation with accurate physics and collision mechanics, without using a fixed time step. You get the same benefits of scale that we discussed, plus some other benefits -- like not having to choose an arbitrary time step for the entire universe.
But that's a topic for another time. In the meantime, please mail me if you have anything to say about event simulation. I'd especially like to hear of any current games that are event-based -- I have my theories on some. Ciao!