Compare commits

..

10 Commits

Author SHA1 Message Date
Serhii Snitsaruk e55611a1a9
Bump doc version 2024-05-12 17:34:12 +02:00
Serhii Snitsaruk ba467ad62c
Merge pull request #102 from limbonaut/btplayer-agent-property
Allow specifying agent in `BTPlayer` node via inspector
2024-05-12 17:29:05 +02:00
Serhii Snitsaruk e5d04b9eda
Doc: Update doc pages and examples 2024-05-02 19:38:05 +02:00
Serhii Snitsaruk e36ea6d3e6
Better error handling in BTState, BTPlayer & BehaviorTree 2024-05-02 14:10:29 +02:00
Serhii Snitsaruk 803da63fa8
Doc: Update class docs 2024-05-02 13:39:19 +02:00
Serhii Snitsaruk 84c89356a6
Print error if agent node is set after initialization 2024-05-02 12:49:32 +02:00
Serhii Snitsaruk a2dae24b99
Rename BTPlayer's agent => agent_node 2024-05-02 12:11:59 +02:00
Serhii Snitsaruk af23272e3d
Fix unnamed arguments in method bindings 2024-05-02 01:27:14 +02:00
Serhii Snitsaruk 506d8aa967
Use `scene_root` with `BBParam` 2024-05-01 23:39:09 +02:00
Serhii Snitsaruk 5dff2e537b
Add `agent` parameter to `BTPlayer` to propagate upon `BehaviorTree` initialization, and add `scene_root` property to `BTTask`
`scene_root` is useful to resolve exported NodePath properties in `BTTask` instances (and for BBNode parameters).
2024-05-01 23:20:17 +02:00
57 changed files with 302 additions and 194 deletions

View File

