
Game Development Essentials #1 - Don’t use inheritance for your game objects - whalabi
http://unlikekinds.com/post/17192015158
======
amitt
This is great advice.

This is how the Unreal Engine was architected and it makes it really easy to
create new composite objects.

In contrast, when we did the first version of the MyMiniLife/FarmVille engine
in flash, in the name of the speed, we made the mistake of not architecting
the game object model in this way. We had to go through a painful refactor
process 6-months after FarmVille's launch to fix-up the object model because
we were duplicating behavior across objects that were really close to being
the same...but not at the same time.

Couple other benefits of using this approach:

1/ You can let game designers dynamically create new objects without
programmer intervention. Just let them mixup new components as they see fit
through an editor.

2/ If you build a visual editor/CMS for your objects, you can do some pretty
nifty stuff with custom editors/renderers for components. For example, the
UnrealEditor at one point had a custom color picker for the ColorComponent
that made it much easier to work with colors than entering R,G,B manually.

3/ If you build in serialization to your components at a low level, you can do
things like have lazy deserialization of your components so that they are only
instantiated/initialized once you access the component. This may not seem like
a big deal at first but when you are working with tens of thousands of objects
across millions of users, deserialization time ends up being significant.

~~~
Arelius
It needs to be mentioned that components are not how UnrealEngine was
architected, rather Unreal was designed as a monolithic single inheritance
tree, and components were very recently just shoehorned into the system.
Unreal still carries a lot of complexities from it's inheretiance even though
some of the more basic systems have been converted into component systems.

~~~
mrcharles
This cannot be said enough. If you are in doubt of the truth of this post,
look at Actor.uc.

------
hythloday
This is reasonable advice for the top 50% of your game (i.e. the bit you could
write in a scripting language or a custom data format: AI, dialogue, game
mechanics). For the bottom 50% (physics, effects, collision, animation), this
technique will cripple performance because of the massive amounts of
indirection and you will want to use a data-oriented programming approach
instead.

------
bnastic
Splitting objects into components and pursuing a more data-oriented design is
absolutely the way to go.

However, if you're not very careful this approach won't scale past mobile
games with a few dozen objects - too many iterations to update all the
components, or to find one of a particular type (e.g. the physics component),
killing your caches in the process (it's more complicated than that, but this
is just the gist of it). A well known games company used this exact approach
for their AAA engine (on a couple of games) and it was the no.1 reason for a
sluggish frame rate. It took considerable effort to get some caching
implemented so that there weren't as many indirect jumps around the code.

Splitting objects into components which are then kept physically close in
memory and updated in batches is another story, but that almost anti-OOP in
nature.

------
delinka
I've seen articles (book chapters, blog posts, usenet rants, etc) for years
similar in intent. "Don't use language feature X because it can be misused and
abused."

If we took all the advice offered by such articles, we'd all be using a
procedural language with few features. ASM perhaps. Humans have various mental
models when it comes to the world, technology, computing, and programming
languages. Humans tend to make mistakes. The benefits of modern programming
languages necessarily allow the human programmer to screw things up.

How about instead of "avoid X" we learn how to use X properly? We point out
the disadvantages in _certain_ _situations_ rather than insisting on complete
avoidance of X.

hythloday has the right idea here:
<http://news.ycombinator.com/item?id=3561182>

~~~
Lewton
The article didn't say "Don't use language feature X because it can be misused
and abused."

It said "Don't use language feature X in this fairly specific situation and
here's why:"

------
acron0
This model is pretty mainstream these days and the best commercial
implementation I've seen has to be in the Unity3D engine. It's almost entirely
componentised, and writing totally generic 'work-anywhere' components is
liberating.

One thing I dont think the article mentioned is that polymorphic approaches
are also often a lot slower, and this is crucial when working on platform
hardware, especially the more constrained stuff.

All that said, some of the comments are correct; inheritence does have a
place, but it should be used sparingly.

------
scott_s
I'm surprised no one mentioned that this is, conceptually, prototype-based OO.

