Intro
Behavior trees are super handy tools, usually used to help organize and design complex AI behavior flow. If you don’t understand behavior trees, none of the rest of this post will make any sense. Here’s a great article explaining what they are, how they work, and why they’re useful for AI development:
This is an extension of the awesome work by @tyridge77 and @oniich_n. Read their posts to get fully caught up to speed:
The BehaviorTrees2 module and tyridge’s node editor are awesome, but I ran into some limitations that I wanted to fix, so I started forking. Three days later, the changes are substantial enough that tyridge and I think this is worth its own post.
Note: Trees created with the BTrees Editor 2.0 are not compatible with the BTrees Editor 3.0, and BehaviorTrees3 has slightly different constructor API than BehaviorTrees2.
Functional Overview
BehaviorTrees3 is the module that compiles and runs behavior trees in the game. If you wish, you could use it directly to construct behavior trees manually through code by setting up a bunch of tables, but this isn’t easy or intuitive.
BTrees Visual Editor v3.0 is a plugin that you can use to visually set up behavior trees in a node based graphical environment. It’s a lot more intuitive and fun than writing out tables! It represents behavior trees with folders instances, which have a bunch of other instances in them to hold the tree data. Don’t mess with those descendant instances if you want to stay out of trouble.
BehaviorTreeCreator is a module that is the bridge between the visual editor and the BehaviorTrees3 module. Pass it a Tree folder that you made with the plugin, and it will give you a tree object that you can :run()
.
Main Takeaways
Check it out! Curvey lines! Node paremeters! Repeat node! Tree node! The repeat node can repeat its child actions. The tree node can be used to link to another tree, and will pass the status of that tree. Tree reusability will be super handy to reuse logic for lots of NPC classes with similar AI.
All of these trees, which previously did not run as expected because of how Decorators would only work when directly parented to a Task, are now all valid and work as expected:
Noteable QoL improvements:
- Super easy to add new node types to the editor if you make a local fork of the plugin. Nodes are automatically added to the help menu + add node menu.
- Root nodes are explicit nodes in the editor, so you can change the tree root without needing to destroy a bunch of nodes until the autoselected root happens to be the one you want. You can add multiple root nodes, but only the first will be the real active root. The other root nodes will be grayscaled.
- Dragging a node will also move all of its descendants as a group.
- After dragging a node, it and its siblings index order is automatically resorted based on X position. This alleviates a common design failure mode in which you don’t realize that the index order isn’t what you expected based on the visual graph.
Changes / Improvements
This is a mostly comprehensive list all the updates I’ve made:
BTrees Visual Editor 3.0
- Added NodeInfo module to make adding node types to the editor easier. This does not include actual node functionality; this must be implemented in BehaviorTrees3.
- Support for nodes with string/number/boolean parameters
- Explicit root node
- Nodes are colored by category (composite/decorator/leaf/root) rather than by individual node type. I think this is easier to read at a glance.
- Dragging will drag descendants
- Automatic index ordering after dragging
- Behavior tree folders can be created anywhere, instead of locked to a single directory for all of them. When you click the Create toolbar button, it will create a tree in your current selection.
- The node info in the help window is dynamically populated from the list of node types in the NodeInfo module.
- Random weight inputs will round up decimal inputs, as these aren’t supported with the random weighting implementation
- Pretty bezier curves
BehaviorTrees3
- Previously, decorators could only be adorned directly to tasks, and they would have no function if adorned anywhere else. They now can be put anywhere or even chained together, and will work as expected. I changed the way decorators work internally, but preserved the pretty clever and efficient tree traversal system that BehaviorTrees2 already had, so it should still be just as fast.
- Trees return their result when you call :run()
- Added Repeat node
- Added Tree node, which will process and pass a result from another tree.
- Small refactor in the ProcessNode algorithm to make it easier to read and more intuitive to add new node types. Mainly, consolidated the node traversal nested loops which appeared in a few places into a single interateNodes() iterator, and added a addNode() function.
- Success/fail nodes can act as a standalone leaf if left dangling
- Changed success/fail/running node states to number enums instead of strings, so the tree doesn’t have to do string comparisons. Should be a tiny bit faster.
- Changed tasks to report their status by returning a status number enum, instead of calling a function on self
- Slightly changed node constructors
- Added better assertions to tell you if you try to construct an invalid tree
- Changed “Task”/“Selector” language to more conventional “Leaf”/“Composite”
- Improved commenting/documentation
BehaviorTreeCreator
- Added TreeCreator:SetTreeID(treeId, treeFolder), to associate trees with Tree nodes based on the TreeID string parameter.
- Changed TreeCreator:Create parameters from obj, treeFolder to treeFolder, obj. Made obj optional.
- Trees are created and cached from the tree folder directly, rather than a string treeindex.
- Added commenting/documentation
That’s it! Let me know if you have feedback for UX improvements, new nodes with common use cases, etc. Also, this isn’t fully battle tested yet, so let me know if you find any bugs or if you find yourself with a tree that isn’t behaving as you expect.
Edit Dec 2 2020: @tyridge77 just made some big updates, see below. Note that if you move to this version, there is slightly different API; tree:setObject() is no longer used, and the object is run with the object as a parameter. BehaviorTrees3 + BTrees Visual Editor v3.0 - #23 by tyridge77