What are games?
In my view, games are interactive simulations.
They are often "real-time" too.
What are simulations?
Simulations are imaginary worlds
running inside the computer, and subject to
well-defined (but potentially crazy) "laws".
Implementation-wise, simulations are perhaps best thought
of as a set of software
"objects" which can interact with one another, and whose state develops over time.
The "object oriented" model of programming fits this domain very well --
and was, in fact, invented for making simulations (Simula 67).
Mainloop 1
The mainloop of a game tends to look something like this:
function mainloop()
{
while (isRunningGame)
{
gatherInputs();
updateSimulation();
sendOutputs();
}
}
Let's explain those sections in turn...
gatherInputs()
gatherInputs() includes obvious stuff such as
reading the keyboard and mouse state,
along with less obvious stuff such as...
...joypads, motion-detectors / head-trackers, microphones, cameras, network inputs, file-streaming inputs, random number seeds...
...and
time
itself.
updateSimulation()
updateSimulation() takes some coherent instantaneous state of the simulated system (i.e. the representation of a bunch of objects at a particular instant in time), and the relevant inputs for this update, and produces a new coherent instantaneous state for a future instant in time.
In doing so, it may also trigger various output events
(e.g. sounds, or network packets, or haptic feedback signals).
sendOutputs()
sendOutputs() handles getting the necessary outputs to the relevant devices and, importantly, that includes the output device known as the "screen".
The screen is usually dealt with as something of a special case, and it is handled by making a special pass over the simulation state itself -- this pass is called "rendering".
Mainloop 2
So, more precisely, we can actually say:
{
inputs = gatherInputs();
newState, outputs = updateSimulation(oldState, inputs);
sendOutputs(outputs, newState);
oldState = newState;
}
or, in approximate object-speak:
{
inputs = gatherInputs();
outputs = simulation.update(inputs);
simulation.sendOutputs(outputs);
}
..in practice, we tend to tweak things a bit (for reasons of practicality and efficiency), but this is the basic outline.
Details...
This, however, is only part of the story.
For one thing, we need to have a way of setting things up before entering the mainloop. Likewise, we (usually)
have to clean-up after it has terminated.
function game()
{
init(); // init subsystems; establish preconditions for mainloop
mainloop(); // keep doing stuff until it's time to quit
term(); // cleanup, terminate subsystems, and exit back to OS
}
Events
The other, more significant, additional detail is the role of "events" in the scheme of things.
An event is, in a sense, a specialised form of external input which is generated by the Operating System or Operating Environment (e.g. the web browser) in which a program is running.
They include things like mouse and keyboard interactions and, in the case of a windowing system, drag / resize and repaint events.
The cause of events
Events are usually a way of "pushing" information into
a program which is not able to "pull" it directly.
Often, the inability to "pull" is because the program is running in a restricted environment which denies it direct access to external hardware (usually for reasons of safety/security,
but occasionally it's a convenience).
Also, these events are often considered to be high priority;
a well-behaved program should respond to them quickly
(and may in fact be terminated if it doesn't!)
The Event Loop
The result of including events in the mix is
a modified loop something like this:
while (isRunningGame)
{
handleEvents(); // process all OS events as a high priority
mainIterate(); // run all of the "game logic" (input/sim/output)
}
The browser environment is unusual in that it is almost entirely driven by "events", and has quite limited ability
to do anything else, but we'll return to that later...
Now What?
It might seem natural to dig into
the elements of the mainloop in order...
...or, perhaps, to work "middle out" by starting
on the core of the simulation update.
But I've decided to start by looking at the "rendering"
phase (i.e. the special part of "sendOutputs"). Why?
Well, if you were trying to reverse-engineer the system
from the outside, that's all you'd have to work from!