Tutorial
icon-dog1_01

A Lazy Approach to Designing Consistent AI Behaviors

Alex J. Champandard on November 14, 2007

When logic is built from multiple modular behaviors, you must make sure they work together consistently. Typically, to do that, your AI stores a variety of information shared by the behaviors to make sure there aren’t any clashes. Of course, this takes a lot of time and effort, but luckily there’s a lazier way!

A good example is last week’s tutorial which created simple dog behaviors using a tree structure with random decisions. The problem was temporal coherence of the behaviors; the peeing or pooping animations could be followed immediately by sitting down or sleeping!

This article investigates design techniques for making behaviors consistent — without relying on any kind of mental model. In fact, you’ll learn how to do this using only a simple behavior tree, with sequences and probabilistic selectors (for random decisions). Not only is this sufficient for creating consistent and meaningful behaviors, but it’s also useful for adding personality and character quirks too.

A Simple Methodology

The gist is to focus on the outcome, i.e. what you want the behaviors to be. Be sure to read examples of this method in practice below, but here’s the roughly how the dog’s behaviors were improved:

  1. Identify which additional feature of the mental model would improve or fix the current behaviors.

  2. Work out what types of behaviors would result from reasoning with this extra information.

  3. Insert this behavioral response, without the mental model, somewhere into the problematic behaviors.

This is the secret why scripting works so well. You can use sequences of behaviors to capture the designers intentions without modeling them in the AI. Four case studies follow, but first it should be easy to reuse behavior trees…

Parameterized Tree Behaviors

Re-usability of behavior trees quickly becomes necessary when building large trees or increasing the complexity of existing behaviors. This can easily be done using a builder function that takes parameters, builds a tree from the parameters, and returns the root node as an encapsulated modular behavior.

Parameterized Behavior Tree

Figure 1: A modular parameterized BT.

Basically, movement is defined as a parallel behavior that executes an animate action, a translate action, and an action to wait for a certain amount of time. The code for building the tree looks something like this:

Node* makeMove(float time, char* type, float speed)
{
  return TreeBuilder()
    .composite()
    // Actions added here using parameters.
    .end();
}

Both movement (animation + translation) and turning (animation + rotation) are implemented this way.

The Sleeping Behavior

All the behaviors in the previous tutorial simply play an animation. In the case of sleeping, the dog would just pass out instantly from the previous posture. Using the methodology described above, it can be improved drastically without any extra technology:

  1. Ideally, the sleeping should reflect a mental model of tiredness, so the dogs energy levels would drop slowly over time.

  2. To portray this, the dog should slowly become drowsy over time, and use lower-energy behaviors as it gets sleepier.

  3. Now, to achieve this without the mental model, you can extend the sleep behavior to include the process of getting drowsy.

The sleep behavior now is preceded by a slow walk, lying down with the head up, then resting the head on the floor. Once the sleep is over, the dog sits up first, then bounces around once.

Behavior Tree

Figure 2: A BT for sleeping that reuses parameterized trees.

This tree is essentially a sequence at the top level, which relies on the parametric move behavior defined above. Then it plays a bunch of animations with time limits, and then moves again with a different parametric tree.

Scratching Itches with AI

The scratching behavior doesn’t require as much effort as the sleeping behavior; it’s pretty much fine to execute it randomly. However, it’s a good opportunity to add personality to this behavior using no new technology:

  • The dog bounces around once as if it was stung,

  • then it scratches its back by rolling over,

  • and finally scratches its neck sitting down.

This specific sequence can be packed with any kind of idiosyncrasies to portray the character of a particular dog.

Peeing Consistently

The dog’s peeing behavior is another example where adding a mental model would improve the behavior. Here’s how it can be faked the lazy way:

  1. Ideally, the AI should keep track of the different locations where a dog has peed, or possibly just the last place.

  2. This would be taken into account by preventing other behaviors from moving to this area for a while.

  3. To fake this without a mental model, the peeing animation can be followed-up by walking away, which reduces the chances of the dog returning to this point.

In this case, adding a simple post-fix to the behavior makes all the other behaviors consistent and sensible.

Pooping with a Purpose

This bodily behavior can be handled very similarly than the peeing behavior, moving away from the location after defecating to make sure the following behaviors are consistent. However, this is a good example to show how to add personality to the behavior also.

In the existing assets, the pooping animation is portrayed in a very paranoid way, so the dog looks around frantically before actually pooping. Splitting that animation into two is a good way to make the most of the low animation budget. However, adding a few more animations into the pooping behavior can also help emphasize this paranoid feeling, for example by sneaking up slowly to a location to poop.

Behavior Tree

Figure 3: An overly complex behavior for defecating…

This tree is another sequence that relies on the parametric tree behaviors for moving and turning. The parallels below play animations and count for a certain amount of time. The pooping behavior is a call to animate that doesn’t loop, so it needs no timer.

Summary

Now obviously peeing and pooping behaviors aren’t easy to apply to all games… but honestly, there’s something to be learned here! You don’t always have to implement a mental model about things to portray them in behaviors. You can setup sequences to encode all these assumptions into the logic without requiring any other information.

By focusing on the outcome instead of the underlying concepts, you can often take shortcuts that will save you time in many cases. This is most probably the best bang-for-buck you’ll ever get in game AI. Keep that in mind!

Stay tuned next week for the full application code. In the meantime, feel free to post a comment below!

Discussion 2 Comments

anpd on November 15th, 2007

An interesting ideá would be to use Neural Networks and genetic algorithms. Make it so the dogs could mate and be a child of it´s parents. You could make so peeing and pooping would punish the dog and encurage it to pee and poop at different locations. So after a few generations the dog wouldn´t pee in the same place by itself.

lexilexmark on February 16th, 2011

I like the idea of Neural Nets in this case. Nodes for feelings and physical state could be plugged into the behaviour tree, i.e. Having weighted connections from feelings like tiredness to actions like move that control the speed in which the dog moves. Or using back propagation of some node outcomes to control higher level nodes, e.g. After pooping the dirt factor increases and the probability of initiating a cleaning action rises.

If you'd like to add a comment or question on this page, simply log-in to the site. You can create an account from the sign-up page if necessary... It takes less than a minute!