So, there's this idea I've had in my head for the past several days. It's not going away so I should write it down.
Games are referred to by some places that want to pretend they're more than what they are (e.g. universities) as "Real-time interactive simulations." The name is actually pretty accurate:
- We've got a simulation, a collection of game objects in a game world that change state according to a set of rules. Can't pass through walls, take damage when shot, etc.
- We've got a clock. A load of rules in the simulation are about what to do in response to the clock "ticking."
- We've got one or more players. They have a "portal" into the world that gives them a view on the simulation and allows them some interaction, some limited ways to affect the state of the simulation. Or, swapping the active/passive roles, the player has ways of triggering events that the simulation can respond to ("gun fired," "step taken," etc).
So here's the crux of the idea: The way we write code for games, the procedural style, where we loop over bits of data and process things individually - it's not actually a very good way of doing it. Processing game objects one at a time is not the right way to think about things, really, because we're performing exactly the same operations on most game objects.
Another thing to consider is that programming isn't really "telling the computer what to do." If we were doing that, we'd be writing in assembly. What we're really doing is expressing the behaviour of our program in a form that our tools (compilers) can understand. As long as we've got the tools we can write them however we like.
So I'm wondering about experimenting with a more natural approach to writing, at least, the core simulation: SQL, or at least something like it. Instead of building up behaviours within self-contained objects, we encode them as set-based selections and updates. Newtonian motion across all objects in your game is just "UPDATE entities SET position = position + (velocity * Tick.slice)".
Sets, of course, are not sufficient to capture it all - so we add events as well. The most obvious event is the "Tick" event, which gets fired every frame; there's also things like the "Collided" event, the "BulletFired" event - whatever you like. Events are user-definable, as the events that make sense for your simulation are up to your simulation. They do also have data fields (which can themselves be sets), but are read-only once created.
There needs to be a way to raise events, natch. I'm considering a "FOR EACH ROW IN" syntax, which raises an event for each record in a set. So collision detection is "FOR EACH ROW IN entities a, entities b WHERE a.id != b.id AND MeshesIntersect(a.collisionMesh, b.collisionMesh) RAISE CollidedEvent(a.id, b.id)" for simple mesh-mesh collision. Obviously, there's more to it than that - we'd want to use separate queries for performing coarse testing (e.g. AABB) before doing mesh/mesh intersection, MeshesIntersect wouldn't be implemented in SQL, etc... there are some nice things that could be done with, for example, Octree-based indexes for positional columns.
The real benefits of this approach, though, become clear when you start having lots of cores available. Dependencies between statements are very obvious; a statement may only refer to data in the tables it is working with and data in the event that caused it to be executed. No other state may be used, and only tables referenced in the statements may be written to. Functions may not have side-effects. So, you've got these neatly-segregated little chunks of processing with clearly defined dependencies, input and output datasets, and a predisposition towards SIMD. Hello, stream-based processors and PS3...
It also produces interesting changes to the development methodology. As well as the demonstrated statements for describing runtime behaviour, what about CREATE/ALTER statements for loadtime? Suddenly an entire element of the game design can be packed up as a set of load-time and run-time behaviours. Want to add an economy to your game? Write a load-time statement to add a 'value' column to all items, populating them with a default value calculated from their weight and damage (plus a few hand-tuned values), and a statement to add a 'gold' column to the players table; and run-time statements to handle the 'PurchaseItem' event, etc. The ramifications for modding are quite impressive; if you've installed mods to add a bunch of new weapons to the game, and then you install a mod to rebalance the money values of all the weapons, the latter mod can update the custom weapons without even knowing that they're present if it uses a formula to calculate the new value.
Of course this only really works for the simulation part of the game. The 'portal' that players use can touch on it - "SELECT FROM entities WHERE InViewFrustum(position)" - and of course everything that the player does to the game world is expressed to the simulation as an event - but most of the code there still needs to be procedural. Still, it's a start.
SQL would of course be too slow at runtime to execute directly, so these behaviours could be fed to a tool that generates a more optimal result - calculates the best layout for data structures, etcetera.