There are many ways to build AI with hierarchical logic, but increasingly game developers seem to be using the similar techniques. Here are a few tips to get you on the right track.
1) Make the Hierarchies Modular Too
Your AI engine becomes much more effective if it can treat any behavior in the same way, regardless of whether it’s a hierarchy or not. If the child behaviors are stored inside their parents, the tree remains hidden and the interface stays the same. This helps the implementation of the central scheduler remain very simple.
2) Support A Hierarchy of Behaviors Running in Parallel
It should be possible for parent behaviors to be running at the same time as their children. This provides ways to control the hierarchy from the top, and monitor it for problems. Not all behaviors will use this parallel execution, but if the functionality is not there, the AI becomes a simple state-machine. (It’s not necessarily a bad thing, just be aware of its limitations.)
3) Provide Common Behaviors for Levels in the Hierarchy
If you look at the hierarchy as a tree structure, the behaviors that have children can be seen as branches. To create AI logic, designers will spend a lot of time with these plugging lower-level behaviors together (the leaves in the tree).
A good way to make this content creation easier is to provide common behaviors for these levels. For example:
Sequence: Runs a fixed set of child behaviors one after the other.
Queue: Like a sequence, but stores requests for running new behaviors at runtime.
Parallel: Runs the child behaviors all at the same time.
Selector: Picks one of the child behaviors to run, e.g. using priorities or probabilities.
Such behaviors make it much easier for the designers to create AI logic, but you can help them even more…
4) Make These Primitives as Flexible as Possible
To help your designers combine these behaviors like building blocks, you should make sure your sequences (and selectors, etc.) work in the most generic way possible. Don’t impose limitations on what kind of behaviors they can contain. Reduce the assumptions of what these primitives require from their children; all that should be required is for the child to be any other behavior.
Also, most of these primitives have an internal behavior that can be tweaked by settings (e.g. what to do in a sequence if a child behavior fails, or how many children the parallel must wait for before finishing, etc.). Exposing the settings for these behaviors gives much more power to the designers.
5) Keep Trees as Small as Possible
Given such powerful primitives to build hierarchical logic, it’s easy to get carried away building large trees. However, it’s best to keep things at a manageable size and split up large trees where possible.
Just as long programs are hard to maintain, the same goes for hierarchical behaviors. Try to keep the branching down to a minimum (2-4 children at each level in the hierarchy), and limit the depth of the common tree to 1 or 2. This makes for well factored behaviors, which are easier to create, debug and re-use.