Personally, in C++, I have found that mixins solve this problem well:
<http://www.cs.umass.edu/~yannis/practical-fmtd.pdf>

And for everyone who balks at multiple inheritance, I present to you some of
my own code, which uses mixins _and_ multiple inheritance:
[https://github.com/scotts/cellgen/blob/master/src/xformers.h...](https://github.com/scotts/cellgen/blob/master/src/xformers.h#L1186)
Probably baroque to an outsider, but it solved my problem well, and enabled me
to have a maintainable code base that I could also play around with when
testing new ideas.

------
lyudmil
This is good general programming advice beyond game development. If you think
you need inheritance, you probably will be better off using composition
because the general "is-a" rule of thumb is, in my opinion, a bad way to
figure out if inheritance is required.

More in this old thread: <http://news.ycombinator.com/item?id=1992745>

In short, use composition and if inheritance is what you want instead, it will
become obvious soon enough.

------
comex
Even though the suggested model is ultimately much more flexible, I'm
surprised the article didn't even mention multiple inheritance as the most
obvious solution to the dialog/animated problem. Not all the world is Java.

~~~
forrestthewoods
Eek! Multiple inheritance is not implemented in most programming languages for
good reason. In general it is wise to favor composition over inheritance.

~~~
zura
It is deciding between "IS A" and "HAS A".

In general, there is no such a rule that says: "HAS A" is wiser. It depends on
the use case.

~~~
Ygor
Related, an interesting article: IS-A IS-A HAS-A

<http://weblog.raganwald.com/2008/03/is-is-has.html>

------
djpowell
Is a massive rippling mutable OO graph the universal way to do game state? It
presumably makes concurrency an impossible deadlock-fest.

Are there any examples of using something like relational tables for game
state?

~~~
hythloday
Essentially, yes. Concurrency concerns in games are quite different to most
applications (because of the high value of latency and the low value of
fidelity[0]): typically there are around a dozen large computations to do 60
times a second (AI, animation, physics, collision, audio, effects, rendering,
UI, game mechanics, pathfinding, networking), and the environment for games
has been manycore (in the sense of both number and inhomogeneity of processor)
since the PS2.

Typically the approach is to push an "object graph" (actually it's usually a
bunch of arrays) down a pipeline, for example animation → physics → rendering
(for player walking), or collision → game mechanics → UI (for someone being
shot). For cache coherency reasons, all the stages are done in a single
operation, so where the data pipelines are too long to process in a single
frame it's very common to defer processing to a later frame--for example a
footfall event (caused by an animation) might trigger a sound effect on the
next frame, because audio processing takes a relatively large amount of frame
budget (which is OK because it has its own processor, and because we _know_
that the next frame will come around in less than 16.7ms).

I think that pursuing a row-locked model is unlikely because of the amount of
indirection involved--the Xenon (in the 360) and the PPE (in the PS3) are both
in-order processors, so synchronous reads and writes to memory (obviously
necessary for shared data) are absolutely destructive to performance.

[0] When I say "the high value of latency and the low value of fidelity",
making a tradeoff involving a, say, 5% performance hit for safety or
scalability is a no-brainer for most environments, e.g. a web app. In a game
it can well mean the difference between being able to release the game, and
having Sony and MSFT refuse to licence it because the framerate is too
unstable.

Similarly, a strategy that involves throwing away user data if it becomes too
large would be, let's say, eyebrow-raising in most industries, but it's _very_
common to have fixed-size particle buffers and to simply retire old particles
even if they haven't died naturally. This sort of "cheating" is pervasive and
necessary in games programming, and honestly, it's part of the fun. :)

------
stonemetal
Sorry if this sounds a bit ranty I recently inherited a really poorly done
component based system at work.

This reeks of the fallacy that if I use method X really really poorly and I
use method Y perfectly then obviously method Y is much better than method X.
No you didn't discover that Y is better than X, what you found is good
implementation beats bad implementation no matter how you assign the X and Y.
So yeah, I am glad you found Y to be better than X, good for you. If only the
rest of us were so lucky.

~~~
AerieC
I don't think it's ranty at all, and I think you make a good point.

There is always a lot of debate in this industry over which way of doing
things is "the best", and I don't think there's ever going to be a time where
one "best way" of doing things will apply to all circumstances. Rarely are two
problems in development exactly the same. How can anybody make the assertion
that any solution will be a one-size-fits-all?

In regards to this example, I can see many instances where inheritance in
games could be useful. Not every game needs as much flexibility as the author
suggests.

As long as code does what it's supposed to do, as fast as it should be doing
it, is well documented, and is understandable and clear, the path you take to
get the job done is irrelevant.

~~~
stonemetal
_Not every game needs as much flexibility as the author suggests._

That is the thing, the way our system got put together it isn't flexible.
Weapon damage got separated from weapon animations, so now there is no way to
dual wield or have shield bashes because there is no way to link damage to the
weapon animation. That happens often in our system, things are sliced and
diced so finely that you assemble everything out of these little blocks like
lego but they are devoid of context so it is impossible say this is apart of
that. That is fully a problem with our specific design not the general
strategy.

    
    
      Also it is rather slow. We store our entities in a database, so the ORM auto generates a 40 table join to check for all the different components we have(performance issues? like hell you say.)  Also for some reason we keep our game objects devoid of all context so there is no way to tell if you have an npc or a potion without examining all the components it has and making a guess.  Basically in our case the flexibility is a lie.  Sure you can gin up any set of components you like but you have no clue how the system is going to react.

------
nixle
I would still use inheritance to create a basic addComponent/componentFunction
class. I don't think inheritance is meant to be used for creating as much sub-
classes as possible. But as with all software design, it is important to know
where to draw the line. Where have you inherrited all you can? What you
describe is pretty much inheritance vs composition. A tradeoff as old as oop.

------
zura
In some sense, this is actually a workaround for languages that don't support
MI.

~~~
hythloday
C++ (where this advice is often given) supports MI, but there are some issues
with its implementation (the diamond problem) that make it unsuitable to solve
this problem in games.

------
extension
Use traits and you get this for free, without implementing all the plumbing
yourself.

Inheritance has its place in game dev though. It's just that your hiearchy
needs to be designed in the space of what you are actually building: not
vehicles, weapons, and monsters but renderers, physics, AI, GUIs, etc. And you
want to figure out how that stuff is going to work _before_ you break it up
into classes.

~~~
terinjokes
I don't come from a game development background, but I do find the realm
interesting, and I'll probably have my crack at it soon enough.

This post is interesting, it kinda goes against what I think of when I think
of OOP. I would have modeled it with inheritance of vehicles, weapons and
monsters. But after reading this post I could see how that could be a bad
idea.

But I'm curious in what you say. "Hierachy needs to be designed in the space
of what you're actually building: renderers, physics, AI and GUIs." Do you
care to explain this farther? Why those over the concrete objects? What do you
even mean by it?

EDIT: Specifically how what do you mean by having inheritance of physics,
renderers, AIs and GUIs?

EDIT II: Grammatical fixes.

~~~
Lazare
Not everyone agrees with this, but, here goes:

When I was in college, I was taught OOP in Java via the obligatory inheritance
examples: "So we have an Mammal class which has a 'walk' method, and then we
have Cat and Dog classes which _inherit_ from Animal, and have 'meow' and
'bark' methods."

This is a sadly ubiquitous anti-pattern which will _cripple_ your ability to
do OOP until you unlearn it. Never model the actual domain. Doesn't matter if
it's a game ("okay, so the PlateArmor class inherits from the Armor class") or
a CRM ("okay, so the Comment class will inherit from the FormattedContent
class").

Instead, model how the program should work. How to do that is a bit beyond the
scope of a comment, but a good start is to think about behaviours and
interfaces that logically fit together.

(And, again, this tends to be a divisive issue. But I - and a lot of very good
programmers whom I respect - strongly feel that the "Cat inherits from Mammal"
example is exactly and precisely what you should never do. Despite the fact
that it's how OOP is taught in most courses and books, seemingly.)

~~~
artmageddon
That's more or less how I learned it, and never really realized that it was an
anti-pattern. Do you have any links that go deeper into it?

~~~
Lazare
It's mostly stuff I picked up from practical experience and talking to other
programmers, so I don't have any links offhand. Let me expand a bit though.

The "wrong" way (or what I view as the wrong way) tends to be common in
academic settings, and (seemingly) among Java programmers. It is often
contemptuous or wary of multiple inheritance, and it follows the Nygaard
Classification[1] "A program execution is regarded as a physical model,
simulating the behavior of either a real or imaginary part of the world."

This definition makes sense when you realise it was created by the inventors
of Simula to describe their OOP simulation language. Unfortuantely, Simula was
far more influential than it deserved, because OOP is terrible at simulating
things[2]. When you create a class structure based on the actual simulation
you (1) will have a bear of a time doing it, (2) will find multiple
inheritance will melt your brain ("okay, so Black and White inherit from
Color, and Zebra inherits from Black, White, and Animal...") and (3) will find
than once your done you haven't actually solved your problem. (Not
surprisingly, Simula didn't have multiple inheritence, and language without
that feature often seem to gravitate to Nygaard-style OOP.)

So much for what _not_ to do. What about the right way? I don't really have
any links, but good principles are: Focus on interfaces and behaviours.
Classes should be abstract (in the common sense, not the language keyword),
not concrete. Understand how your program works, break it down into functional
areas, implement each area as a class. If you find common behaviours, abstract
them into base classes which you mixin where you need them. Inheritence trees
should be flat, minimal, and almost an afterthought. Also, a language like
Python (with duck typing) is a lot easier for most people to "get" OO than
Java.

Finally, the way it was explained to me that really "clicked" is this:

"Where I think most introductory courses in OOP go wrong is introducing
objects as being nouns rather than a collection of verbs. That leads directly
to improper use of inheritance. I was misled for years by the 'is a' idea. I
wish someone had told me 20 years ago that it was 'has the behavior of' that
was important."[3]

I think that's exactly right. But do keep in mind that the world is full of
people who think that's a heretical view and the Nygaard Classification is the
One True Way. :)

[1]: <http://c2.com/cgi/wiki?NygaardClassification> [2]: <http://lambda-the-
ultimate.org/node/3265#comment-48063> [3]: Omnivore, in a conversation on the
##stars! channel on Freenode

------
Dove
Good advice. Inheritance looks like simple code reuse, but it's extremely
inflexible. Every CS 101 class goes through that Mammal.breathe(),
Mammal::Animal.walk(), exercise, and every time, someone asks, "Well, what do
I do with a dolphin?" Do you borrow swim() from Fish? Or breathe() from
Mammal? Or reimplement them? Or what?

I'm convinced they're actually identifying a deep problem with inheritance as
a paradigm.

I won't say it's useless; sometimes you really do have an ironclad
relationship. But I think if you just want to communicate that your object
fulfills a promise, an interface is better. And if the code reuse is
circumstantial rather than essential, you're better off reaching for multiple
inheritance from partial classes -- traits, mixins, whatever your modern
language calls them.

------
lifebeyondfife
I was going to try to write a short description but I may as well just link to
the wiki page: <http://en.wikipedia.org/wiki/Strategy_pattern>

This design pattern gives you the freedom to select algorithms for different
behaviours without being tied to the strictness of the inheritance hierarchy.

------
hosh
This speaks more about the benefits for mixins, be they formal or informal. Or
composable functions for that matter. C++-style "object oriented programming"
is not the only game in town.

What was it Alan Kay said? It's not about the objects, it's about the
messages. (That is, messages and redirecting messages).

------
akg
Great advice. This touches on one of the limitations of OO design. Here is an
interesting writeup on some other arguments against Object Oriented
Programming: <http://c2.com/cgi/wiki?ArgumentsAgainstOop>

~~~
pbackx
I don't think it's a limitation of OO per se, it's just the way you use it.
The "use composition over inheritance" advice comes to mind:
<http://en.wikipedia.org/wiki/Composition_over_inheritance>

