BehaviorTrees3 + BTrees Visual Editor v3.0

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.

4 Likes

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.

3 Likes

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.

1 Like

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.

2 Likes

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

39 Likes

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!

1 Like

Fixed issue where tree node didn’t always work properly

1 Like

Is there an open-sourced .rbxl file I could look at to see how this works in a game?

4 Likes

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

8 Likes

Yes, this would be really useful.

5 Likes

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

6 Likes

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

2 Likes

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.

  1. Create a new place file that contains your behavior tree folders in a folder named Trees under ServerStorage
  2. Save the place as an .rbxlx file (the XML format)
  3. 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")
    
  4. Point that trees.rbxmx file into your game within your Rojo configuration
  5. 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.

7 Likes

Not my video, but for those who wanted a video I found one!

12 Likes

what is an blackboard, invert anyway?

2 Likes

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.

Thanks again Defaultio!

1 Like

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.

1 Like