An Anatomy of Despair: Aggregation Over Inheritance

June 1st, 2008

One of the first decisions that Adrian and I made in our initial work on Despair was to prefer aggregation to inheritance whenever possible.  This is not an original idea.  If you Google for “aggregation inheritance” or “composition inheritance,” you’ll get a million hits.  The C++ development community has been renouncing its irrational exuberance over inheritance for the last few years now.  Sutter and Alexandrescu even include “prefer composition to inheritance” as a guideline in C++ Coding Standards.

liberation2.jpgNonetheless, every game engine we’d worked on before Despair had a similar deep inheritance hierarchy of the sort that was in vogue in the mid-nineties:  a player class might inherit from some kind of combatant class, which would inherit from a mover class, which would inherit from a physical object class, which would inherit from a base game object class.

This architecture has a lot of shortcomings.  Let me enumerate a few of them:

First, it’s inflexible.  If you want to create a new AI enemy that has some of the capabilities of enemy A and some of the capabilities of enemy B, that’s not a task that fits naturally into a hierarchical object classification.  Ideally, you’d like the designers to be able to create a new enemy type without involving you, the programmer, at all.  But with a deep object hierarchy, you have to get involved and you have to try to pick the best implementation from several bad options:  to have your new enemy class inherit from one object and cut-and-paste the functions you need from the other; to not inherit from either, and to cut-and-paste the functions you need from both; or to tiptoe down the treacherous slippery slope of multiple inheritance and hope that it doesn’t lead to a diamond of death.

Second, a handful of classes in your hierarchy tend to grow without bound over a game’s development.  If the player class is part of the object hierarchy, then you can expect this class to include input and control systems, custom animation controls, pickup and inventory systems, targeting assistance, network synchronization–plus any special systems required by the particular game that you’re making.  One previous game that we worked on features a 13,000 line player class implementation, and the player class inherited from a 12,000 physical object class.  It’s hard to find anything in files that size, and they’re frequent spots for merge conflicts since everyone’s trying to add new stuff to them all the time.

Third, deep inheritance is poor physical structure.  If class A inherits from class B which inherits from class C, then the header file for A–A.h–has to #include B.h and C.h.  As your hierarchy gets deeper, you’ll find that all of your leaf classes have to include four or five extra headers for their base classes at different levels.  For most modern games, as much compile time is spent opening and reading header files as is spent actually compiling code.  The more loosely your code is coupled, the faster you can compile.  (See Lakos for more details.)

Therefore we resolved to make Despair as component-based as we could, and to keep our inheritance hierarchies as flat as possible.  A game object in Despair, for example, is basically a thin wrapper around a UID and a standard vector of game components.  Components can be queried for type information and dynamically cast.  The game object provides only lifetime management and identifier scoping.  It knows nothing about component implementations.  It contains no traditional game object state like position, bounds, or visual representation.

This approach has informed other systems as well.  Our scene object implementation is similar to the game object implementation, with a single object representing each model that provides lifetime management for a vector of scene nodes.  Scene nodes manage their own hierarchies for render state or skeletal transforms.

Another family of systems is built on a flow-graph library for visual editing.  Game logic, animation systems, and materials can all be built by non-programmers wiring together graph components in the appropriate tools.

Using composition instead of inheritance has worked very well for us.  Our primary concern when we set out in this new direction was that we’d end up with something that had horrible runtime performance.  With Fracture almost complete, though, there’s no evidence that our performance is worse than it would have been with a deep inheritance hierarchy.  If anything, I’m inclined to suspect that it’s better, since well-encapsulated components have better cache locality than large objects and since the fact that we only update dirty components each frame means that we can decide what does and doesn’t need to be updated at a finer granularity.

If I were starting over again, the only change I’d make with respect to object composition is to make scene objects more opaque and less like game objects.  Scene objects have a different problem to solve.  We have several hundred game components now, with more going in all the time, and the flexibility of having a thin game object interface that allows querying components for type has paid big dividends.  I think that our game object system is close to ideal for iterating rapidly on gameplay.  Scene objects are a different kind of problem, though.  We haven’t added any new scene node types since the scene library was written, and all scene nodes are implemented in the scene library instead of in higher-level code.  At the same time, it would be nice to be able to experiment with different optimizations of updating skeletal hierarchies without breaking higher-level code.  All of this argues that following the Law of Demeter and hiding scene object implementation details would have been appropriate for scene objects even if it wasn’t appropriate in the rapid-prototyping environment of game objects.

Beyond the perfect abstract world of software architecture, component based design also created a couple of surprises in the messier world of development process and human interaction.  One lesson of working with a composition-based engine is that the learning curve for new programmers is steeper.  For programmers who are used to being able to step through a few big nested functions and see the whole game, it can be disorienting to step through the game and discover that there’s just no there there.  For example, Despair contains over 500 classes with the word “component” in their names and 100 classes with the word “object” in their names.  Our games aren’t defined by C++ objects, they’re defined by relationships between them.  To understand those relationships, you need good documentation and communication more than ever.

