Ah, I see.
Regardless, the plugin is amazing. You did an amazing job with it. Hopefully someone can create a tutorial for how to use it. If not then I’ll look into making one. It really makes creating enemy bots so much easier. An example is right there
Someone should totally make a vocal scripting tutorial showing how to use them in a practical manner. We’d love that it’d help out a lot of people. I would myself if I had the time, I should’ve probably made one for the original plugin
Great work! This makes me excited to work on my NPCs. I had one idea. What if instead of jumping back to the same task when RUNNING is returned, it traversed through the tree again. task.start and task.end can fire depending on if another task returns RUNNING during the traversal.
Say an enemy NPC is in a wondering state and a player walks into their attack range. The NPC can’t detect any state changes from doing a tree traversal because it’s already running a task. The task itself has to listen for the state change which just seems unnecessary.
I’m new to behavior trees so I’m probably missing something.
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
Video Footage: Imgur: The magic of the Internet
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
-
Here’s an example of using a Shared blackboard
Here’s an example of an Entity blackboard(default)
Added new composite node, While
- 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
Here’s how you would do that
-
-
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
Misc/QOL Updates and Fixes
-
The add node window is now properly clamped to the extents of your screen
-
Fixed issue where repeater node didn’t function properly
-
You can no longer edit trees while a game is running
-
Index resorting happens automatically when you add a new node, before it only happened when you released drag
-
Nodes with more than one parameter are now scaled up a bit more by default
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!
Fixed issue where tree node didn’t always work properly
Is there an open-sourced .rbxl file I could look at to see how this works in a game?
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?
Also, I second the usefulness of an example game!
Can You Do A “How To Use” Video
Yes, this would be really useful.
Good catch thanks! Updated the module on github.
As for a video/example game, I plan on doing that if someone else doesn’t do it before me. May take awhile though as I’m pretty busy these days
Wow. This deserves to be recognised. This’ll probably make NPC making / behavior building like, I dunno, a BAZILLION times easier.
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.