Good question, I’m also not super experienced with behavior trees but this is what I think I know.
From what I understand, the reentry point of a running tree is an implementation detail that seems to vary, and oniich and tyridge decided to implement it this way. As you described, it seems like the main advantage of reevaluating the entire tree is that it allows higher priority tasks to interrupt the current running task, and the main disadvantage is that tree retraversal could be expensive. I’m also not exactly sure how previous tasks would behave when being iterated over on the way back to the current running task.
I’m sure this could be implemented as an optional mode, but I think in the case of the example you described it would not be necessary. I’d design any idle tasks to always return success, and reserve the running state for tasks that run over a defined bounded period of time.
Incredible plugin, I’ve used the extremely old behaviour tree module originally ported by oniich and I got to say, using this is a major step up in terms of workflow and enjoyment.
A new AI I've been working on using this BTrees, quite simple but effective
If I may make a few suggestions, there are a few minor workflow problems that I’ve encountered with this plugin that I haven’t encountered with any other plugin and I’m unsure what causes it. When trying to select/edit a BTree folder in a relatively large place, my studio will hard freeze for a good 10 - 25 seconds before it becomes responsive again (which then will finally open up the editor). Deselecting the folder will do another 10 - 25 seconds of freezing before it returns back to normal Roblox studio view. My computer is quite at the top end so I doubt that is the issue.
Due to the issue above, I’ve been using the Btrees plugin in an empty baseplate and then copying over the tree to the main place once I am finished. This then poses another problem where the tree tag doesn’t carry over and then the plugin doesn’t recognize that it’s a tree (until I add back the tag manually). Would be incredible if the plugin would recognize the btree folders automatically in different places (or has a way to easily add the tags itself!). The only way for the plugin to recognize it is a tree again is to restart studio or to keep using the original place it came from.
Yikes, sorry about the freezing. This is caused by this issue from the 2.0 thread:
I left that in despite the issue because I couldn’t find a better solution around the problem. This would be worth writing a studio bug report for. For now, you can comment out that line if you want to work on your main place, but accidentally pressing undo can delete or revert your trees.
Hey, I just want to point some troubles I had while updating from BT2 to BT3. Just to be clear, I am not using the plugin, I am creating the nodes using the BT3 module.
I ran into a problem while using the nodes table like this:
newSequence = BehaviorTree3.Sequence({
nodes = {
-- Node 1
node1 = BehaviorTree3.Task({
run = function(object)
--Code
end
}),
-- Node 2
node2 = BehaviorTree3.Task({
run = function(object)
-- Code
end
})
}
})
Since the module is using:
assert(#node.params.nodes >= 1, "Can't process tree; sequence composite node has no children")
I am getting a minor error but it can be easily bypassed by not giving nodes a specific key in the table like this:
-- Node 1
BehaviorTree3.Task({
run = function(object)
--Code
end
})
And now the reason why I am posting is that I’m getting an error due to “task leaf node has no linked task module”, and to be honest I have no idea why is that. Is there something I’m missing?
btw, great update, I really like the new task status.
The nodes table needs to be a list, the way you had it set up in your first snippet was a dictionary. This would be the correct setup:
newSequence = BehaviorTree3.Sequence({
nodes = {
-- Node 1
BehaviorTree3.Task({
run = function(object)
--Code
end
}),
-- Node 2
BehaviorTree3.Task({
run = function(object)
-- Code
end
})
}
})
Good catch on that linked module error, that was an oversight with assuming you’re using the plugin editor. I’ve fixed the module so the Task constructor matches the documentation.
Hey everyone, I spent the last couple weeks making a big update to the plugin that adds new content and fixes some issues.
If you update anything you should update everything, including the BehaviorTreeCreator and BehaviorTree3 modules otherwise things may not work properly!
Here’s a rather large list of all the changes
Added live tree debugging to assist in figuring out what your tree is doing !
Only works in studio
To use , join a game and click on the debug button under the plugin buttons
This will open a window of trees which have ran at least once.
Each debuggable tree in the window will have its name set to the object’s table name, unless the object has an index called name or Name, which it will then use
To debug a tree, click on the debug button.
This will open up the tree similar to the editor, only you can’t edit anything
The currently running task/tree will be higlighted in yellow, with a yellow path dictating how it got there from the root
Changed how trees are created and ran
Trees are now decoupled from instances, acting more as pure functions not necessarily attached to any specific object - kind of like module scripts. This is how behavior trees are typically implemented in the game industry because it’s more sensible and has less performance + memory overhead
Trees are created with BehaviorTreeCreator:Create(treefolder). If a tree is already created for that folder it will return that tree
Trees are now run using Tree:Run(object,…)
object must be a table
Here’s an example of how you’d now set up and run a tree
local BehaviorTreeCreator = require(game.ReplicatedStorage.BehaviorTreeCreator)
local IdleTree = BehaviorTreeCreator:Create(game.ReplicatedStorage.Idle)
local Object = {
Name = "Bob",
model = workspace.Bob,
human = workspace.Bob.Humanoid,
}
-- In some update function
IdleTree:run(object)
--
Added new tree method, Tree:Abort()
Calls finish() on the running task of the tree, and sets the tree index back to 1
Should be used if you want to cancel out of a tree to swap to another(for instance in the case of a state change if your tree is driven by states)
Added Blackboards (Partially inspired by UE4s blackboard system)
Literally just a table that trees can read from and write to via tasks
Injected into the object passed into a tree via Tree:Run(object), if it doesn’t already exist
Commonly used to see if a value is set or not in order to dictate the flow of relevant logic in a tree
Shared Blackboards are also a thing. You shouldn’t write to these in a tree, but they are useful for reading shared states(global stuff, player stuff, are the lights off in a scene)
To register a Shared Blackboard use BehaviorTreeCreator:RegisterSharedBlackboard(name,table)
function task.run(obj)
local Blackboard = obj.Blackboard -- Use this if you want to read and write values which your "Blackboard" nodes can reference
Added new Leaf node, Blackboard Query
These are used to perform fast and simple comparisons on a specific key in a blackboard
For instance, if you wanted a sequence to execute only if the entity’s “LowHealth” state was set to true, or if a world’s “NightTime” state was set to true(Shared Blackboard)
You can do this with tasks , but it’s a bit faster if you only need to perform a simple boolean or nil check
You can only read from a blackboard using this node.
Behavior Trees aren’t meant to be visual scripting - just a way to carry out plans
Parameters:
Board: string that defaults to Entity if no value is specified.
Entity will reference the object’s blackboard passed into the tree via tree:Run(object)
If a value is given, say “WorldStates”, it will attempt to grab the Shared Blackboard to use with the same name. You can register these via BehaviorTreeCreator:RegisterSharedBlackboard(name,table)
Key: the string index of the key you’re trying to query(for instance, “LowHealth”)
Value: string which specifies what kind of query you’re trying to perform.
You can choose true,false,set,or unset to perform boolean/nil checks. Alternatively you can specify a string of your choice to perform a string comparison
Only accepts two children, a condition(1st child), and an action(2nd child)
Repeats until either
condition returns fail, wherein the node itself returns fail
MaxAttempts is reached, wherein the node itself returns fail
action returns success, wherein the node itself returns success
Used for processing stacks of items. Not sure if there are any other use cases
Say you want an NPC to create a stack of nearby doors,
then try to enter each door until there are no doors left to try,
or the NPC got through a door successfully.
If the NPC got through a door successfully, the node would return success. Otherwise, if there were no doors that were able to be entered, the node will return fail
This makes it much easier to do behavior on a set of objects without having to hard code it all within a task, which can get way uglier especially when you have to introduce fallback conditions.
Added comment nodes!
These serve no functional purpose but allow you to comment areas in your tree for organization or mnemonic purposes
To edit a comment click the edit button on top of the comment node
Two other toggle buttons are TextScaled and Text X Alignment
I’m not even gonna lie to myself, This looks like an actual software outside of Roblox, Which is how incredible it looks. Might as well try it out once in a while. Looking forward to using this!
I noticed that another breaking change is that a folder must be specified if the tree is run in studio? I think this might just be an oversight related to the new debugger functionality? On line 446 of BehaviorTree3.lua, self.folder is referenced only if IsStudio is true and it is breaking some example code from BehaviorTree3TS. I believe the check above should be checking if IsStudio and self.folder?
For any of you who grabbed the BehaviorTree3 module directly from the plugin folder in studio instead of the github, the module was actually named BehaviorTree4 there , so BehaviorTreeCreator will fail because it’s attempting to index “BehaviorTree3”.
Small issue, just rename the module in that case back to BehaviorTree3. @Defaultio should be able to update the plugin when he gets the time
If anyone uses Rojo and wants to sync their data up externally, I’m currently doing the following. Granted, it’s not the smoothest experience, but it’s better than nothing. In short, I use remodel to read the place file that contains the behavior trees and then write an .rbxmx file.
Create a new place file that contains your behavior tree folders in a folder named Trees under ServerStorage
Save the place as an .rbxlx file (the XML format)
Write a remodel script to grab the Trees folder and write an .rbxmx file whenever needed:
local game = remodel.readPlaceFile("YOUR_PLACE_FILE.rbxlx")
local trees = game:GetService("ServerStorage").Trees
remodel.writeModelFile(trees, "./some/path/for/rojo/trees.rbxmx")
Point that trees.rbxmx file into your game within your Rojo configuration
Run the above script whenever you want to write the file remodel your_script.lua
Now you can freely open up the place file and edit it however you like. Whenever you want to sync it into your actual game with Rojo running, just save the place file and then run the remodel script.
Bonus points if you set up some sort of file watcher on the place file and automatically run the remodel script when the place file is saved.
Alternatively, if you don’t want to use remodel, you could just right-click the Trees folder and save it as an .rbxmx file in your desired location, but this feels too manual to me.
Very interesting plugin. Will most certainly be incorporating this into a future project.
Not sure if anyone else has reported this but there are some slight issues with Team Create and using the plugin where deleting a node will replicate immediately to other team create members but creating a new node won’t replicate until the other clients either disable and reenable the plugin or they leave and reopen the place.
Is there any possibility of implementing a force refresh button/have the BTree refresh the node data each time the root is opened? I really don’t care about having true live syncing but being able to work on a BTree and then letting another team create member work on it after I’m done without having them close/reopen studio and/or disable/reenable the plugin would be life changing.
This plugin has been a huge quality of life when creating AI. Though I do have one question, Is there any way to recover a tree if you accidently delete it? Some times I muscle memory press the key delete when trying to delete a node and end up deleting the whole tree. From what I can tell hitting undo or even trying to make duplicates of a tree all seem unsuccessful when trying to access them. If there is a way, it would be a huge life savor for me haha.