@ -20,9 +20,9 @@
#include <godot_cpp/classes/node.hpp>
#endif // LIMBOAI_GDEXTENSION
Variant BBNode::get_value(Object *p_agent, const Ref<Blackboard> &p_blackboard, const Variant &p_default) {
ERR_FAIL_COND_V(p_agent == nullptr, Variant());
ERR_FAIL_COND_V(!p_blackboard.is_valid(), Variant());
Variant BBNode::get_value(Node *p_scene_root, const Ref<Blackboard> &p_blackboard, const Variant &p_default) {
ERR_FAIL_NULL_V_MSG(p_scene_root, Variant(), "BBNode: get_value() failed - scene_root is null.");
ERR_FAIL_NULL_V_MSG(p_blackboard, Variant(), "BBNode: get_value() failed - blackboard is null.");
Variant val;
if (get_value_source() == SAVED_VALUE) {
@ -32,9 +32,7 @@ Variant BBNode::get_value(Object *p_agent, const Ref<Blackboard> &p_blackboard,
}
if (val.get_type() == Variant::NODE_PATH) {
Node *agent = Object::cast_to<Node>(p_agent);
ERR_FAIL_COND_V_MSG(agent == nullptr, Variant(), "BBNode: p_agent must be a Node.");
return agent->get_node_or_null(val);
return p_scene_root->get_node_or_null(val);
} else {
Object *obj = val;
if (unlikely(obj == nullptr && val.get_type() != Variant::NIL)) {

View File

@ -21,7 +21,7 @@ protected:
virtual Variant::Type get_type() const override { return Variant::NODE_PATH; }
public:
virtual Variant get_value(Object *p_agent, const Ref<Blackboard> &p_blackboard, const Variant &p_default = Variant()) override;
virtual Variant get_value(Node *p_scene_root, const Ref<Blackboard> &p_blackboard, const Variant &p_default = Variant()) override;
};
#endif // BB_NODE_H

View File

@ -75,7 +75,7 @@ String BBParam::_to_string() {
}
}
Variant BBParam::get_value(Object *p_agent, const Ref<Blackboard> &p_blackboard, const Variant &p_default) {
Variant BBParam::get_value(Node *p_scene_root, const Ref<Blackboard> &p_blackboard, const Variant &p_default) {
ERR_FAIL_COND_V(!p_blackboard.is_valid(), p_default);
if (value_source == SAVED_VALUE) {
@ -105,7 +105,7 @@ void BBParam::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_variable", "variable_name"), &BBParam::set_variable);
ClassDB::bind_method(D_METHOD("get_variable"), &BBParam::get_variable);
ClassDB::bind_method(D_METHOD("get_type"), &BBParam::get_type);
ClassDB::bind_method(D_METHOD("get_value", "agent", "blackboard", "default"), &BBParam::get_value, Variant());
ClassDB::bind_method(D_METHOD("get_value", "scene_root", "blackboard", "default"), &BBParam::get_value, Variant());
ADD_PROPERTY(PropertyInfo(Variant::INT, "value_source", PROPERTY_HINT_ENUM, "Saved Value,Blackboard Var"), "set_value_source", "get_value_source");
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "variable", PROPERTY_HINT_NONE, "", 0), "set_variable", "get_variable");

View File

@ -66,7 +66,7 @@ public:
virtual String _to_string();
#endif
virtual Variant get_value(Object *p_agent, const Ref<Blackboard> &p_blackboard, const Variant &p_default = Variant());
virtual Variant get_value(Node *p_scene_root, const Ref<Blackboard> &p_blackboard, const Variant &p_default = Variant());
BBParam();
};

View File

@ -71,10 +71,12 @@ void BehaviorTree::copy_other(const Ref<BehaviorTree> &p_other) {
root_task = p_other->get_root_task();
}
Ref<BTTask> BehaviorTree::instantiate(Node *p_agent, const Ref<Blackboard> &p_blackboard) const {
Ref<BTTask> BehaviorTree::instantiate(Node *p_agent, const Ref<Blackboard> &p_blackboard, Node *p_scene_root) const {
ERR_FAIL_COND_V_MSG(root_task == nullptr, memnew(BTTask), "Trying to instance a behavior tree with no valid root task.");
ERR_FAIL_NULL_V_MSG(p_agent, memnew(BTTask), "Trying to instance a behavior tree with no valid agent.");
ERR_FAIL_NULL_V_MSG(p_scene_root, memnew(BTTask), "Trying to instance a behavior tree with no valid scene root.");
Ref<BTTask> inst = root_task->clone();
inst->initialize(p_agent, p_blackboard);
inst->initialize(p_agent, p_blackboard, p_scene_root);
return inst;
}
@ -92,7 +94,7 @@ void BehaviorTree::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_root_task"), &BehaviorTree::get_root_task);
ClassDB::bind_method(D_METHOD("clone"), &BehaviorTree::clone);
ClassDB::bind_method(D_METHOD("copy_other", "other"), &BehaviorTree::copy_other);
ClassDB::bind_method(D_METHOD("instantiate", "agent", "blackboard"), &BehaviorTree::instantiate);
ClassDB::bind_method(D_METHOD("instantiate", "agent", "blackboard", "scene_root"), &BehaviorTree::instantiate);
ADD_PROPERTY(PropertyInfo(Variant::STRING, "description", PROPERTY_HINT_MULTILINE_TEXT), "set_description", "get_description");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "blackboard_plan", PROPERTY_HINT_RESOURCE_TYPE, "BlackboardPlan", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT), "set_blackboard_plan", "get_blackboard_plan");

View File

@ -53,7 +53,7 @@ public:
Ref<BehaviorTree> clone() const;
void copy_other(const Ref<BehaviorTree> &p_other);
Ref<BTTask> instantiate(Node *p_agent, const Ref<Blackboard> &p_blackboard) const;
Ref<BTTask> instantiate(Node *p_agent, const Ref<Blackboard> &p_blackboard, Node *p_scene_root) const;
BehaviorTree();
~BehaviorTree();

View File

@ -50,9 +50,13 @@ void BTPlayer::_load_tree() {
}
#endif
tree_instance.unref();
ERR_FAIL_COND_MSG(!behavior_tree.is_valid(), "BTPlayer: Needs a valid behavior tree.");
ERR_FAIL_COND_MSG(!behavior_tree->get_root_task().is_valid(), "BTPlayer: Behavior tree has no valid root task.");
tree_instance = behavior_tree->instantiate(get_owner(), blackboard);
ERR_FAIL_COND_MSG(!behavior_tree.is_valid(), "BTPlayer: Initialization failed - needs a valid behavior tree.");
ERR_FAIL_COND_MSG(!behavior_tree->get_root_task().is_valid(), "BTPlayer: Initialization failed - behavior tree has no valid root task.");
Node *agent = GET_NODE(this, agent_node);
ERR_FAIL_NULL_MSG(agent, vformat("BTPlayer: Initialization failed - can't get agent with path '%s'.", agent_node));
Node *scene_root = get_owner();
ERR_FAIL_NULL_MSG(scene_root, "BTPlayer: Initialization failed - can't get scene root (make sure the BTPlayer's owner property is set).");
tree_instance = behavior_tree->instantiate(agent, blackboard, scene_root);
#ifdef DEBUG_ENABLED
if (IS_DEBUGGER_ACTIVE()) {
LimboDebugger::get_singleton()->register_bt_instance(tree_instance, get_path());
@ -89,6 +93,13 @@ void BTPlayer::set_behavior_tree(const Ref<BehaviorTree> &p_tree) {
}
}
void BTPlayer::set_agent_node(const NodePath &p_agent_node) {
agent_node = p_agent_node;
if (tree_instance.is_valid()) {
ERR_PRINT("BTPlayer: Agent node cannot be set after the behavior tree is instantiated. This change will not affect the behavior tree instance.");
}
}
void BTPlayer::set_blackboard_plan(const Ref<BlackboardPlan> &p_plan) {
blackboard_plan = p_plan;
_update_blackboard_plan();
@ -228,6 +239,8 @@ void BTPlayer::_notification(int p_notification) {
void BTPlayer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_behavior_tree", "behavior_tree"), &BTPlayer::set_behavior_tree);
ClassDB::bind_method(D_METHOD("get_behavior_tree"), &BTPlayer::get_behavior_tree);
ClassDB::bind_method(D_METHOD("set_agent_node", "agent_node"), &BTPlayer::set_agent_node);
ClassDB::bind_method(D_METHOD("get_agent_node"), &BTPlayer::get_agent_node);
ClassDB::bind_method(D_METHOD("set_update_mode", "update_mode"), &BTPlayer::set_update_mode);
ClassDB::bind_method(D_METHOD("get_update_mode"), &BTPlayer::get_update_mode);
ClassDB::bind_method(D_METHOD("set_active", "active"), &BTPlayer::set_active);
@ -245,6 +258,7 @@ void BTPlayer::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_tree_instance"), &BTPlayer::get_tree_instance);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "behavior_tree", PROPERTY_HINT_RESOURCE_TYPE, "BehaviorTree"), "set_behavior_tree", "get_behavior_tree");
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "agent_node"), "set_agent_node", "get_agent_node");
ADD_PROPERTY(PropertyInfo(Variant::INT, "update_mode", PROPERTY_HINT_ENUM, "Idle,Physics,Manual"), "set_update_mode", "get_update_mode");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "active"), "set_active", "get_active");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "blackboard", PROPERTY_HINT_NONE, "Blackboard", 0), "set_blackboard", "get_blackboard");
@ -266,6 +280,7 @@ void BTPlayer::_bind_methods() {
BTPlayer::BTPlayer() {
blackboard = Ref<Blackboard>(memnew(Blackboard));
agent_node = LW_NAME(node_pp);
}
BTPlayer::~BTPlayer() {

View File

@ -37,6 +37,7 @@ public:
private:
Ref<BehaviorTree> behavior_tree;
NodePath agent_node;
Ref<BlackboardPlan> blackboard_plan;
UpdateMode update_mode = UpdateMode::PHYSICS;
bool active = true;
@ -57,6 +58,9 @@ public:
void set_behavior_tree(const Ref<BehaviorTree> &p_tree);
Ref<BehaviorTree> get_behavior_tree() const { return behavior_tree; };
void set_agent_node(const NodePath &p_agent_node);
NodePath get_agent_node() const { return agent_node; }
void set_blackboard_plan(const Ref<BlackboardPlan> &p_plan);
Ref<BlackboardPlan> get_blackboard_plan() const { return blackboard_plan; }

View File

@ -52,8 +52,9 @@ void BTState::_update_blackboard_plan() {
void BTState::_setup() {
LimboState::_setup();
ERR_FAIL_COND_MSG(behavior_tree.is_null(), "BTState: BehaviorTree is not assigned.");
// TODO: BBNode relies on agent to be scene owner, so if the user provides anything else, the behavior tree can break.
tree_instance = behavior_tree->instantiate(get_agent(), get_blackboard());
Node *scene_root = get_owner();
ERR_FAIL_NULL_MSG(scene_root, "BTState: Initialization failed - can't get scene root (make sure the BTState's owner property is set).");
tree_instance = behavior_tree->instantiate(get_agent(), get_blackboard(), scene_root);
#ifdef DEBUG_ENABLED
if (tree_instance.is_valid() && IS_DEBUGGER_ACTIVE()) {

View File

@ -57,7 +57,7 @@ BT::Status BTCheckVar::_tick(double p_delta) {
ERR_FAIL_COND_V_MSG(!get_blackboard()->has_var(variable), FAILURE, vformat("BTCheckVar: Blackboard variable doesn't exist: \"%s\". Returning FAILURE.", variable));
Variant left_value = get_blackboard()->get_var(variable, Variant());
Variant right_value = value->get_value(get_agent(), get_blackboard());
Variant right_value = value->get_value(get_scene_root(), get_blackboard());
return LimboUtility::get_singleton()->perform_check(check_type, left_value, right_value) ? SUCCESS : FAILURE;
}

View File

@ -26,7 +26,7 @@ BT::Status BTSetVar::_tick(double p_delta) {
ERR_FAIL_COND_V_MSG(!value.is_valid(), FAILURE, "BTSetVar: `value` is not set.");
Variant result;
Variant error_result = LW_NAME(error_value);
Variant right_value = value->get_value(get_agent(), get_blackboard(), error_result);
Variant right_value = value->get_value(get_scene_root(), get_blackboard(), error_result);
ERR_FAIL_COND_V_MSG(right_value == error_result, FAILURE, "BTSetVar: Failed to get parameter value. Returning FAILURE.");
if (operation == LimboUtility::OPERATION_NONE) {
result = right_value;

View File

@ -159,13 +159,15 @@ void BTTask::set_custom_name(const String &p_name) {
}
};
void BTTask::initialize(Node *p_agent, const Ref<Blackboard> &p_blackboard) {
ERR_FAIL_COND(p_agent == nullptr);
ERR_FAIL_COND(p_blackboard == nullptr);
void BTTask::initialize(Node *p_agent, const Ref<Blackboard> &p_blackboard, Node *p_scene_root) {
ERR_FAIL_NULL(p_agent);
ERR_FAIL_NULL(p_blackboard);
ERR_FAIL_NULL(p_scene_root);
data.agent = p_agent;
data.blackboard = p_blackboard;
data.scene_root = p_scene_root;
for (int i = 0; i < data.children.size(); i++) {
get_child(i)->initialize(p_agent, p_blackboard);
get_child(i)->initialize(p_agent, p_blackboard, p_scene_root);
}
VCALL_OR_NATIVE(_setup);
@ -378,7 +380,7 @@ void BTTask::_bind_methods() {
// Public Methods.
ClassDB::bind_method(D_METHOD("is_root"), &BTTask::is_root);
ClassDB::bind_method(D_METHOD("get_root"), &BTTask::get_root);
ClassDB::bind_method(D_METHOD("initialize", "agent", "blackboard"), &BTTask::initialize);
ClassDB::bind_method(D_METHOD("initialize", "agent", "blackboard", "scene_root"), &BTTask::initialize);
ClassDB::bind_method(D_METHOD("clone"), &BTTask::clone);
ClassDB::bind_method(D_METHOD("execute", "delta"), &BTTask::execute);
ClassDB::bind_method(D_METHOD("get_child", "idx"), &BTTask::get_child);
@ -399,6 +401,7 @@ void BTTask::_bind_methods() {
// Properties, setters and getters.
ClassDB::bind_method(D_METHOD("get_agent"), &BTTask::get_agent);
ClassDB::bind_method(D_METHOD("set_agent", "agent"), &BTTask::set_agent);
ClassDB::bind_method(D_METHOD("get_scene_root"), &BTTask::get_scene_root);
ClassDB::bind_method(D_METHOD("_get_children"), &BTTask::_get_children);
ClassDB::bind_method(D_METHOD("_set_children", "children"), &BTTask::_set_children);
ClassDB::bind_method(D_METHOD("get_blackboard"), &BTTask::get_blackboard);
@ -410,6 +413,7 @@ void BTTask::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::STRING, "custom_name"), "set_custom_name", "get_custom_name");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "agent", PROPERTY_HINT_RESOURCE_TYPE, "Node", PROPERTY_USAGE_NONE), "set_agent", "get_agent");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "scene_root", PROPERTY_HINT_NODE_TYPE, "Node", PROPERTY_USAGE_NONE), "", "get_scene_root");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "blackboard", PROPERTY_HINT_RESOURCE_TYPE, "Blackboard", PROPERTY_USAGE_NONE), "", "get_blackboard");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "children", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_children", "_get_children");
ADD_PROPERTY(PropertyInfo(Variant::INT, "status", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "", "get_status");

View File

@ -75,6 +75,7 @@ private:
int index = -1;
String custom_name;
Node *agent = nullptr;
Node *scene_root = nullptr;
Ref<Blackboard> blackboard;
BTTask *parent = nullptr;
Vector<Ref<BTTask>> children;
@ -116,6 +117,8 @@ public:
_FORCE_INLINE_ Node *get_agent() const { return data.agent; }
void set_agent(Node *p_agent) { data.agent = p_agent; }
_FORCE_INLINE_ Node *get_scene_root() const { return data.scene_root; }
void set_display_collapsed(bool p_display_collapsed);
bool is_displayed_collapsed() const;
@ -126,7 +129,7 @@ public:
Ref<BTTask> get_root() const;
virtual Ref<BTTask> clone() const;
virtual void initialize(Node *p_agent, const Ref<Blackboard> &p_blackboard);
virtual void initialize(Node *p_agent, const Ref<Blackboard> &p_blackboard, Node *p_scene_root);
virtual PackedStringArray get_configuration_warnings(); // ! Native version.
Status execute(double p_delta);

View File

@ -20,7 +20,7 @@ void BTNewScope::set_blackboard_plan(const Ref<BlackboardPlan> &p_plan) {
emit_changed();
}
void BTNewScope::initialize(Node *p_agent, const Ref<Blackboard> &p_blackboard) {
void BTNewScope::initialize(Node *p_agent, const Ref<Blackboard> &p_blackboard, Node *p_scene_root) {
ERR_FAIL_COND(p_agent == nullptr);
ERR_FAIL_COND(p_blackboard == nullptr);
@ -33,7 +33,7 @@ void BTNewScope::initialize(Node *p_agent, const Ref<Blackboard> &p_blackboard)
bb->set_parent(p_blackboard);
BTDecorator::initialize(p_agent, bb);
BTDecorator::initialize(p_agent, bb, p_scene_root);
}
BT::Status BTNewScope::_tick(double p_delta) {

View File

@ -34,7 +34,7 @@ protected:
virtual Status _tick(double p_delta) override;
public:
virtual void initialize(Node *p_agent, const Ref<Blackboard> &p_blackboard) override;
virtual void initialize(Node *p_agent, const Ref<Blackboard> &p_blackboard, Node *p_scene_root) override;
};
#endif // BT_NEW_SCOPE_H

View File

@ -44,14 +44,14 @@ String BTSubtree::_generate_name() {
return vformat("Subtree %s", s);
}
void BTSubtree::initialize(Node *p_agent, const Ref<Blackboard> &p_blackboard) {
void BTSubtree::initialize(Node *p_agent, const Ref<Blackboard> &p_blackboard, Node *p_scene_root) {
ERR_FAIL_COND_MSG(!subtree.is_valid(), "Subtree is not assigned.");
ERR_FAIL_COND_MSG(!subtree->get_root_task().is_valid(), "Subtree root task is not valid.");
ERR_FAIL_COND_MSG(get_child_count() != 0, "Subtree task shouldn't have children during initialization.");
add_child(subtree->get_root_task()->clone());
BTNewScope::initialize(p_agent, p_blackboard);
BTNewScope::initialize(p_agent, p_blackboard, p_scene_root);
}
BT::Status BTSubtree::_tick(double p_delta) {

View File

@ -35,7 +35,7 @@ public:
void set_subtree(const Ref<BehaviorTree> &p_value);
Ref<BehaviorTree> get_subtree() const { return subtree; }
virtual void initialize(Node *p_agent, const Ref<Blackboard> &p_blackboard) override;
virtual void initialize(Node *p_agent, const Ref<Blackboard> &p_blackboard, Node *p_scene_root) override;
virtual PackedStringArray get_configuration_warnings() override;
BTSubtree() = default;

View File

@ -62,7 +62,7 @@ String BTAwaitAnimation::_generate_name() {
void BTAwaitAnimation::_setup() {
setup_failed = true;
ERR_FAIL_COND_MSG(animation_player_param.is_null(), "BTAwaitAnimation: AnimationPlayer parameter is not set.");
animation_player = Object::cast_to<AnimationPlayer>(animation_player_param->get_value(get_agent(), get_blackboard()));
animation_player = Object::cast_to<AnimationPlayer>(animation_player_param->get_value(get_scene_root(), get_blackboard()));
ERR_FAIL_COND_MSG(animation_player == nullptr, "BTAwaitAnimation: Failed to get AnimationPlayer.");
ERR_FAIL_COND_MSG(animation_name == StringName(), "BTAwaitAnimation: Animation Name is not set.");
ERR_FAIL_COND_MSG(!animation_player->has_animation(animation_name), vformat("BTAwaitAnimation: Animation not found: %s", animation_name));

View File

@ -62,7 +62,7 @@ BT::Status BTCheckAgentProperty::_tick(double p_delta) {
Variant left_value = get_agent()->get(property);
#endif
Variant right_value = value->get_value(get_agent(), get_blackboard());
Variant right_value = value->get_value(get_scene_root(), get_blackboard());
return LimboUtility::get_singleton()->perform_check(check_type, left_value, right_value) ? SUCCESS : FAILURE;
}

View File

@ -44,7 +44,7 @@ String BTPauseAnimation::_generate_name() {
void BTPauseAnimation::_setup() {
setup_failed = true;
ERR_FAIL_COND_MSG(animation_player_param.is_null(), "BTPauseAnimation: AnimationPlayer parameter is not set.");
animation_player = Object::cast_to<AnimationPlayer>(animation_player_param->get_value(get_agent(), get_blackboard()));
animation_player = Object::cast_to<AnimationPlayer>(animation_player_param->get_value(get_scene_root(), get_blackboard()));
ERR_FAIL_COND_MSG(animation_player == nullptr, "BTPauseAnimation: Failed to get AnimationPlayer.");
setup_failed = false;
}

View File

@ -77,7 +77,7 @@ String BTPlayAnimation::_generate_name() {
void BTPlayAnimation::_setup() {
setup_failed = true;
ERR_FAIL_COND_MSG(animation_player_param.is_null(), "BTPlayAnimation: AnimationPlayer parameter is not set.");
animation_player = Object::cast_to<AnimationPlayer>(animation_player_param->get_value(get_agent(), get_blackboard()));
animation_player = Object::cast_to<AnimationPlayer>(animation_player_param->get_value(get_scene_root(), get_blackboard()));
ERR_FAIL_COND_MSG(animation_player == nullptr, "BTPlayAnimation: Failed to get AnimationPlayer.");
ERR_FAIL_COND_MSG(animation_name != StringName() && !animation_player->has_animation(animation_name), vformat("BTPlayAnimation: Animation not found: %s", animation_name));
if (animation_name == StringName() && await_completion > 0.0) {

View File

@ -55,7 +55,7 @@ BT::Status BTSetAgentProperty::_tick(double p_delta) {
Variant result;
StringName error_value = LW_NAME(error_value);
Variant right_value = value->get_value(get_agent(), get_blackboard(), error_value);
Variant right_value = value->get_value(get_scene_root(), get_blackboard(), error_value);
ERR_FAIL_COND_V_MSG(right_value == Variant(error_value), FAILURE, "BTSetAgentProperty: Couldn't get value of value-parameter.");
bool r_valid;
if (operation == LimboUtility::OPERATION_NONE) {

View File

@ -56,7 +56,7 @@ String BTStopAnimation::_generate_name() {
void BTStopAnimation::_setup() {
setup_failed = true;
ERR_FAIL_COND_MSG(animation_player_param.is_null(), "BTStopAnimation: AnimationPlayer parameter is not set.");
animation_player = Object::cast_to<AnimationPlayer>(animation_player_param->get_value(get_agent(), get_blackboard()));
animation_player = Object::cast_to<AnimationPlayer>(animation_player_param->get_value(get_scene_root(), get_blackboard()));
ERR_FAIL_COND_MSG(animation_player == nullptr, "BTStopAnimation: Failed to get AnimationPlayer.");
if (animation_name != StringName()) {
ERR_FAIL_COND_MSG(!animation_player->has_animation(animation_name), vformat("BTStopAnimation: Animation not found: %s", animation_name));

View File

@ -83,7 +83,7 @@ String BTCallMethod::_generate_name() {
BT::Status BTCallMethod::_tick(double p_delta) {
ERR_FAIL_COND_V_MSG(method == StringName(), FAILURE, "BTCallMethod: Method Name is not set.");
ERR_FAIL_COND_V_MSG(node_param.is_null(), FAILURE, "BTCallMethod: Node parameter is not set.");
Object *obj = node_param->get_value(get_agent(), get_blackboard());
Object *obj = node_param->get_value(get_scene_root(), get_blackboard());
ERR_FAIL_COND_V_MSG(obj == nullptr, FAILURE, "BTCallMethod: Failed to get object: " + node_param->to_string());
Variant result;
@ -101,7 +101,7 @@ BT::Status BTCallMethod::_tick(double p_delta) {
}
for (int i = 0; i < args.size(); i++) {
Ref<BBVariant> param = args[i];
call_args.push_back(param->get_value(get_agent(), get_blackboard()));
call_args.push_back(param->get_value(get_scene_root(), get_blackboard()));
argptrs[i + int(include_delta)] = &call_args[i];
}
}
@ -117,7 +117,7 @@ BT::Status BTCallMethod::_tick(double p_delta) {
}
for (int i = 0; i < args.size(); i++) {
Ref<BBVariant> param = args[i];
call_args.push_back(param->get_value(get_agent(), get_blackboard()));
call_args.push_back(param->get_value(get_scene_root(), get_blackboard()));
}
// TODO: Unsure how to detect call error, so we return SUCCESS for now...

View File

@ -107,7 +107,7 @@ String BTEvaluateExpression::_generate_name() {
BT::Status BTEvaluateExpression::_tick(double p_delta) {
ERR_FAIL_COND_V_MSG(expression_string.is_empty(), FAILURE, "BTEvaluateExpression: Expression String is not set.");
ERR_FAIL_COND_V_MSG(node_param.is_null(), FAILURE, "BTEvaluateExpression: Node parameter is not set.");
Object *obj = node_param->get_value(get_agent(), get_blackboard());
Object *obj = node_param->get_value(get_scene_root(), get_blackboard());
ERR_FAIL_COND_V_MSG(obj == nullptr, FAILURE, "BTEvaluateExpression: Failed to get object: " + node_param->to_string());
ERR_FAIL_COND_V_MSG(is_parsed != Error::OK, FAILURE, "BTEvaluateExpression: Failed to parse expression: " + expression.get_error_text());
@ -116,7 +116,7 @@ BT::Status BTEvaluateExpression::_tick(double p_delta) {
}
for (int i = 0; i < input_values.size(); ++i) {
const Ref<BBVariant> &bb_variant = input_values[i];
processed_input_values[i + int(input_include_delta)] = bb_variant->get_value(get_agent(), get_blackboard());
processed_input_values[i + int(input_include_delta)] = bb_variant->get_value(get_scene_root(), get_blackboard());
}
Variant result = expression.execute(processed_input_values, obj, false);

View File

@ -49,11 +49,11 @@ Methods
.. table::
:widths: auto
+-----------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------+
+-----------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------+
| :ref:`Variant.Type<enum_@GlobalScope_Variant.Type>` | :ref:`get_type<class_BBParam_method_get_type>` **(** **)** |const| |
+-----------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------+
| Variant | :ref:`get_value<class_BBParam_method_get_value>` **(** Object agent, :ref:`Blackboard<class_Blackboard>` blackboard, Variant default=null **)** |
+-----------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------+
+-----------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------+
| Variant | :ref:`get_value<class_BBParam_method_get_value>` **(** Node scene_root, :ref:`Blackboard<class_Blackboard>` blackboard, Variant default=null **)** |
+-----------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------+
.. rst-class:: classref-section-separator
@ -167,7 +167,7 @@ Returns the expected data type of the parameter.
.. rst-class:: classref-method
Variant **get_value** **(** Object agent, :ref:`Blackboard<class_Blackboard>` blackboard, Variant default=null **)**
Variant **get_value** **(** Node scene_root, :ref:`Blackboard<class_Blackboard>` blackboard, Variant default=null **)**
Returns the value of the parameter.

View File

@ -55,17 +55,17 @@ Methods
.. table::
:widths: auto
+-----------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------+
+-----------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+
| :ref:`BehaviorTree<class_BehaviorTree>` | :ref:`clone<class_BehaviorTree_method_clone>` **(** **)** |const| |
+-----------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------+
+-----------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`copy_other<class_BehaviorTree_method_copy_other>` **(** :ref:`BehaviorTree<class_BehaviorTree>` other **)** |
+-----------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------+
+-----------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+
| :ref:`BTTask<class_BTTask>` | :ref:`get_root_task<class_BehaviorTree_method_get_root_task>` **(** **)** |const| |
+-----------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------+
| :ref:`BTTask<class_BTTask>` | :ref:`instantiate<class_BehaviorTree_method_instantiate>` **(** Node agent, :ref:`Blackboard<class_Blackboard>` blackboard **)** |const| |
+-----------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------+
+-----------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+
| :ref:`BTTask<class_BTTask>` | :ref:`instantiate<class_BehaviorTree_method_instantiate>` **(** Node agent, :ref:`Blackboard<class_Blackboard>` blackboard, Node scene_root **)** |const| |
+-----------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`set_root_task<class_BehaviorTree_method_set_root_task>` **(** :ref:`BTTask<class_BTTask>` task **)** |
+-----------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------+
+-----------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+
.. rst-class:: classref-section-separator
@ -172,9 +172,9 @@ Returns the root task of the BehaviorTree resource.
.. rst-class:: classref-method
:ref:`BTTask<class_BTTask>` **instantiate** **(** Node agent, :ref:`Blackboard<class_Blackboard>` blackboard **)** |const|
:ref:`BTTask<class_BTTask>` **instantiate** **(** Node agent, :ref:`Blackboard<class_Blackboard>` blackboard, Node scene_root **)** |const|
Instantiates the Behavior Tree and returns the root :ref:`BTTask<class_BTTask>`.
Instantiates the behavior tree and returns the root :ref:`BTTask<class_BTTask>`. ``scene_root`` should be the root node of the scene that the Behavior Tree will be used in (e.g., the owner of the node that contains the behavior tree).
.. rst-class:: classref-item-separator

View File

@ -33,27 +33,27 @@ Methods
.. table::
:widths: auto
+-------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`bind_var_to_property<class_Blackboard_method_bind_var_to_property>` **(** StringName var_name, Object object, StringName property, bool create **)** |
+-------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+-------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`bind_var_to_property<class_Blackboard_method_bind_var_to_property>` **(** StringName var_name, Object object, StringName property, bool create=false **)** |
+-------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`erase_var<class_Blackboard_method_erase_var>` **(** StringName var_name **)** |
+-------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+-------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| :ref:`Blackboard<class_Blackboard>` | :ref:`get_parent<class_Blackboard_method_get_parent>` **(** **)** |const| |
+-------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+-------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Variant | :ref:`get_var<class_Blackboard_method_get_var>` **(** StringName var_name, Variant default=null, bool complain=true **)** |const| |
+-------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+-------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| bool | :ref:`has_var<class_Blackboard_method_has_var>` **(** StringName var_name **)** |const| |
+-------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`link_var<class_Blackboard_method_link_var>` **(** StringName var_name, :ref:`Blackboard<class_Blackboard>` target_blackboard, StringName target_var, bool create **)** |
+-------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+-------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`link_var<class_Blackboard_method_link_var>` **(** StringName var_name, :ref:`Blackboard<class_Blackboard>` target_blackboard, StringName target_var, bool create=false **)** |
+-------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`set_parent<class_Blackboard_method_set_parent>` **(** :ref:`Blackboard<class_Blackboard>` blackboard **)** |
+-------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+-------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`set_var<class_Blackboard_method_set_var>` **(** StringName var_name, Variant value **)** |
+-------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+-------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| :ref:`Blackboard<class_Blackboard>` | :ref:`top<class_Blackboard_method_top>` **(** **)** |const| |
+-------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+-------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`unbind_var<class_Blackboard_method_unbind_var>` **(** StringName var_name **)** |
+-------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+-------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
.. rst-class:: classref-section-separator
@ -68,7 +68,7 @@ Method Descriptions
.. rst-class:: classref-method
void **bind_var_to_property** **(** StringName var_name, Object object, StringName property, bool create **)**
void **bind_var_to_property** **(** StringName var_name, Object object, StringName property, bool create=false **)**
Establish a binding between a variable and the object's property specified by ``property`` and ``object``. Changes to the variable update the property, and vice versa. If ``create`` is ``true``, the variable will be created if it doesn't exist.
@ -128,7 +128,7 @@ Returns ``true`` if the Blackboard contains the ``var_name`` variable, including
.. rst-class:: classref-method
void **link_var** **(** StringName var_name, :ref:`Blackboard<class_Blackboard>` target_blackboard, StringName target_var, bool create **)**
void **link_var** **(** StringName var_name, :ref:`Blackboard<class_Blackboard>` target_blackboard, StringName target_var, bool create=false **)**
Links a variable to another Blackboard variable. If a variable is linked to another variable, their state will always be identical, and any change to one will be reflected in the other. If ``create`` is ``true``, the variable will be created if it doesn't exist.

View File

@ -19,7 +19,7 @@ Player of :ref:`BehaviorTree<class_BehaviorTree>` resources.
Description
-----------
BTPlayer node is used for the instantiation and playback of :ref:`BehaviorTree<class_BehaviorTree>` resources at runtime. During instantiation, the behavior tree instance is initialized with a reference to the agent and the :ref:`blackboard<class_BTPlayer_property_blackboard>`. Agent is the owner of the BTPlayer node (see :ref:`Node.owner<class_Node_member_owner>`).
**BTPlayer** node is used to instantiate and play :ref:`BehaviorTree<class_BehaviorTree>` resources at runtime. During initialization, the behavior tree instance is given references to the agent, the :ref:`blackboard<class_BTPlayer_property_blackboard>`, and the current scene root. The agent can be specified by the :ref:`agent_node<class_BTPlayer_property_agent_node>` property (defaults to the BTPlayer's parent node).
For an introduction to behavior trees, see :ref:`BehaviorTree<class_BehaviorTree>`.
@ -31,19 +31,21 @@ Properties
.. table::
:widths: auto
+---------------------------------------------+-------------------------------------------------------------------------+-----------+
+---------------------------------------------+-------------------------------------------------------------------------+--------------------+
| bool | :ref:`active<class_BTPlayer_property_active>` | ``true`` |
+---------------------------------------------+-------------------------------------------------------------------------+-----------+
+---------------------------------------------+-------------------------------------------------------------------------+--------------------+
| NodePath | :ref:`agent_node<class_BTPlayer_property_agent_node>` | ``NodePath("..")`` |
+---------------------------------------------+-------------------------------------------------------------------------+--------------------+
| :ref:`BehaviorTree<class_BehaviorTree>` | :ref:`behavior_tree<class_BTPlayer_property_behavior_tree>` | |
+---------------------------------------------+-------------------------------------------------------------------------+-----------+
+---------------------------------------------+-------------------------------------------------------------------------+--------------------+
| :ref:`Blackboard<class_Blackboard>` | :ref:`blackboard<class_BTPlayer_property_blackboard>` | |
+---------------------------------------------+-------------------------------------------------------------------------+-----------+
+---------------------------------------------+-------------------------------------------------------------------------+--------------------+
| :ref:`BlackboardPlan<class_BlackboardPlan>` | :ref:`blackboard_plan<class_BTPlayer_property_blackboard_plan>` | |
+---------------------------------------------+-------------------------------------------------------------------------+-----------+
+---------------------------------------------+-------------------------------------------------------------------------+--------------------+
| bool | :ref:`monitor_performance<class_BTPlayer_property_monitor_performance>` | ``false`` |
+---------------------------------------------+-------------------------------------------------------------------------+-----------+
+---------------------------------------------+-------------------------------------------------------------------------+--------------------+
| :ref:`UpdateMode<enum_BTPlayer_UpdateMode>` | :ref:`update_mode<class_BTPlayer_property_update_mode>` | ``1`` |
+---------------------------------------------+-------------------------------------------------------------------------+-----------+
+---------------------------------------------+-------------------------------------------------------------------------+--------------------+
.. rst-class:: classref-reftable-group
@ -161,6 +163,23 @@ If ``true``, the behavior tree will be executed during update.
----
.. _class_BTPlayer_property_agent_node:
.. rst-class:: classref-property
NodePath **agent_node** = ``NodePath("..")``
.. rst-class:: classref-property-setget
- void **set_agent_node** **(** NodePath value **)**
- NodePath **get_agent_node** **(** **)**
Path to the node that will be used as the agent. Setting it after instantiation will have no effect.
.. rst-class:: classref-item-separator
----
.. _class_BTPlayer_property_behavior_tree:
.. rst-class:: classref-property

View File

@ -46,6 +46,8 @@ Properties
+-------------------------------------+---------------------------------------------------------+--------+
| float | :ref:`elapsed_time<class_BTTask_property_elapsed_time>` | |
+-------------------------------------+---------------------------------------------------------+--------+
| Node | :ref:`scene_root<class_BTTask_property_scene_root>` | |
+-------------------------------------+---------------------------------------------------------+--------+
| :ref:`Status<enum_BT_Status>` | :ref:`status<class_BTTask_property_status>` | |
+-------------------------------------+---------------------------------------------------------+--------+
@ -57,59 +59,59 @@ Methods
.. table::
:widths: auto
+-------------------------------+---------------------------------------------------------------------------------------------------------------------------+
+-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`_enter<class_BTTask_private_method__enter>` **(** **)** |virtual| |
+-------------------------------+---------------------------------------------------------------------------------------------------------------------------+
+-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`_exit<class_BTTask_private_method__exit>` **(** **)** |virtual| |
+-------------------------------+---------------------------------------------------------------------------------------------------------------------------+
+-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| String | :ref:`_generate_name<class_BTTask_private_method__generate_name>` **(** **)** |virtual| |const| |
+-------------------------------+---------------------------------------------------------------------------------------------------------------------------+
+-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| PackedStringArray | :ref:`_get_configuration_warnings<class_BTTask_private_method__get_configuration_warnings>` **(** **)** |virtual| |const| |
+-------------------------------+---------------------------------------------------------------------------------------------------------------------------+
+-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`_setup<class_BTTask_private_method__setup>` **(** **)** |virtual| |
+-------------------------------+---------------------------------------------------------------------------------------------------------------------------+
+-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| :ref:`Status<enum_BT_Status>` | :ref:`_tick<class_BTTask_private_method__tick>` **(** float delta **)** |virtual| |
+-------------------------------+---------------------------------------------------------------------------------------------------------------------------+
+-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`abort<class_BTTask_method_abort>` **(** **)** |
+-------------------------------+---------------------------------------------------------------------------------------------------------------------------+
+-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`add_child<class_BTTask_method_add_child>` **(** :ref:`BTTask<class_BTTask>` task **)** |
+-------------------------------+---------------------------------------------------------------------------------------------------------------------------+
+-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`add_child_at_index<class_BTTask_method_add_child_at_index>` **(** :ref:`BTTask<class_BTTask>` task, int idx **)** |
+-------------------------------+---------------------------------------------------------------------------------------------------------------------------+
+-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| :ref:`BTTask<class_BTTask>` | :ref:`clone<class_BTTask_method_clone>` **(** **)** |const| |
+-------------------------------+---------------------------------------------------------------------------------------------------------------------------+
+-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| :ref:`Status<enum_BT_Status>` | :ref:`execute<class_BTTask_method_execute>` **(** float delta **)** |
+-------------------------------+---------------------------------------------------------------------------------------------------------------------------+
+-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| :ref:`BTTask<class_BTTask>` | :ref:`get_child<class_BTTask_method_get_child>` **(** int idx **)** |const| |
+-------------------------------+---------------------------------------------------------------------------------------------------------------------------+
+-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| int | :ref:`get_child_count<class_BTTask_method_get_child_count>` **(** **)** |const| |
+-------------------------------+---------------------------------------------------------------------------------------------------------------------------+
+-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| int | :ref:`get_child_count_excluding_comments<class_BTTask_method_get_child_count_excluding_comments>` **(** **)** |const| |
+-------------------------------+---------------------------------------------------------------------------------------------------------------------------+
+-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| int | :ref:`get_index<class_BTTask_method_get_index>` **(** **)** |const| |
+-------------------------------+---------------------------------------------------------------------------------------------------------------------------+
+-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| :ref:`BTTask<class_BTTask>` | :ref:`get_parent<class_BTTask_method_get_parent>` **(** **)** |const| |
+-------------------------------+---------------------------------------------------------------------------------------------------------------------------+
+-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| :ref:`BTTask<class_BTTask>` | :ref:`get_root<class_BTTask_method_get_root>` **(** **)** |const| |
+-------------------------------+---------------------------------------------------------------------------------------------------------------------------+
+-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| String | :ref:`get_task_name<class_BTTask_method_get_task_name>` **(** **)** |
+-------------------------------+---------------------------------------------------------------------------------------------------------------------------+
+-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| bool | :ref:`has_child<class_BTTask_method_has_child>` **(** :ref:`BTTask<class_BTTask>` task **)** |const| |
+-------------------------------+---------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`initialize<class_BTTask_method_initialize>` **(** Node agent, :ref:`Blackboard<class_Blackboard>` blackboard **)** |
+-------------------------------+---------------------------------------------------------------------------------------------------------------------------+
+-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`initialize<class_BTTask_method_initialize>` **(** Node agent, :ref:`Blackboard<class_Blackboard>` blackboard, Node scene_root **)** |
+-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| bool | :ref:`is_descendant_of<class_BTTask_method_is_descendant_of>` **(** :ref:`BTTask<class_BTTask>` task **)** |const| |
+-------------------------------+---------------------------------------------------------------------------------------------------------------------------+
+-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| bool | :ref:`is_root<class_BTTask_method_is_root>` **(** **)** |const| |
+-------------------------------+---------------------------------------------------------------------------------------------------------------------------+
+-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| :ref:`BTTask<class_BTTask>` | :ref:`next_sibling<class_BTTask_method_next_sibling>` **(** **)** |const| |
+-------------------------------+---------------------------------------------------------------------------------------------------------------------------+
+-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`print_tree<class_BTTask_method_print_tree>` **(** int initial_tabs=0 **)** |
+-------------------------------+---------------------------------------------------------------------------------------------------------------------------+
+-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`remove_child<class_BTTask_method_remove_child>` **(** :ref:`BTTask<class_BTTask>` task **)** |
+-------------------------------+---------------------------------------------------------------------------------------------------------------------------+
+-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`remove_child_at_index<class_BTTask_method_remove_child_at_index>` **(** int idx **)** |
+-------------------------------+---------------------------------------------------------------------------------------------------------------------------+
+-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
.. rst-class:: classref-section-separator
@ -131,7 +133,7 @@ Node **agent**
- void **set_agent** **(** Node value **)**
- Node **get_agent** **(** **)**
The agent is a contextual object for the task's :ref:`BehaviorTree<class_BehaviorTree>` instance. Usually, agent is the owner of the :ref:`BTPlayer<class_BTPlayer>` node containing the :ref:`BehaviorTree<class_BehaviorTree>` resource.
The agent is the contextual object for the :ref:`BehaviorTree<class_BehaviorTree>` instance. This is usually the parent of the :ref:`BTPlayer<class_BTPlayer>` node that utilizes the :ref:`BehaviorTree<class_BehaviorTree>` resource.
.. rst-class:: classref-item-separator
@ -190,6 +192,33 @@ Returns ``0`` when task is not ``RUNNING``.
----
.. _class_BTTask_property_scene_root:
.. rst-class:: classref-property
Node **scene_root**
.. rst-class:: classref-property-setget
- Node **get_scene_root** **(** **)**
Root node of the scene the behavior tree is used in (e.g., the owner of the :ref:`BTPlayer<class_BTPlayer>` node). Can be uses to retrieve ``NodePath`` references.
\ **Example:**\
::
extends BTAction
@export var node_path: NodePath
func _setup():
var node: Node = scene_root.get_node(node_path)
.. rst-class:: classref-item-separator
----
.. _class_BTTask_property_status:
.. rst-class:: classref-property
@ -457,11 +486,11 @@ Returns ``true`` if ``task`` is a child of this task.
.. rst-class:: classref-method
void **initialize** **(** Node agent, :ref:`Blackboard<class_Blackboard>` blackboard **)**
void **initialize** **(** Node agent, :ref:`Blackboard<class_Blackboard>` blackboard, Node scene_root **)**
Initilizes the task. Assigns :ref:`agent<class_BTTask_property_agent>` and :ref:`blackboard<class_BTTask_property_blackboard>`, and calls :ref:`_setup<class_BTTask_private_method__setup>` for the task and its children.
The method is called recursively for each child task.
The method is called recursively for each child task. ``scene_root`` should be the root node of the scene the behavior tree is used in (e.g., the owner of the node that contains the behavior tree).
.. rst-class:: classref-item-separator

View File

@ -3,9 +3,10 @@
Accessing nodes in the scene tree
=================================
There are several ways to access nodes in the agent's scene tree.
There are several ways to access nodes in the agent's scene tree from a :ref:`BTTask<class_BTTask>`.
**🛈 Note:** Agent is the owner of :ref:`BTPlayer<class_BTPlayer>` node.
**🛈 Note:** The root node of the agent's scene tree can be accessed with the
:ref:`scene_root<class_BTTask_property_scene_root>` property.
With ``BBNode`` property
@ -16,7 +17,7 @@ With ``BBNode`` property
@export var cast_param: BBNode
func _tick(delta) -> Status:
var node: ShapeCast3D = cast_param.get_value(agent, blackboard)
var node: ShapeCast3D = cast_param.get_value(scene_root, blackboard)
With ``NodePath`` property
@ -27,14 +28,16 @@ With ``NodePath`` property
@export var cast_path: NodePath
func _tick(delta) -> Status:
var node: ShapeCast3D = agent.get_node(cast_path)
var node: ShapeCast3D = scene_root.get_node(cast_path)
Using blackboard plan
---------------------
You can :ref:`create a blackboard variable<editing_plan>` in the editor with the type ``NodePath``
and point it to the proper node in the :ref:`BTPlayer<class_BTPlayer>` blackboard plan.
and point it to the proper node in the :ref:`BTPlayer<class_BTPlayer>` blackboard plan. By default,
any ``NodePath`` variable will be replaced with the node instance when the blackboard is instantiated
at runtime (see :ref:`BlackboardPlan.prefetch_nodepath_vars<class_BlackboardPlan_property_prefetch_nodepath_vars>`).
.. code:: gdscript

View File

@ -92,7 +92,7 @@ Example 1: A simple action
# Called each time this task is ticked (aka executed).
func _tick(p_delta: float) -> Status:
var n: CanvasItem = agent.get_node_or_null(node_path)
var n: CanvasItem = scene_root.get_node_or_null(node_path)
if is_instance_valid(n):
n.visible = visible
return SUCCESS

View File

@ -91,7 +91,7 @@ Usage example:
@export var speed: BBFloat
func _tick(delta: float) -> Status:
var current_speed: float = speed.get_value(agent, blackboard, 0.0)
var current_speed: float = speed.get_value(scene_root, blackboard, 0.0)
...
Advanced topic: Blackboard scopes

View File

@ -18,7 +18,7 @@
</method>
<method name="get_value">
<return type="Variant" />
<param index="0" name="agent" type="Object" />
<param index="0" name="scene_root" type="Node" />
<param index="1" name="blackboard" type="Blackboard" />
<param index="2" name="default" type="Variant" default="null" />
<description>

View File

@ -4,7 +4,7 @@
Player of [BehaviorTree] resources.
</brief_description>
<description>
BTPlayer node is used for the instantiation and playback of [BehaviorTree] resources at runtime. During instantiation, the behavior tree instance is initialized with a reference to the agent and the [member blackboard]. Agent is the owner of the BTPlayer node (see [member Node.owner]).
[BTPlayer] node is used to instantiate and play [BehaviorTree] resources at runtime. During initialization, the behavior tree instance is given references to the agent, the [member blackboard], and the current scene root. The agent can be specified by the [member agent_node] property (defaults to the BTPlayer's parent node).
For an introduction to behavior trees, see [BehaviorTree].
</description>
<tutorials>
@ -40,6 +40,9 @@
<member name="active" type="bool" setter="set_active" getter="get_active" default="true">
If [code]true[/code], the behavior tree will be executed during update.
</member>
<member name="agent_node" type="NodePath" setter="set_agent_node" getter="get_agent_node" default="NodePath(&quot;..&quot;)">
Path to the node that will be used as the agent. Setting it after instantiation will have no effect.
</member>
<member name="behavior_tree" type="BehaviorTree" setter="set_behavior_tree" getter="get_behavior_tree">
[BehaviorTree] resource to instantiate and execute at runtime.
</member>

View File

@ -144,9 +144,10 @@
<return type="void" />
<param index="0" name="agent" type="Node" />
<param index="1" name="blackboard" type="Blackboard" />
<param index="2" name="scene_root" type="Node" />
<description>
Initilizes the task. Assigns [member agent] and [member blackboard], and calls [method _setup] for the task and its children.
The method is called recursively for each child task.
The method is called recursively for each child task. [param scene_root] should be the root node of the scene the behavior tree is used in (e.g., the owner of the node that contains the behavior tree).
</description>
</method>
<method name="is_descendant_of" qualifiers="const">
@ -193,7 +194,7 @@
</methods>
<members>
<member name="agent" type="Node" setter="set_agent" getter="get_agent">
The agent is a contextual object for the task's [BehaviorTree] instance. Usually, agent is the owner of the [BTPlayer] node containing the [BehaviorTree] resource.
The agent is the contextual object for the [BehaviorTree] instance. This is usually the parent of the [BTPlayer] node that utilizes the [BehaviorTree] resource.
</member>
<member name="blackboard" type="Blackboard" setter="" getter="get_blackboard">
Provides access to the [Blackboard]. Blackboard is used to share data among tasks of the associated [BehaviorTree].
@ -206,6 +207,18 @@
Elapsed time since the task was "entered". See [method _enter].
Returns [code]0[/code] when task is not [code]RUNNING[/code].
</member>
<member name="scene_root" type="Node" setter="" getter="get_scene_root">
Root node of the scene the behavior tree is used in (e.g., the owner of the [BTPlayer] node). Can be uses to retrieve [NodePath] references.
[b]Example:[/b]
[codeblock]
extends BTAction
@export var node_path: NodePath
func _setup():
var node: Node = scene_root.get_node(node_path)
[/codeblock]
</member>
<member name="status" type="int" setter="" getter="get_status" enum="BT.Status">
Last execution [enum BT.Status] returned by [method _tick].
</member>

View File

@ -38,8 +38,9 @@
<return type="BTTask" />
<param index="0" name="agent" type="Node" />
<param index="1" name="blackboard" type="Blackboard" />
<param index="2" name="scene_root" type="Node" />
<description>
Instantiates the Behavior Tree and returns the root [BTTask].
Instantiates the behavior tree and returns the root [BTTask]. [param scene_root] should be the root node of the scene that the Behavior Tree will be used in (e.g., the owner of the node that contains the behavior tree).
</description>
</method>
<method name="set_root_task">

View File

@ -16,7 +16,7 @@
<param index="0" name="var_name" type="StringName" />
<param index="1" name="object" type="Object" />
<param index="2" name="property" type="StringName" />
<param index="3" name="create" type="bool" />
<param index="3" name="create" type="bool" default="false" />
<description>
Establish a binding between a variable and the object's property specified by [param property] and [param object]. Changes to the variable update the property, and vice versa. If [param create] is [code]true[/code], the variable will be created if it doesn't exist.
</description>
@ -55,7 +55,7 @@
<param index="0" name="var_name" type="StringName" />
<param index="1" name="target_blackboard" type="Blackboard" />
<param index="2" name="target_var" type="StringName" />
<param index="3" name="create" type="bool" />
<param index="3" name="create" type="bool" default="false" />
<description>
Links a variable to another Blackboard variable. If a variable is linked to another variable, their state will always be identical, and any change to one will be reflected in the other. If [param create] is [code]true[/code], the variable will be created if it doesn't exist.
You can use this method to link a variable in the current scope to a variable in another scope, or in another Blackboard instance. A variable can only be linked to one other variable. Calling this method again will overwrite the previous link. However, it is possible to link to the same variable from multiple different variables.

View File

@ -51,13 +51,13 @@ TEST_CASE("[SceneTree][LimboAI] BTAwaitAnimation") {
SUBCASE("When AnimationPlayer doesn't exist") {
player_param->set_saved_value(NodePath("./NotFound"));
ERR_PRINT_OFF;
awa->initialize(dummy, bb);
awa->initialize(dummy, bb, dummy);
CHECK(awa->execute(0.01666) == BTTask::FAILURE);
ERR_PRINT_ON;
}
SUBCASE("When AnimationPlayer exists") {
player_param->set_saved_value(player->get_path());
awa->initialize(dummy, bb);
awa->initialize(dummy, bb, dummy);
SUBCASE("When AnimationPlayer is not playing") {
REQUIRE_FALSE(player->is_playing());

View File

@ -47,7 +47,7 @@ TEST_CASE("[Modules][LimboAI] BTCallMethod") {
node_param->set_variable("object");
cm->set_method("callback");
cm->initialize(dummy, bb);
cm->initialize(dummy, bb, dummy);
SUBCASE("When method is empty") {
cm->set_method("");

View File

@ -39,7 +39,7 @@ TEST_CASE("[Modules][LimboAI] BTCheckAgentProperty") {
Ref<BTCheckAgentProperty> cap = memnew(BTCheckAgentProperty);
Node *agent = memnew(Node);
Ref<Blackboard> bb = memnew(Blackboard);
cap->initialize(agent, bb);
cap->initialize(agent, bb, agent);
StringName agent_name = "SimpleNode";
agent->set_name(agent_name);

View File

@ -24,7 +24,7 @@ TEST_CASE("[Modules][LimboAI] BTCheckTrigger") {
Node *dummy = memnew(Node);
Ref<Blackboard> bb = memnew(Blackboard);
ct->initialize(dummy, bb);
ct->initialize(dummy, bb, dummy);
SUBCASE("Empty") {
ERR_PRINT_OFF;

View File

@ -36,7 +36,7 @@ TEST_CASE("[Modules][LimboAI] BTCheckVar") {
Ref<BTCheckVar> cv = memnew(BTCheckVar);
Ref<Blackboard> bb = memnew(Blackboard);
Node *dummy = memnew(Node);
cv->initialize(dummy, bb);
cv->initialize(dummy, bb, dummy);
SUBCASE("Check with empty variable and value") {
cv->set_variable("");

View File

@ -47,7 +47,7 @@ TEST_CASE("[Modules][LimboAI] BTEvaluateExpression") {
node_param->set_variable("object");
ee->set_expression_string("callback()");
ee->initialize(dummy, bb);
ee->initialize(dummy, bb, dummy);
SUBCASE("When expression string is empty") {
ee->set_expression_string("");

View File

@ -23,7 +23,7 @@ TEST_CASE("[Modules][LimboAI] BTForEach") {
Ref<BTForEach> fe = memnew(BTForEach);
Node *dummy = memnew(Node);
Ref<Blackboard> blackboard = memnew(Blackboard);
fe->initialize(dummy, blackboard);
fe->initialize(dummy, blackboard, dummy);
Array arr;
arr.append("apple");

View File

@ -26,7 +26,7 @@ TEST_CASE("[Modules][LimboAI] BTNewScope") {
SUBCASE("When empty") {
ERR_PRINT_OFF;
ns->initialize(dummy, parent_bb);
ns->initialize(dummy, parent_bb, dummy);
CHECK(ns->execute(0.01666) == BTTask::FAILURE);
ERR_PRINT_ON;
}
@ -45,7 +45,7 @@ TEST_CASE("[Modules][LimboAI] BTNewScope") {
REQUIRE(parent_bb->has_var("vegetable"));
REQUIRE(parent_bb->get_var("vegetable", "wetgoop") == "carrot");
parent->initialize(dummy, parent_bb);
parent->initialize(dummy, parent_bb, dummy);
CHECK(ns->get_blackboard() != parent->get_blackboard());
CHECK(ns->get_blackboard() == child->get_blackboard());

View File

@ -48,13 +48,13 @@ TEST_CASE("[SceneTree][LimboAI] BTPauseAnimation") {
SUBCASE("When AnimationPlayer doesn't exist") {
player_param->set_saved_value(NodePath("./NotFound"));
ERR_PRINT_OFF;
pa->initialize(dummy, bb);
pa->initialize(dummy, bb, dummy);
CHECK(pa->execute(0.01666) == BTTask::FAILURE);
ERR_PRINT_ON;
}
SUBCASE("When AnimationPlayer exists") {
player_param->set_saved_value(player->get_path());
pa->initialize(dummy, bb);
pa->initialize(dummy, bb, dummy);
SUBCASE("When AnimationPlayer is not playing") {
REQUIRE_FALSE(player->is_playing());

View File

@ -49,13 +49,13 @@ TEST_CASE("[SceneTree][LimboAI] BTPlayAnimation") {
SUBCASE("When AnimationPlayer doesn't exist") {
player_param->set_saved_value(NodePath("./NotFound"));
ERR_PRINT_OFF;
pa->initialize(dummy, bb);
pa->initialize(dummy, bb, dummy);
CHECK(pa->execute(0.01666) == BTTask::FAILURE);
ERR_PRINT_ON;
}
SUBCASE("When AnimationPlayer exists") {
player_param->set_saved_value(player->get_path());
pa->initialize(dummy, bb);
pa->initialize(dummy, bb, dummy);
SUBCASE("When not waiting to finish") {
pa->set_await_completion(0.0);

View File

@ -28,7 +28,7 @@ TEST_CASE("[Modules][LimboAI] BTSetAgentProperty") {
Ref<BTSetAgentProperty> sap = memnew(BTSetAgentProperty);
Node *agent = memnew(Node);
Ref<Blackboard> bb = memnew(Blackboard);
sap->initialize(agent, bb);
sap->initialize(agent, bb, agent);
sap->set_property("process_priority"); // * property that will be set by the task
Ref<BBVariant> value = memnew(BBVariant);

View File

@ -29,7 +29,7 @@ TEST_CASE("[Modules][LimboAI] BTSetVar") {
Ref<Blackboard> bb = memnew(Blackboard);
Node *dummy = memnew(Node);
sv->initialize(dummy, bb);
sv->initialize(dummy, bb, dummy);
SUBCASE("When variable is not set") {
ERR_PRINT_OFF;

View File

@ -48,13 +48,13 @@ TEST_CASE("[SceneTree][LimboAI] BTStopAnimation") {
SUBCASE("When AnimationPlayer doesn't exist") {
player_param->set_saved_value(NodePath("./NotFound"));
ERR_PRINT_OFF;
sa->initialize(dummy, bb);
sa->initialize(dummy, bb, dummy);
CHECK(sa->execute(0.01666) == BTTask::FAILURE);
ERR_PRINT_ON;
}
SUBCASE("When AnimationPlayer exists") {
player_param->set_saved_value(player->get_path());
sa->initialize(dummy, bb);
sa->initialize(dummy, bb, dummy);
SUBCASE("When AnimationPlayer is not playing") {
REQUIRE_FALSE(player->is_playing());

View File

@ -29,7 +29,7 @@ TEST_CASE("[Modules][LimboAI] BTSubtree") {
SUBCASE("When empty") {
ERR_PRINT_OFF;
st->initialize(dummy, bb);
st->initialize(dummy, bb, dummy);
CHECK(st->execute(0.01666) == BTTask::FAILURE);
ERR_PRINT_ON;
}
@ -41,7 +41,7 @@ TEST_CASE("[Modules][LimboAI] BTSubtree") {
st->set_subtree(bt);
CHECK(st->get_child_count() == 0);
st->initialize(dummy, bb);
st->initialize(dummy, bb, dummy);
CHECK(st->get_child_count() == 1);
CHECK(st->get_child(0) != task);

View File

@ -162,7 +162,7 @@ TEST_CASE("[Modules][LimboAI] BTTask") {
Node *dummy = memnew(Node);
Ref<Blackboard> bb = memnew(Blackboard);
SUBCASE("With valid parameters") {
task->initialize(dummy, bb);
task->initialize(dummy, bb, dummy);
CHECK(task->get_agent() == dummy);
CHECK(task->get_blackboard() == bb);
CHECK(child1->get_agent() == dummy);
@ -174,12 +174,17 @@ TEST_CASE("[Modules][LimboAI] BTTask") {
}
SUBCASE("Test if not crashes when agent is null") {
ERR_PRINT_OFF;
task->initialize(nullptr, bb);
task->initialize(nullptr, bb, dummy);
ERR_PRINT_ON;
}
SUBCASE("Test if not crashes when scene_owner is null") {
ERR_PRINT_OFF;
task->initialize(dummy, bb, nullptr);
ERR_PRINT_ON;
}
SUBCASE("Test if not crashes when BB is null") {
ERR_PRINT_OFF;
task->initialize(dummy, nullptr);
task->initialize(dummy, nullptr, dummy);
ERR_PRINT_ON;
}
memdelete(dummy);

View File

@ -53,6 +53,7 @@
#define PERFORMANCE_ADD_CUSTOM_MONITOR(m_id, m_callable) (Performance::get_singleton()->add_custom_monitor(m_id, m_callable, Variant()))
#define GET_SCRIPT(m_obj) (m_obj->get_script_instance() ? m_obj->get_script_instance()->get_script() : nullptr)
#define ADD_STYLEBOX_OVERRIDE(m_control, m_name, m_stylebox) (m_control->add_theme_style_override(m_name, m_stylebox))
#define GET_NODE(m_parent, m_path) m_parent->get_node(m_path)
_FORCE_INLINE_ bool OBJECT_HAS_PROPERTY(Object *p_obj, const StringName &p_prop) {
bool r_valid;
@ -137,6 +138,7 @@ using namespace godot;
#define PERFORMANCE_ADD_CUSTOM_MONITOR(m_id, m_callable) (Performance::get_singleton()->add_custom_monitor(m_id, m_callable))
#define GET_SCRIPT(m_obj) (m_obj->get_script())
#define ADD_STYLEBOX_OVERRIDE(m_control, m_name, m_stylebox) (m_control->add_theme_stylebox_override(m_name, m_stylebox))
#define GET_NODE(m_parent, m_path) m_parent->get_node_internal(m_path)
_FORCE_INLINE_ bool OBJECT_HAS_PROPERTY(Object *p_obj, const StringName &p_prop) {
return Variant(p_obj).has_key(p_prop);

View File

@ -166,4 +166,6 @@ LimboStringNames::LimboStringNames() {
repeat_forever.parse_utf8("Repeat ∞");
output_var_prefix.parse_utf8("");
node_pp = NodePath("..");
}

View File

@ -13,6 +13,7 @@
#define LIMBO_STRING_NAMES_H
#ifdef LIMBOAI_MODULE
#include "core/string/node_path.h"
#include "core/string/string_name.h"
#include "core/typedefs.h"
#include "modules/register_module_types.h"
@ -20,6 +21,7 @@
#ifdef LIMBOAI_GDEXTENSION
#include "godot_cpp/variant/string.hpp"
#include <godot_cpp/variant/node_path.hpp>
#include <godot_cpp/variant/string_name.hpp>
using namespace godot;
#endif // LIMBOAI_GDEXTENSION
@ -181,6 +183,8 @@ public:
String repeat_forever;
String output_var_prefix;
NodePath node_pp;
};
#define LW_NAME(m_arg) LimboStringNames::get_singleton()->m_arg

View File

@ -36,7 +36,7 @@ using namespace godot;
#define LOGICAL_XOR(a, b) (a) ? !(b) : (b)
#define LIMBO_DOC_VERSION "v1.0"
#define LIMBO_DOC_VERSION "latest"
class LimboUtility : public Object {
GDCLASS(LimboUtility, Object);