RiverMan Media

RiverMan Media 1

Object Oriented Game Programming: The Behavior System PDF Print E-mail
Written by Paul Stevens   
Thursday, 01 May 2008 07:04
Article Index
Object Oriented Game Programming: The Behavior System
Implementation
Behavior case studies
All Pages

Object Oriented Game Programming: Use the Behavior System to share diverse functionality between objects in your game.


Object Oriented Game Programming:

The Behavior System

The Behavior System is the single most important idea I've had as a game programmer.  They are extremely useful on their own, but they also inspired other powerful systems, most notably Robot Functions (http://rivermanmedia.com/programming/6-programming/17-object-oriented-game-programming-robot-functions) and the Scripted Event System (article coming soon).  I hope that I can do them justice in this article.

What are Behaviors?

Behaviors are small objects that add functionality to a parent object.  The parent has a reference to the behavior, and the behavior has a reference to the parent, set when the behavior is initialized.  The behavior manipulates the data of it's parent object to produce a desired set of functionality.  The real strength behind behaviors is that they allow totally different objects to share the same capabilities.

For example, the same code (behavior) that slides your menus onto the screen also moves around enemies in the game world.  The same behavior that animates your walking player character also animates a glowing effect behind your powerups.  The same behavior that makes your widgets clickable can be applied to any rectangular object, and they become clickable too, with just a couple lines of code. 

If you are familiar with component-based designs, Behaviors could be considered a hybrid between a "pure component" system and a typical class hierarchy.  Unlike a pure component system, Behaviors supplement a traditional class hierarchy (rather than replacing it), giving you the full freedom to use "components" AND inheritance to generate objects in your game.

Benefits of Behaviors:

Behaviors help you transform huge amounts of previously object-specific code into library-code, which can now be shared by many diverse entities in your game, including menus, cinemas, characters, effects, etc.  When you add a behavior to an object, you save the time of re-implementing the same functionality again.  You avoid bugs and a lot of testing, because you know that the behavior already works elsewhere.  If you add more content to the behavior, all of the present and future parents of the behavior get to make use of the new content.  Behaviors also keep your parent classes shorter, more organized, and easier to understand by collecting some of the different things that the parent can do and distilling them down into easily managable objects.

Origins of the system:

The earliest inspiration for behaviors came from an article that I read several years ago about objects in a first-person shooter (I'll try to find the article again so that I can credit the author).  I don't remember the specifics, but basically the programmer was describing how objects in the game like the player, pickups, grenades, and enemies don't follow a normal class heirarchy (e.g., they could all extend GameObject and branch as they get more specific), but instead are an aggregate of different chracteristics, like "affected by gravity" and "destructable." 

This allows objects to only have the attributes that they need.  Otherwise, all of the functionality for every object would need to be contained in the top superclass, GameObject, making each individual object much heavier because they have to ignore a bunch of functionality that they aren't actually using.  Either that, or you'd have to copy and paste a lot of code to get the objects to behave how you want, which is much worse.  This idea seemed really cool, but I couldn't figure out a good way to implement it.

The main motivation for behaviors in their current form came near the end of Cash Cow's development when I realized that I had written slight variations on the same code in many different classes.  For example, I wrote code to slide the coins to a specific point when they combine, and I wrote similar code to slide the menus in and out.  I knew that writing these slight variations on the same code again and again required a whole lot more time, testing, and frustration than if the menus and the coins could somehow share this "sliding" behavior, but how? 

Thus, the Behavior System was born, as a solution to both of the above situations.  Not only do behaviors add attributes to their parents, they can also add more complex capabilities related to game logic, motion, rendering options, and more.  It might make more sense with some examples (I'll write all the examples in this article in Java, since I first implemented them in Java).

Behavior Examples:

One really common behavior is animation.  In this case, I mean animating by periodically changing an object's image to the next image in a pre-defined set of frames, like frames of a person walking.  Many objects in your game need to do this kind of animation, but they might have nothing else in common.  For example, in Primate Panic, the propellers on the plane in the first cinema use an animation behavior to spin, and the chameleons use an animation behavior when the stick out their tongues.  The monkeys use one as they climb vines, the humans use one when the throw a monkey, and... you get the idea, they're everywhere.  They've saved me a ton of time.

While some behaviors are general purpose, like animation, others are more specific and will probably only ever operate on one class.  This means that instead of accepting a simple interface as the "parent" object, it would take a reference to a single class, like "Monkey."  In these cases, behaviors are still very useful because they allow you to greatly simplify and organize the capabilities of the parent.

Here are some behavior examples from Primate Panic (there are dozens more):

  1. A WalkBehavior syncs up the player character's walking frame with the distance in the game world that s/he has actually moved, making it look like the player is really walking on the ground.  Monkeys use this too.
  2. When you click an area on the overworld map, FlipBehaviors "flip" the boxes with all the level icons while changing their image.
  3. When a monkey pulls on a trap door handle, a TrapDoorBehavior moves the monkey and pull bar down, opens the trap door, and waits a couple of seconds before telling the monkey to drop and the trap door to close.
  4. A FadeBehavior periodically fades the ghosts in and out, with a little extra code in the Ghost class to activate and deactivate their collision box in the process.
  5. A ButtonBehavior is responsible for nearly all the logic in the menu buttons.  ButtonBehavior detects the mouse's presence, stores button state data, and notifies the parent GUI when it is clicked (see http://rivermanmedia.com/programming/6-programming/5-object-oriented-game-programming-the-gui-stack for more about graphical user interfaces).

 

Example 2 is a case of a fairly general behavior.  Any object that can scale and change images can do the same flipping effect as those icons.  This means that if I ever wanted, I could make the monkeys flip like that, the GUI windows, the raft, etc.  Same deal with the FadeBehavior for ghosts: any object that can change its alpha value can fade in and out just like the ghosts.  All it has to do is implement an interface, CanFade, that contains method stubs for getting and setting its alpha.

As for the ButtonBehavior, any object with just a couple important functions, like functions to get its location and dimensions, can become a button just by containing and updating its own ButtonBehavior.  Again, this means that, in addition to whatever else they do, spacial objects in any part of your game could also operate as buttons just by implementing a simple interface and updating a ButtonBehavior object.  In fact, most objects in your game that store their coordinates and size probably already implement the interface necessary to be a button. 

What's the point?  Without any extra work, a monkey picking fruit could be a button.  Folliage could be a button.  The shopkeeper could be a button.  Darkfellow's grave in the last cinema could be a button.  Buttons could be buttons.  The list goes on and on.

Note: in most of the last examples, you probably wouldn't need to use a ButtonBehavior, but instead a simpler variation that just detects when it's parent is clicked.  All the additional state data and graphic changes of actual buttons is more complicated than most clickable things use.  That's why I have another behavior, ClickableBehavior, which is a simplification of ButtonBehavior for widgets like the "buttons" on the title screen that don't actually get depressed like normal buttons when clicked.

Read on for implementation-->