limbonaut / limboai

LimboAI - Behavior Trees and State Machines for Godot 4

Home Page:https://limboai.readthedocs.io/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Updating `BTSubTree.subtree` at runtime

onze opened this issue · comments

commented

My tree contains a BTSubTree that I want to load dynamically. In order to do this, I leave the subtree field unset, and a sibling custom node does this:

var target_subtree: BTSubtree = find_btask(
	bt_player.get_tree_instance(), 
	my_subtree_task_name
) as BTSubtree
# assert target_subtree != null
target_subtree.subtree = null
var new_btree := ... # roughly load('res://...') as BehaviorTree
target_subtree.subtree = new_btree

In the editor, before this runs, I get the following error:

my_file.gd:95 @ <anonymous lambda>(): Subtree root task is not valid.
  <C++ Error>    Condition "!subtree->get_root_task().is_valid()" is true.
  <C++ Source>   limboai/bt/tasks/decorators/bt_subtree.cpp:33 @ initialize()

And when my code above runs, the task doesn't update properly.

As a workaround, I tried creating a dummy btree and assign it to the BTSubTree, so that the error isn't raised and the subtree can be replaced. It works until I try to set subtree to the new BehaviorTree, which doesn't raise an issue and doesn't update it.

Is this supported?

There was a discussion some time ago: #58
It is currently not supported, as BTSubtree is designed to be a simple loader. However, you can do it dynamically, you just need to do it without BTSubtree, either by using a custom decorator, or by walking the tree and inserting your branch where you need it to be. Here's a simple example decorator (written from memory):

extends BTDecorator
## MyCustomBranch

func _setup() -> void:
    var bt: BehaviorTree = load("res://...")
    var my_branch: BTTask = bt.instantiate(agent, blackboard)
    add_child(my_branch)

func _tick(delta) -> Status:
    var child: BTTask = get_child(0)
    return child.execute(delta)

You may or may not want to isolate the blackboard scope for your subtree like this:

var new_scope := Blackboard.new()
new_scope.set_parent(blackboard)
var my_branch: BTTask = bt.instantiate(agent, new_scope)

If your branch utilizes blackboard plan system:

# Using new scope:
var new_scope: Blackboard = bt.blackboard_plan.create_blackboard(agent)
var my_branch: BTTask = bt.instantiate(agent, new_scope)

# Or not using new scope:
bt.blackboard_plan.populate_blackboard(blackboard, false, agent)
var my_branch: BTTask = bt.instantiate(agent, blackboard)
commented

Makes sense, thanks for the quick answer. I opened a PR to add a quick mention to this in BTSubtree's doc itself. Feel free to dismiss it if you feel like it's too much detail.