The GDC 2003 Game Object Structure Roundtable
By Kyle Wilson
Tuesday, March 11, 2003
Thanks to everyone who came to the Game Object Structure Roundtable at this year's Game Developers Conference. We had about a hundred attendees over the course of the three sessions. It looks like a lot of people are interested in game object structure.
Composition vs. Inheritance
When polled as to whether they were currently using a Dungeon-Siege-style component system or a "traditional C++" inheritance hierarchy, developers' responses were split about half-and-half. It looks like there really is a broad movement away from deep inheritance hierarchies. There was some debate as to whether those deep inheritance hierarchies that crop up in so many games are really traditional C++ or just the result of bad teaching methods. Most developers seemed quite familiar with the shortcomings of inheritance-based class structure, particularly the lack of flexibility in responding to changing requirements (see The Diamond of Death).
All the discussion of inheritance prompted a question about when it makes since to use inheritance from mix-in classes instead of contained objects. Most respondents seemed to agree that inheritance of interfaces was good, but that inheritance from concrete classes was bad.
Almost everyone agreed that data-driven design is a good thing. The lone dissenting voice came from a developer from Naughty Dog. He said that they had a programmer-heavy team and that a selling point of their games was having a lot of non-repetitive gameplay, so they write a lot of custom script to drive their levels. He said that 99% of the code witting for Jak and Daxter was written in script. (See Gamasutra's Postmortem on Jak and Daxter.)
A developer from Ritual observed that this approach only works because Naughty Dog has a large number of programmers relative to designers. On his team at Ritual, he said, three programmers support about thirty designers and artists. It was in their interest to make a game that was as data-driven as possible. In the game he was developing, he said, object properties weren't even hard-coded. Rather, their system maps variable names to property values in a way that sounded sort of like Alex Duran's object system for DX2.
The only downside to data-driven design that got mentioned is the difficulty of debugging when so much of your game behavior is determined by data. Some developers argued that you just had to have better debugging tools incorporated into your engine, and that adding such tools is a good idea in any case. A developer from Legend observed that he hardly used the Visual Studio debugger anymore because the debugging output from the Unreal Engine was so informative.
The developer from Legend also mentioned that Unreal 2 involves much less UnrealScript than Wheel of Time, their earlier game. Ion Storm has also moved away from UnrealScript in Deus Ex 2 after using it extensively in Deus Ex. There was some suggestion that both companies are moving toward more data-driven architectures, and consequently are able to do more things in data that would have once required custom script.
I brought up a post by Christer Ericson of Sony to the Game Dev Algorithms mailing list, in which he wrote that "even the order of our various server subsystems (animation, rendering, scripts, etc) is controlled from a data file," and that this excessively data-driven approach complicated debugging while providing little useful benefit. The Sony people in attendance didn't know him (or apparently work on the same engine), though, and everyone else present seemed to be laboring under too little data-driven design, not too much.
The discussion of external object structure was a lesson in how odd my experiences have been. I've shipped three games. One was based entirely around a scene graph (realMyst), one had no scene graph at all (Savage Skies), and one had a hybrid system in which a game object in a list referenced a scene object in a scene graph (Carrier Strike Fighter).
As far as I can tell, everyone else is using a hybrid scheme like the one I just described. I think one person in the back may have raised a hand when I asked about pure scene graphs. No one was using flat object lists. A fellow from Gnostic Labs confirmed that they're using a hybrid in Eschaton. Day 1 used a hybrid in MechAssault. One developer suggested that it was impossible to ship a game using a pure scene graph architecture. I pointed out that Cyan had done so with realMYST and talked a little bit about the system's strengths and weaknesses.
There was a brief mention of augmenting scene graphs by, e.g., pushing other renderer state like materials into scene graph nodes.
There was a suggestion that a directed dependency graph would support a wider variety of features than a traditional (tree) scene graph. Concern was expressed about the efficiency of such a system.
The subject of databases came up several times. There was widespread interest in using off-the-shelf databases between the file system and the game. The first time this came up, no one seemed eager to be the first to try it; a number of people expressed concerns about database speed. The next day, however, a developer from World Fusion said that his company was using ObjectStore for such a purpose in their MMORPG Atriarch, and was looking into moving to GigaBASE, an open-source database engine.
A developer from Gas Powered said they were using SQL in tools, but not in game code.
Run-Time Type Information
One developer expressed great fondness for the C++ language's built-in RTTI system, but I think he was using it only in a pet project on a single platform, not in a shipping game. A developer from Rockstar said his team had tried RTTI in Visual Studio 7.0 and found it prohibitively slow, and that they'd had to flatten their inheritance hierarchy to bring it up to acceptable speed. Their less-happy experience might have been a consequence of the number of RTTI queries they were doing every frame.
There was some discussion as to whether RTTI was really necessary at all. If every object exposes some list of properties, queries as to "type" are no longer relevant.
A developer from EA working on their Harry Potter game said they used dynamic_casts the same way asserts are generally used. They dynamic_cast in debug builds, but fall back on static_casts in release.
A lot of developers seem to just use the old tried-and-true "check the enum from a virtual GetType() call and static_cast appropriately" method. No one said they were using anything like COM's QueryInterface, which kind of surprised me.
A question about game object interaction in a multithreaded system didn't get much useful response. A developer from Gas Powered said you should keep your multithreaded code in limited areas; if you lock at the component level, you'll find yourself having to strew synchronizations all through your game code. Scott Bilas went into this more in his article, "The Continuous World of Dungeon Siege", in which he writes "[Game object] creation involves nearly every system in the game, and because this runs on the second thread, most of the game had to be made thread-safe. This reduced performance considerably (5% skimmed right off the top just to serialize the heap) and created a fountain of bugs."
Object-editing tools seem to be surprisingly sparse. Only a third of the developers present said they even had level editors for their games. Fewer than one in ten had any sort of standalone object property editor. A similarly small percentage said they had custom plug-ins for editing object properties in Max.
There seems to be widespread use of script code to place objects and set object properties. (The Harry Potter developer from EA said they relied entirely on script. All of EA is apparently moving to Lua as a scripting language.) One developer mentioned that The Nebula Device, Radon Labs open-source game engine, uses tools that automatically generate Tcl/Tk script code, which is then read by the engine.
There were some complaints about using 3D Studio Max for editing of game object properties. One developer said he'd had problems with nodes referring to other nodes that weren't in the scene and other issues related to exposing too many technical properties to non-technical artists. I mentioned that it's useful to have a data verification step on export (something our level editor at Day 1 does). A developer from Microsoft pointed out that artists tend to ignore verification errors (the same way that programmers ignore compiler warnings). A developer from Raven said that they had a verification step in the data builds for their upcoming X-Men game which kept bad art from integrating into their primary data directory.
The developer from EA said they used a registry system with a fixed-size array of numeric tags pointing to objects. The numeric tags are hidden from scripters however. Scripters refer to objects by name, and names are translated into tags when scripts are compiled.
Another developer said that he used URL-like paths to reference any object in his scene graph by name. The Nebula Device apparently uses a similar system.
The developer from the X-Men team said that they had something like a "flags registry". They had an object that, given a flag value, would return an iterator to all game objects on which that flag was set. This gave the game quick access to, for example, all NPCs, all entities on a particular team, all spawned entities, and so forth.
Serialization and Persistence
I brought up serialization and persistence together because I think that exporting/importing an object, saving/loading an object at runtime, and maintaining an object's state across a network are all similar problems, and all could be solved through a common interface. To some extent, the engine we used in my time at Cyan did this. Save/load and network communication used the same functions to serialize state information to a stream, relying on dirty bits to mark differences in state from initial state (for save) or from the last transmitted state (for network data). The network component was never used in realMyst, however, and I understand the engine has been almost completely rewritten for Uru: Online Ages Beyond Myst.
A developer from Totally Games said that in Star Trek Bridge Commander they'd found that their save/load serialization code was too inefficient for network usage. They ended up having separate Serialize and NetSerialize calls, with NetSerialize transmitting minimal object state. He said they had separate Read and Write versions of each call, but that they had no serious problems with the functions getting out of synch. If something got written without being read or vice-versa, the program just crashed immediately, so problems weren't hard to track down.
Alex Duran from Ion Storm pointed out that saving/loading and export/import have different needs, and these are best addressed through different methods. Saving and loading should involve minimal data. It should compress game state as much as possible and almost certainly keep it in binary form. Exporting and importing, on the other hand, needs to replicate an object in all its complexity. It conveys maximum state information, needs to be versionable, and may benefit from being text-based so the data is human-comprehensible. He said that Ion Storm's serialization code had just one function which either read or wrote properties depending on the stream passed to it. But he said he wished they had separate Read and Write calls like the Bridge Commander guys. Instead, they ended up having to strew if (read) and if (write) conditionals throughout their serialization functions.
It's really hard to talk about game architecture. We all inhabit the landscapes of the engines we're working on, and when we're dropped in a room together at GDC and talking to people from other companies working on dissimilar products, there are few common referents. What I've summed up is what was said as I recall it and as I understood it, but I'm sure that some of what I've written is wrong because I processed it based on the architectures I've known. (If any readers notice that I've misreported things horribly, e-mail me at kyle at gamearchitect.net).
I wonder if a better forum wouldn't be to gather fewer people and drill down instead of trying to survey in breadth. A handful of people taking turns at a whiteboard and describing their game engines for ten or fifteen minutes each might be more instructive than the format of a GDC roundtable. It would certainly be more clear than studying features in isolation.
Despite these barriers to clear communication, the game object structure roundtable did offer a good deal of useful information. There really does seem to be a broad shift away from inheritance-based object structures toward a variety of component-based models. Most developers seem to be using hybrid systems that store game objects in a flat list, but the game objects refer to scene objects in a scene graph hierarchy. A number of teams seemed to be rolling their own RTTI systems, using macros or templates to construct static "class info" objects for each class. Fewer were extending these to add reflection information to the class info structures. Everyone likes data-driven design, but the dearth of good tools for building game content suggests that we're not there yet.
I'm Kyle Wilson. I've worked in the game industry since I got out of grad school in 1997. Any opinions expressed herein are in no way representative of those of my employers.