Another composition lesson is that component-based design takes a lot of the complexity that used to exist in code and pushes it into data.  Designers aren’t used to designing objects and constructing inheritance hierarchies.  Working with components requires new processes and good people.  Cross-pollination is important.  Your programmers need to work building objects in your tools, as well as writing code for components, and they need to work hand-in-hand with good technical designers who can provide tool feedback and build on the object primitives available to them.  Like a game, your team isn’t defined by its individual components but by the relationships between them.

An Anatomy of Despair: Orthogonal Views

April 19th, 2008

A frustrating feature of previous game engines we’d used was that they tended to overload hierarchy to mean multiple things.

The engine that we used at Cyan, for example, was a Treepure scene graph.  Every part of the game was represented by one or more nodes in a hierarchy.  But the hierarchy represented logical relationships in some places and kinematic relationships in others.  Throughout the graph, ownership was conflated with update order:  children would be deleted when their parents were deleted and parents always updated before their children.  Kinematic attachment was performed by pruning and grafting trees in the graph, which had the effect of tying the lifetimes of attached objects to the lifetimes of their parents.

Read the rest of this entry »

An Anatomy of Despair: Object Ownership

April 15th, 2008

In every game engine that I worked on before Despair, I spent a lot of time tracking down memory leaks.  Some leaks were obvious and easy to find.  Some leaks involved complex patterns of ownership that thoroughly obscured what object was supposed to be responsible for deleting another.  And some leaks involved AddRef/Release mismatches that would create cycles of ownership or that would leak whole object hierarchies.

Read the rest of this entry »

An Anatomy of Despair: Introduction

April 15th, 2008

I’ve been working for a little more than three years on the Despair Engine, the game engine that Day 1 is using in Fracture and another, as-yet-unannounced, title.  In the beginning, there were two of us working on the technology, me and my longtime friend and collaborator Adrian Stone.  Now we’ve got thirty programmers working in the same codebase.  Fracture’s getting close to shipping.  This seems like a good time to look back at the principles that shaped our initial architecture and at the decisions whose consequences we’re living with today.

The name started as a joke.  Adrian and I were out to lunch with our lead one day.  Somebody made a crack about naming the engine “Despair.”  One thing led to another, and by the end of the meal we’d plotted out a whole suite of despair-themed content creation tools, most of which never got made.  It was 2004.  We were starting from scratch on core technology for big-budget AAA games running on consoles that didn’t even exist yet.  We figured that “Despair” would work one way or the other, either as an imperative to our competitors if we succeeded or as a sadly accurate description of our own feelings if we failed.

Read the rest of this entry »

GDC 2008

March 12th, 2008

This year was the tenth anniversary of my first GDC.  It’s milestones like this that make one pause and take stock of one’s life.  Over the last decade, one of my college classmates was appointed United States Attorney for South Carolina; another lost a limb fighting in Iraq; another married actor Steve Martin (yes, that Steve Martin).  And I… I have spent most of my waking hours contributing, in my own small way, to the perpetual adolescence of the American male.

Like a career in game development, GDC is a mix of small rewards and great frustrations–and yet the two somehow balance each other out.  Back in 1998, the attendees were a small brash crowd, excited that gaming had finally arrived and enthusiastic about the future.  I think that was the first time somebody announced the oft-repeated canard about how we’re bigger than the movie industry.  My friends and I all made the crawl from room to room on Suite Night, availing ourselves of the free drinks and the catered food from the warming trays.  There was a party on the Queen Mary.  I got a thick pile of t-shirts and an Intel graphics card, and thought, 3dfx better watch out now that Intel’s getting into the market.

Now GDC has become like SIGGRAPH.  The crowds are enormous–nearly 15,000 in attendance this year, I’m told.  But it’s not the size of the crowds that makes it less intimate.  It’s the fact that so many of the people there don’t really have much to say to each other:  there are indie game developers, console game developers, serious game developers, mobile phone game developers, people selling middleware and hardware and outsourcing to all of the above, recruiters, wannabes, publishers trying to sign developers, developers looking for a publisher, HR folks looking to hire artists and programmers and musicians, and press trying to cover the whole spectacle.  The death of E3 meant that this year there were more sessions than ever that could be summed up as, ”look at my game and how awesome it is.”  I tried to avoid those.  I spent my three days at GDC looking for quiet places to talk to the people who do the same thing I do.  I didn’t go to any of the parties. Read the rest of this entry »

WordPress

March 9th, 2008

When I started GameArchitect, the word blog didn’t exist yet, and there was, therefore, a real shortage of decent blogging software.  I ended up buying Joel Spolsky’s CityDesk, mostly because it was what he used for Joel on Software and I like the look of his site.

CityDesk hasn’t had a lot of work over the last couple of years, though, and I never have managed to figure out how to make it generate a proper RSS feed.  So I’m switching the site over to WordPress, about which I’ve heard good things.  All old content is still available at http://www.gamearchitect.net/citydesk.html.  Pardon the dust and plaster.