After some research and experimentation I have begun creating a customized behavior tree implementation that is integrated into the core of my framework. Each entity of the framework can have behavior trees and will look at environment, sensory, and memory information and drive the entity through actions which are things like move, attack, cast, use, consume, and so on. There will likely be many actions and the framework will support adding additional custom actions, conditions, or composite behaviors to create new operations as needed.
First, if you don’t know what behavior trees are, they have been prominent in game AI over the last several years and are utilized in a number of high profile AAA titles. I found one of the definitive resources by Alex Champanard at AiGameDev.com. This requires you to sign up, but it is free and worth it with a great slide presentation. The site has lots of great AI info on it anyway so it will be a good resource in your virtual bookshelf. I am not going to go into all the details of behavior trees because the link above and others scattered around the internet provide good coverage of the topic. Just Google it!
I have chosen to implement a pretty standard behavior tree framework, however there are a couple of things that make my implementation a little more involved than the standard approach and everything I am doing is a large experiment so we will see how it comes out.
The first major extension is that I am making the definition of the behavior trees data driven from my configuration files which are basically JSON on steroids with support for inheritance and embeddable/linkable configurations that both help with tweaking/reuse etc. This configuration system is used throughout the framework so you can easily define an entire character through it from stats, contents, ai, dialog, whatever and generate it procedurally.
The two best aspects of this configuration system are the ability to simply extend an existing configuration and change a few properties to make something different and to simply specify the path (absolute or relative) to another configuration. Combining these two features allows lots of reuse and the ability to easily adjust existing content that may have lots of settings.
Additionally, within this configuration system, I am supporting an expression language that allows variables and engine data to be used within the element and behavior definitions. This makes it easy to specify information to behaviors that only exists at runtime.
Here is an example configuration for a skeleton:
Skeleton configuration |
Attack behavior configuration |
The first file is the main one which is associated with the skeleton prefab. It has stats that extend from some basic human stats and use a simplistic default behavior that is shown in the second file which makes the skeleton simply wander around and then pursue and attack the player when nearby.
If you look carefully at the behavior file you will see strings preceded by a $ that point to other data and strings preceded by a # that point to entities and engine data related to them. Also data is defined starting at the scene and entity level and at any composite behavior down the behavior tree branches as needed. This provides a simplistic run time binding while having hierarchical inheritance based data which acts like long and short term memory. This is similar to the black board system in behavior trees.
All of this is meant to be kept very simple with the idea being that any detailed logic or calculations are done within pre-built parameter driven reusable actions or conditions or if there simply isn’t one that meets the needs a custom one (hopefully also reusable) can be built and used in the same manner.
Here is a screenshot of the skeleton in action:
The skeleton with the behavior in action |
The debug window on the right shows the entity data and the behavior tree running. Red indicates a failed behavior, green successful, white running, and gray ready (not executed yet). The labels are shown followed by the behavior type in parenthesis. State information is shown on the right. The entire window updates in real time showing data and behavior as it is changing.
The behavior tree shown is very simple and just an experiment. A real behavior tree would likely be much larger. The current state shown indicates the ‘follow’ sequence has failed at the ‘pursue’ step since the player is not in range and the ‘wander’ sequence has been successful up to the ‘rest’ step which is the current running action.
So this is where I am at now. There is much more to be done and I will likely do a follow-up post once I develop a more sophisticated behavior with some actual combat mechanics being executed. This will hopefully be an actual video which people have been asking for!