From 5dff2e537ba1aa3aac8071739f06c4e4c1f3d663 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Wed, 1 May 2024 23:20:17 +0200 Subject: [PATCH 1/8] 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). --- bt/behavior_tree.cpp | 4 ++-- bt/behavior_tree.h | 2 +- bt/bt_player.cpp | 14 +++++++++++--- bt/bt_player.h | 4 ++++ bt/bt_state.cpp | 2 +- bt/tasks/bt_task.cpp | 12 ++++++++---- bt/tasks/bt_task.h | 5 ++++- bt/tasks/decorators/bt_new_scope.cpp | 4 ++-- bt/tasks/decorators/bt_new_scope.h | 2 +- bt/tasks/decorators/bt_subtree.cpp | 4 ++-- bt/tasks/decorators/bt_subtree.h | 2 +- tests/test_await_animation.h | 4 ++-- tests/test_call_method.h | 2 +- tests/test_check_agent_property.h | 2 +- tests/test_check_trigger.h | 2 +- tests/test_check_var.h | 2 +- tests/test_evaluate_expression.h | 2 +- tests/test_for_each.h | 2 +- tests/test_new_scope.h | 4 ++-- tests/test_pause_animation.h | 4 ++-- tests/test_play_animation.h | 4 ++-- tests/test_set_agent_property.h | 2 +- tests/test_set_var.h | 2 +- tests/test_stop_animation.h | 4 ++-- tests/test_subtree.h | 4 ++-- tests/test_task.h | 11 ++++++++--- util/limbo_compat.h | 2 ++ util/limbo_string_names.cpp | 2 ++ util/limbo_string_names.h | 4 ++++ 29 files changed, 73 insertions(+), 41 deletions(-) diff --git a/bt/behavior_tree.cpp b/bt/behavior_tree.cpp index 4b5e5f0..01168ff 100644 --- a/bt/behavior_tree.cpp +++ b/bt/behavior_tree.cpp @@ -71,10 +71,10 @@ void BehaviorTree::copy_other(const Ref &p_other) { root_task = p_other->get_root_task(); } -Ref BehaviorTree::instantiate(Node *p_agent, const Ref &p_blackboard) const { +Ref BehaviorTree::instantiate(Node *p_agent, const Ref &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."); Ref inst = root_task->clone(); - inst->initialize(p_agent, p_blackboard); + inst->initialize(p_agent, p_blackboard, p_scene_root); return inst; } diff --git a/bt/behavior_tree.h b/bt/behavior_tree.h index 8935bd6..01ae806 100644 --- a/bt/behavior_tree.h +++ b/bt/behavior_tree.h @@ -53,7 +53,7 @@ public: Ref clone() const; void copy_other(const Ref &p_other); - Ref instantiate(Node *p_agent, const Ref &p_blackboard) const; + Ref instantiate(Node *p_agent, const Ref &p_blackboard, Node *p_scene_root) const; BehaviorTree(); ~BehaviorTree(); diff --git a/bt/bt_player.cpp b/bt/bt_player.cpp index 626962a..d3d3bc3 100644 --- a/bt/bt_player.cpp +++ b/bt/bt_player.cpp @@ -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_node = GET_NODE(this, agent); + ERR_FAIL_NULL_MSG(agent_node, vformat("BTPlayer: Initialization failed - can't get agent by provided path '%s'.", agent)); + Node *scene_root = get_owner(); + ERR_FAIL_NULL_MSG(scene_root, "BTPlayer: Initialization failed - can't get scene root (make sure the BTPlayer.owner is set)."); + tree_instance = behavior_tree->instantiate(agent_node, blackboard, scene_root); #ifdef DEBUG_ENABLED if (IS_DEBUGGER_ACTIVE()) { LimboDebugger::get_singleton()->register_bt_instance(tree_instance, get_path()); @@ -228,6 +232,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", "agent"), &BTPlayer::set_agent); + ClassDB::bind_method(D_METHOD("get_agent"), &BTPlayer::get_agent); 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 +251,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"), "set_agent", "get_agent"); 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 +273,7 @@ void BTPlayer::_bind_methods() { BTPlayer::BTPlayer() { blackboard = Ref(memnew(Blackboard)); + agent = LW_NAME(node_pp); } BTPlayer::~BTPlayer() { diff --git a/bt/bt_player.h b/bt/bt_player.h index 06eec72..dffa913 100644 --- a/bt/bt_player.h +++ b/bt/bt_player.h @@ -37,6 +37,7 @@ public: private: Ref behavior_tree; + NodePath agent; Ref blackboard_plan; UpdateMode update_mode = UpdateMode::PHYSICS; bool active = true; @@ -57,6 +58,9 @@ public: void set_behavior_tree(const Ref &p_tree); Ref get_behavior_tree() const { return behavior_tree; }; + void set_agent(const NodePath &p_agent) { agent = p_agent; } + NodePath get_agent() const { return agent; } + void set_blackboard_plan(const Ref &p_plan); Ref get_blackboard_plan() const { return blackboard_plan; } diff --git a/bt/bt_state.cpp b/bt/bt_state.cpp index c03c67c..d362479 100644 --- a/bt/bt_state.cpp +++ b/bt/bt_state.cpp @@ -53,7 +53,7 @@ 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()); + tree_instance = behavior_tree->instantiate(get_agent(), get_blackboard(), get_owner()); #ifdef DEBUG_ENABLED if (tree_instance.is_valid() && IS_DEBUGGER_ACTIVE()) { diff --git a/bt/tasks/bt_task.cpp b/bt/tasks/bt_task.cpp index 66ee145..d910f99 100644 --- a/bt/tasks/bt_task.cpp +++ b/bt/tasks/bt_task.cpp @@ -159,13 +159,15 @@ void BTTask::set_custom_name(const String &p_name) { } }; -void BTTask::initialize(Node *p_agent, const Ref &p_blackboard) { - ERR_FAIL_COND(p_agent == nullptr); - ERR_FAIL_COND(p_blackboard == nullptr); +void BTTask::initialize(Node *p_agent, const Ref &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); @@ -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"); diff --git a/bt/tasks/bt_task.h b/bt/tasks/bt_task.h index 5b7a4ac..e9017ad 100644 --- a/bt/tasks/bt_task.h +++ b/bt/tasks/bt_task.h @@ -75,6 +75,7 @@ private: int index = -1; String custom_name; Node *agent = nullptr; + Node *scene_root = nullptr; Ref blackboard; BTTask *parent = nullptr; Vector> 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 get_root() const; virtual Ref clone() const; - virtual void initialize(Node *p_agent, const Ref &p_blackboard); + virtual void initialize(Node *p_agent, const Ref &p_blackboard, Node *p_scene_root); virtual PackedStringArray get_configuration_warnings(); // ! Native version. Status execute(double p_delta); diff --git a/bt/tasks/decorators/bt_new_scope.cpp b/bt/tasks/decorators/bt_new_scope.cpp index e25436e..5aaa4bc 100644 --- a/bt/tasks/decorators/bt_new_scope.cpp +++ b/bt/tasks/decorators/bt_new_scope.cpp @@ -20,7 +20,7 @@ void BTNewScope::set_blackboard_plan(const Ref &p_plan) { emit_changed(); } -void BTNewScope::initialize(Node *p_agent, const Ref &p_blackboard) { +void BTNewScope::initialize(Node *p_agent, const Ref &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 &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) { diff --git a/bt/tasks/decorators/bt_new_scope.h b/bt/tasks/decorators/bt_new_scope.h index 05226fa..a7e0020 100644 --- a/bt/tasks/decorators/bt_new_scope.h +++ b/bt/tasks/decorators/bt_new_scope.h @@ -34,7 +34,7 @@ protected: virtual Status _tick(double p_delta) override; public: - virtual void initialize(Node *p_agent, const Ref &p_blackboard) override; + virtual void initialize(Node *p_agent, const Ref &p_blackboard, Node *p_scene_root) override; }; #endif // BT_NEW_SCOPE_H diff --git a/bt/tasks/decorators/bt_subtree.cpp b/bt/tasks/decorators/bt_subtree.cpp index dfa7636..9124912 100644 --- a/bt/tasks/decorators/bt_subtree.cpp +++ b/bt/tasks/decorators/bt_subtree.cpp @@ -44,14 +44,14 @@ String BTSubtree::_generate_name() { return vformat("Subtree %s", s); } -void BTSubtree::initialize(Node *p_agent, const Ref &p_blackboard) { +void BTSubtree::initialize(Node *p_agent, const Ref &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) { diff --git a/bt/tasks/decorators/bt_subtree.h b/bt/tasks/decorators/bt_subtree.h index b248df7..b97504a 100644 --- a/bt/tasks/decorators/bt_subtree.h +++ b/bt/tasks/decorators/bt_subtree.h @@ -35,7 +35,7 @@ public: void set_subtree(const Ref &p_value); Ref get_subtree() const { return subtree; } - virtual void initialize(Node *p_agent, const Ref &p_blackboard) override; + virtual void initialize(Node *p_agent, const Ref &p_blackboard, Node *p_scene_root) override; virtual PackedStringArray get_configuration_warnings() override; BTSubtree() = default; diff --git a/tests/test_await_animation.h b/tests/test_await_animation.h index 4f949f3..06c4f52 100644 --- a/tests/test_await_animation.h +++ b/tests/test_await_animation.h @@ -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()); diff --git a/tests/test_call_method.h b/tests/test_call_method.h index 711908a..3dd7324 100644 --- a/tests/test_call_method.h +++ b/tests/test_call_method.h @@ -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(""); diff --git a/tests/test_check_agent_property.h b/tests/test_check_agent_property.h index 3465bad..88c0cb4 100644 --- a/tests/test_check_agent_property.h +++ b/tests/test_check_agent_property.h @@ -39,7 +39,7 @@ TEST_CASE("[Modules][LimboAI] BTCheckAgentProperty") { Ref cap = memnew(BTCheckAgentProperty); Node *agent = memnew(Node); Ref bb = memnew(Blackboard); - cap->initialize(agent, bb); + cap->initialize(agent, bb, agent); StringName agent_name = "SimpleNode"; agent->set_name(agent_name); diff --git a/tests/test_check_trigger.h b/tests/test_check_trigger.h index af490af..0f4307a 100644 --- a/tests/test_check_trigger.h +++ b/tests/test_check_trigger.h @@ -24,7 +24,7 @@ TEST_CASE("[Modules][LimboAI] BTCheckTrigger") { Node *dummy = memnew(Node); Ref bb = memnew(Blackboard); - ct->initialize(dummy, bb); + ct->initialize(dummy, bb, dummy); SUBCASE("Empty") { ERR_PRINT_OFF; diff --git a/tests/test_check_var.h b/tests/test_check_var.h index 1406837..4bc1f9c 100644 --- a/tests/test_check_var.h +++ b/tests/test_check_var.h @@ -36,7 +36,7 @@ TEST_CASE("[Modules][LimboAI] BTCheckVar") { Ref cv = memnew(BTCheckVar); Ref 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(""); diff --git a/tests/test_evaluate_expression.h b/tests/test_evaluate_expression.h index 58d4ca5..15702c7 100644 --- a/tests/test_evaluate_expression.h +++ b/tests/test_evaluate_expression.h @@ -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(""); diff --git a/tests/test_for_each.h b/tests/test_for_each.h index 0ab4919..00cad30 100644 --- a/tests/test_for_each.h +++ b/tests/test_for_each.h @@ -23,7 +23,7 @@ TEST_CASE("[Modules][LimboAI] BTForEach") { Ref fe = memnew(BTForEach); Node *dummy = memnew(Node); Ref blackboard = memnew(Blackboard); - fe->initialize(dummy, blackboard); + fe->initialize(dummy, blackboard, dummy); Array arr; arr.append("apple"); diff --git a/tests/test_new_scope.h b/tests/test_new_scope.h index 988edd7..c5627b7 100644 --- a/tests/test_new_scope.h +++ b/tests/test_new_scope.h @@ -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()); diff --git a/tests/test_pause_animation.h b/tests/test_pause_animation.h index a438068..8f7e3fb 100644 --- a/tests/test_pause_animation.h +++ b/tests/test_pause_animation.h @@ -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()); diff --git a/tests/test_play_animation.h b/tests/test_play_animation.h index 245ee74..85c71ec 100644 --- a/tests/test_play_animation.h +++ b/tests/test_play_animation.h @@ -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); diff --git a/tests/test_set_agent_property.h b/tests/test_set_agent_property.h index bb84e18..50f84b4 100644 --- a/tests/test_set_agent_property.h +++ b/tests/test_set_agent_property.h @@ -28,7 +28,7 @@ TEST_CASE("[Modules][LimboAI] BTSetAgentProperty") { Ref sap = memnew(BTSetAgentProperty); Node *agent = memnew(Node); Ref 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 value = memnew(BBVariant); diff --git a/tests/test_set_var.h b/tests/test_set_var.h index 51f2c1f..da214e2 100644 --- a/tests/test_set_var.h +++ b/tests/test_set_var.h @@ -29,7 +29,7 @@ TEST_CASE("[Modules][LimboAI] BTSetVar") { Ref 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; diff --git a/tests/test_stop_animation.h b/tests/test_stop_animation.h index 1ffb960..1313761 100644 --- a/tests/test_stop_animation.h +++ b/tests/test_stop_animation.h @@ -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()); diff --git a/tests/test_subtree.h b/tests/test_subtree.h index ebaa4bc..a5e5204 100644 --- a/tests/test_subtree.h +++ b/tests/test_subtree.h @@ -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); diff --git a/tests/test_task.h b/tests/test_task.h index 1d1e71d..5e79ae0 100644 --- a/tests/test_task.h +++ b/tests/test_task.h @@ -162,7 +162,7 @@ TEST_CASE("[Modules][LimboAI] BTTask") { Node *dummy = memnew(Node); Ref 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); diff --git a/util/limbo_compat.h b/util/limbo_compat.h index 321dc40..228cb3e 100644 --- a/util/limbo_compat.h +++ b/util/limbo_compat.h @@ -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); diff --git a/util/limbo_string_names.cpp b/util/limbo_string_names.cpp index 0f1dd67..a223ded 100644 --- a/util/limbo_string_names.cpp +++ b/util/limbo_string_names.cpp @@ -166,4 +166,6 @@ LimboStringNames::LimboStringNames() { repeat_forever.parse_utf8("Repeat ∞"); output_var_prefix.parse_utf8("➜"); + + node_pp = NodePath(".."); } diff --git a/util/limbo_string_names.h b/util/limbo_string_names.h index fe62c27..10e6542 100644 --- a/util/limbo_string_names.h +++ b/util/limbo_string_names.h @@ -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 #include 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 From 506d8aa967cfa73b50ca7ad05d74a3ffe7f41c43 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Wed, 1 May 2024 23:32:44 +0200 Subject: [PATCH 2/8] Use `scene_root` with `BBParam` --- blackboard/bb_param/bb_node.cpp | 10 ++++------ blackboard/bb_param/bb_node.h | 2 +- blackboard/bb_param/bb_param.cpp | 4 ++-- blackboard/bb_param/bb_param.h | 2 +- bt/tasks/blackboard/bt_check_var.cpp | 2 +- bt/tasks/blackboard/bt_set_var.cpp | 2 +- bt/tasks/scene/bt_await_animation.cpp | 2 +- bt/tasks/scene/bt_check_agent_property.cpp | 2 +- bt/tasks/scene/bt_pause_animation.cpp | 2 +- bt/tasks/scene/bt_play_animation.cpp | 2 +- bt/tasks/scene/bt_set_agent_property.cpp | 2 +- bt/tasks/scene/bt_stop_animation.cpp | 2 +- bt/tasks/utility/bt_call_method.cpp | 6 +++--- bt/tasks/utility/bt_evaluate_expression.cpp | 4 ++-- 14 files changed, 21 insertions(+), 23 deletions(-) diff --git a/blackboard/bb_param/bb_node.cpp b/blackboard/bb_param/bb_node.cpp index 3110712..d305f4b 100644 --- a/blackboard/bb_param/bb_node.cpp +++ b/blackboard/bb_param/bb_node.cpp @@ -20,9 +20,9 @@ #include #endif // LIMBOAI_GDEXTENSION -Variant BBNode::get_value(Object *p_agent, const Ref &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 &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 &p_blackboard, } if (val.get_type() == Variant::NODE_PATH) { - Node *agent = Object::cast_to(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)) { diff --git a/blackboard/bb_param/bb_node.h b/blackboard/bb_param/bb_node.h index e6db0c8..ce6c0ee 100644 --- a/blackboard/bb_param/bb_node.h +++ b/blackboard/bb_param/bb_node.h @@ -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 &p_blackboard, const Variant &p_default = Variant()) override; + virtual Variant get_value(Node *p_scene_root, const Ref &p_blackboard, const Variant &p_default = Variant()) override; }; #endif // BB_NODE_H diff --git a/blackboard/bb_param/bb_param.cpp b/blackboard/bb_param/bb_param.cpp index 88ad5e7..c5308c1 100644 --- a/blackboard/bb_param/bb_param.cpp +++ b/blackboard/bb_param/bb_param.cpp @@ -75,7 +75,7 @@ String BBParam::_to_string() { } } -Variant BBParam::get_value(Object *p_agent, const Ref &p_blackboard, const Variant &p_default) { +Variant BBParam::get_value(Node *p_scene_root, const Ref &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"); diff --git a/blackboard/bb_param/bb_param.h b/blackboard/bb_param/bb_param.h index 7d75884..98a96cb 100644 --- a/blackboard/bb_param/bb_param.h +++ b/blackboard/bb_param/bb_param.h @@ -66,7 +66,7 @@ public: virtual String _to_string(); #endif - virtual Variant get_value(Object *p_agent, const Ref &p_blackboard, const Variant &p_default = Variant()); + virtual Variant get_value(Node *p_scene_root, const Ref &p_blackboard, const Variant &p_default = Variant()); BBParam(); }; diff --git a/bt/tasks/blackboard/bt_check_var.cpp b/bt/tasks/blackboard/bt_check_var.cpp index 96ea05e..14962dc 100644 --- a/bt/tasks/blackboard/bt_check_var.cpp +++ b/bt/tasks/blackboard/bt_check_var.cpp @@ -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; } diff --git a/bt/tasks/blackboard/bt_set_var.cpp b/bt/tasks/blackboard/bt_set_var.cpp index 67adef5..4b44d1d 100644 --- a/bt/tasks/blackboard/bt_set_var.cpp +++ b/bt/tasks/blackboard/bt_set_var.cpp @@ -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; diff --git a/bt/tasks/scene/bt_await_animation.cpp b/bt/tasks/scene/bt_await_animation.cpp index 67cee8e..a511cdf 100644 --- a/bt/tasks/scene/bt_await_animation.cpp +++ b/bt/tasks/scene/bt_await_animation.cpp @@ -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(animation_player_param->get_value(get_agent(), get_blackboard())); + animation_player = Object::cast_to(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)); diff --git a/bt/tasks/scene/bt_check_agent_property.cpp b/bt/tasks/scene/bt_check_agent_property.cpp index 31e4c4b..7ac0c4d 100644 --- a/bt/tasks/scene/bt_check_agent_property.cpp +++ b/bt/tasks/scene/bt_check_agent_property.cpp @@ -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; } diff --git a/bt/tasks/scene/bt_pause_animation.cpp b/bt/tasks/scene/bt_pause_animation.cpp index 86e3958..4f1dd3c 100644 --- a/bt/tasks/scene/bt_pause_animation.cpp +++ b/bt/tasks/scene/bt_pause_animation.cpp @@ -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(animation_player_param->get_value(get_agent(), get_blackboard())); + animation_player = Object::cast_to(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; } diff --git a/bt/tasks/scene/bt_play_animation.cpp b/bt/tasks/scene/bt_play_animation.cpp index e2de77d..f373e5f 100644 --- a/bt/tasks/scene/bt_play_animation.cpp +++ b/bt/tasks/scene/bt_play_animation.cpp @@ -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(animation_player_param->get_value(get_agent(), get_blackboard())); + animation_player = Object::cast_to(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) { diff --git a/bt/tasks/scene/bt_set_agent_property.cpp b/bt/tasks/scene/bt_set_agent_property.cpp index f104a98..600e138 100644 --- a/bt/tasks/scene/bt_set_agent_property.cpp +++ b/bt/tasks/scene/bt_set_agent_property.cpp @@ -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) { diff --git a/bt/tasks/scene/bt_stop_animation.cpp b/bt/tasks/scene/bt_stop_animation.cpp index 20e7f41..0bc247b 100644 --- a/bt/tasks/scene/bt_stop_animation.cpp +++ b/bt/tasks/scene/bt_stop_animation.cpp @@ -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(animation_player_param->get_value(get_agent(), get_blackboard())); + animation_player = Object::cast_to(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)); diff --git a/bt/tasks/utility/bt_call_method.cpp b/bt/tasks/utility/bt_call_method.cpp index 0d3f36d..f559d2e 100644 --- a/bt/tasks/utility/bt_call_method.cpp +++ b/bt/tasks/utility/bt_call_method.cpp @@ -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 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 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... diff --git a/bt/tasks/utility/bt_evaluate_expression.cpp b/bt/tasks/utility/bt_evaluate_expression.cpp index bea39dc..b6ab59b 100644 --- a/bt/tasks/utility/bt_evaluate_expression.cpp +++ b/bt/tasks/utility/bt_evaluate_expression.cpp @@ -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 &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); From af23272e3deadeb15bbf91c023d50a685057145a Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Thu, 2 May 2024 01:27:14 +0200 Subject: [PATCH 3/8] Fix unnamed arguments in method bindings --- bt/behavior_tree.cpp | 2 +- bt/tasks/bt_task.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bt/behavior_tree.cpp b/bt/behavior_tree.cpp index 01168ff..5bd2a69 100644 --- a/bt/behavior_tree.cpp +++ b/bt/behavior_tree.cpp @@ -92,7 +92,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"); diff --git a/bt/tasks/bt_task.cpp b/bt/tasks/bt_task.cpp index d910f99..9a5e284 100644 --- a/bt/tasks/bt_task.cpp +++ b/bt/tasks/bt_task.cpp @@ -380,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); From a2dae24b999c8340650dd53f096a1cb075fddf8d Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Thu, 2 May 2024 12:11:59 +0200 Subject: [PATCH 4/8] Rename BTPlayer's agent => agent_node --- bt/bt_player.cpp | 14 +++++++------- bt/bt_player.h | 6 +++--- bt/bt_state.cpp | 1 - 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/bt/bt_player.cpp b/bt/bt_player.cpp index d3d3bc3..bd00b44 100644 --- a/bt/bt_player.cpp +++ b/bt/bt_player.cpp @@ -52,11 +52,11 @@ void BTPlayer::_load_tree() { tree_instance.unref(); 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_node = GET_NODE(this, agent); - ERR_FAIL_NULL_MSG(agent_node, vformat("BTPlayer: Initialization failed - can't get agent by provided path '%s'.", agent)); + 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.owner is set)."); - tree_instance = behavior_tree->instantiate(agent_node, blackboard, scene_root); + 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()); @@ -232,8 +232,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", "agent"), &BTPlayer::set_agent); - ClassDB::bind_method(D_METHOD("get_agent"), &BTPlayer::get_agent); + 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); @@ -251,7 +251,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"), "set_agent", "get_agent"); + 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"); @@ -273,7 +273,7 @@ void BTPlayer::_bind_methods() { BTPlayer::BTPlayer() { blackboard = Ref(memnew(Blackboard)); - agent = LW_NAME(node_pp); + agent_node = LW_NAME(node_pp); } BTPlayer::~BTPlayer() { diff --git a/bt/bt_player.h b/bt/bt_player.h index dffa913..74b72e2 100644 --- a/bt/bt_player.h +++ b/bt/bt_player.h @@ -37,7 +37,7 @@ public: private: Ref behavior_tree; - NodePath agent; + NodePath agent_node; Ref blackboard_plan; UpdateMode update_mode = UpdateMode::PHYSICS; bool active = true; @@ -58,8 +58,8 @@ public: void set_behavior_tree(const Ref &p_tree); Ref get_behavior_tree() const { return behavior_tree; }; - void set_agent(const NodePath &p_agent) { agent = p_agent; } - NodePath get_agent() const { return agent; } + void set_agent_node(const NodePath &p_agent_node) { agent_node = p_agent_node; } + NodePath get_agent_node() const { return agent_node; } void set_blackboard_plan(const Ref &p_plan); Ref get_blackboard_plan() const { return blackboard_plan; } diff --git a/bt/bt_state.cpp b/bt/bt_state.cpp index d362479..08c40a6 100644 --- a/bt/bt_state.cpp +++ b/bt/bt_state.cpp @@ -52,7 +52,6 @@ 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(), get_owner()); #ifdef DEBUG_ENABLED From 84c89356a62407b12ab459a208ec2ab5ca5bf5b7 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Thu, 2 May 2024 12:49:32 +0200 Subject: [PATCH 5/8] Print error if agent node is set after initialization --- bt/bt_player.cpp | 7 +++++++ bt/bt_player.h | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/bt/bt_player.cpp b/bt/bt_player.cpp index bd00b44..e491ec8 100644 --- a/bt/bt_player.cpp +++ b/bt/bt_player.cpp @@ -93,6 +93,13 @@ void BTPlayer::set_behavior_tree(const Ref &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 &p_plan) { blackboard_plan = p_plan; _update_blackboard_plan(); diff --git a/bt/bt_player.h b/bt/bt_player.h index 74b72e2..026185d 100644 --- a/bt/bt_player.h +++ b/bt/bt_player.h @@ -58,7 +58,7 @@ public: void set_behavior_tree(const Ref &p_tree); Ref get_behavior_tree() const { return behavior_tree; }; - void set_agent_node(const NodePath &p_agent_node) { agent_node = p_agent_node; } + void set_agent_node(const NodePath &p_agent_node); NodePath get_agent_node() const { return agent_node; } void set_blackboard_plan(const Ref &p_plan); From 803da63fa82a4343cec6ae6201dca3af55039c1c Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Thu, 2 May 2024 13:39:19 +0200 Subject: [PATCH 6/8] Doc: Update class docs --- doc/source/classes/class_bbparam.rst | 12 +- doc/source/classes/class_behaviortree.rst | 26 ++-- doc/source/classes/class_blackboard.rst | 46 +++---- doc/source/classes/class_btplayer.rst | 45 +++++-- doc/source/classes/class_bttask.rst | 139 +++++++++++++--------- doc_classes/BBParam.xml | 2 +- doc_classes/BTPlayer.xml | 3 + doc_classes/BTTask.xml | 15 ++- doc_classes/BehaviorTree.xml | 3 +- doc_classes/Blackboard.xml | 4 +- 10 files changed, 180 insertions(+), 115 deletions(-) diff --git a/doc/source/classes/class_bbparam.rst b/doc/source/classes/class_bbparam.rst index af4eef6..e6002aa 100644 --- a/doc/source/classes/class_bbparam.rst +++ b/doc/source/classes/class_bbparam.rst @@ -49,11 +49,11 @@ Methods .. table:: :widths: auto - +-----------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------+ - | :ref:`Variant.Type` | :ref:`get_type` **(** **)** |const| | - +-----------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------+ - | Variant | :ref:`get_value` **(** Object agent, :ref:`Blackboard` blackboard, Variant default=null **)** | - +-----------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------+ + +-----------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------+ + | :ref:`Variant.Type` | :ref:`get_type` **(** **)** |const| | + +-----------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------+ + | Variant | :ref:`get_value` **(** Node scene_root, :ref:`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` blackboard, Variant default=null **)** +Variant **get_value** **(** Node scene_root, :ref:`Blackboard` blackboard, Variant default=null **)** Returns the value of the parameter. diff --git a/doc/source/classes/class_behaviortree.rst b/doc/source/classes/class_behaviortree.rst index b91f662..a97d85c 100644 --- a/doc/source/classes/class_behaviortree.rst +++ b/doc/source/classes/class_behaviortree.rst @@ -55,17 +55,17 @@ Methods .. table:: :widths: auto - +-----------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------+ - | :ref:`BehaviorTree` | :ref:`clone` **(** **)** |const| | - +-----------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------+ - | void | :ref:`copy_other` **(** :ref:`BehaviorTree` other **)** | - +-----------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------+ - | :ref:`BTTask` | :ref:`get_root_task` **(** **)** |const| | - +-----------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------+ - | :ref:`BTTask` | :ref:`instantiate` **(** Node agent, :ref:`Blackboard` blackboard **)** |const| | - +-----------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------+ - | void | :ref:`set_root_task` **(** :ref:`BTTask` task **)** | - +-----------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------+ + +-----------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+ + | :ref:`BehaviorTree` | :ref:`clone` **(** **)** |const| | + +-----------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+ + | void | :ref:`copy_other` **(** :ref:`BehaviorTree` other **)** | + +-----------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+ + | :ref:`BTTask` | :ref:`get_root_task` **(** **)** |const| | + +-----------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+ + | :ref:`BTTask` | :ref:`instantiate` **(** Node agent, :ref:`Blackboard` blackboard, Node scene_root **)** |const| | + +-----------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+ + | void | :ref:`set_root_task` **(** :ref:`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` **instantiate** **(** Node agent, :ref:`Blackboard` blackboard **)** |const| +:ref:`BTTask` **instantiate** **(** Node agent, :ref:`Blackboard` blackboard, Node scene_root **)** |const| -Instantiates the Behavior Tree and returns the root :ref:`BTTask`. +Instantiates the behavior tree and returns the root :ref:`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 diff --git a/doc/source/classes/class_blackboard.rst b/doc/source/classes/class_blackboard.rst index 8661ca4..824480f 100644 --- a/doc/source/classes/class_blackboard.rst +++ b/doc/source/classes/class_blackboard.rst @@ -33,27 +33,27 @@ Methods .. table:: :widths: auto - +-------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ - | void | :ref:`bind_var_to_property` **(** StringName var_name, Object object, StringName property, bool create **)** | - +-------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ - | void | :ref:`erase_var` **(** StringName var_name **)** | - +-------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ - | :ref:`Blackboard` | :ref:`get_parent` **(** **)** |const| | - +-------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ - | Variant | :ref:`get_var` **(** StringName var_name, Variant default=null, bool complain=true **)** |const| | - +-------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ - | bool | :ref:`has_var` **(** StringName var_name **)** |const| | - +-------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ - | void | :ref:`link_var` **(** StringName var_name, :ref:`Blackboard` target_blackboard, StringName target_var, bool create **)** | - +-------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ - | void | :ref:`set_parent` **(** :ref:`Blackboard` blackboard **)** | - +-------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ - | void | :ref:`set_var` **(** StringName var_name, Variant value **)** | - +-------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ - | :ref:`Blackboard` | :ref:`top` **(** **)** |const| | - +-------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ - | void | :ref:`unbind_var` **(** StringName var_name **)** | - +-------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + +-------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + | void | :ref:`bind_var_to_property` **(** StringName var_name, Object object, StringName property, bool create=false **)** | + +-------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + | void | :ref:`erase_var` **(** StringName var_name **)** | + +-------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + | :ref:`Blackboard` | :ref:`get_parent` **(** **)** |const| | + +-------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + | Variant | :ref:`get_var` **(** StringName var_name, Variant default=null, bool complain=true **)** |const| | + +-------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + | bool | :ref:`has_var` **(** StringName var_name **)** |const| | + +-------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + | void | :ref:`link_var` **(** StringName var_name, :ref:`Blackboard` target_blackboard, StringName target_var, bool create=false **)** | + +-------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + | void | :ref:`set_parent` **(** :ref:`Blackboard` blackboard **)** | + +-------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + | void | :ref:`set_var` **(** StringName var_name, Variant value **)** | + +-------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + | :ref:`Blackboard` | :ref:`top` **(** **)** |const| | + +-------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + | void | :ref:`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` target_blackboard, StringName target_var, bool create **)** +void **link_var** **(** StringName var_name, :ref:`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. diff --git a/doc/source/classes/class_btplayer.rst b/doc/source/classes/class_btplayer.rst index 8678139..067940e 100644 --- a/doc/source/classes/class_btplayer.rst +++ b/doc/source/classes/class_btplayer.rst @@ -31,19 +31,21 @@ Properties .. table:: :widths: auto - +---------------------------------------------+-------------------------------------------------------------------------+-----------+ - | bool | :ref:`active` | ``true`` | - +---------------------------------------------+-------------------------------------------------------------------------+-----------+ - | :ref:`BehaviorTree` | :ref:`behavior_tree` | | - +---------------------------------------------+-------------------------------------------------------------------------+-----------+ - | :ref:`Blackboard` | :ref:`blackboard` | | - +---------------------------------------------+-------------------------------------------------------------------------+-----------+ - | :ref:`BlackboardPlan` | :ref:`blackboard_plan` | | - +---------------------------------------------+-------------------------------------------------------------------------+-----------+ - | bool | :ref:`monitor_performance` | ``false`` | - +---------------------------------------------+-------------------------------------------------------------------------+-----------+ - | :ref:`UpdateMode` | :ref:`update_mode` | ``1`` | - +---------------------------------------------+-------------------------------------------------------------------------+-----------+ + +---------------------------------------------+-------------------------------------------------------------------------+--------------------+ + | bool | :ref:`active` | ``true`` | + +---------------------------------------------+-------------------------------------------------------------------------+--------------------+ + | NodePath | :ref:`agent_node` | ``NodePath("..")`` | + +---------------------------------------------+-------------------------------------------------------------------------+--------------------+ + | :ref:`BehaviorTree` | :ref:`behavior_tree` | | + +---------------------------------------------+-------------------------------------------------------------------------+--------------------+ + | :ref:`Blackboard` | :ref:`blackboard` | | + +---------------------------------------------+-------------------------------------------------------------------------+--------------------+ + | :ref:`BlackboardPlan` | :ref:`blackboard_plan` | | + +---------------------------------------------+-------------------------------------------------------------------------+--------------------+ + | bool | :ref:`monitor_performance` | ``false`` | + +---------------------------------------------+-------------------------------------------------------------------------+--------------------+ + | :ref:`UpdateMode` | :ref:`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 diff --git a/doc/source/classes/class_bttask.rst b/doc/source/classes/class_bttask.rst index b448e87..cc9b4ea 100644 --- a/doc/source/classes/class_bttask.rst +++ b/doc/source/classes/class_bttask.rst @@ -46,6 +46,8 @@ Properties +-------------------------------------+---------------------------------------------------------+--------+ | float | :ref:`elapsed_time` | | +-------------------------------------+---------------------------------------------------------+--------+ + | Node | :ref:`scene_root` | | + +-------------------------------------+---------------------------------------------------------+--------+ | :ref:`Status` | :ref:`status` | | +-------------------------------------+---------------------------------------------------------+--------+ @@ -57,59 +59,59 @@ Methods .. table:: :widths: auto - +-------------------------------+---------------------------------------------------------------------------------------------------------------------------+ - | void | :ref:`_enter` **(** **)** |virtual| | - +-------------------------------+---------------------------------------------------------------------------------------------------------------------------+ - | void | :ref:`_exit` **(** **)** |virtual| | - +-------------------------------+---------------------------------------------------------------------------------------------------------------------------+ - | String | :ref:`_generate_name` **(** **)** |virtual| |const| | - +-------------------------------+---------------------------------------------------------------------------------------------------------------------------+ - | PackedStringArray | :ref:`_get_configuration_warnings` **(** **)** |virtual| |const| | - +-------------------------------+---------------------------------------------------------------------------------------------------------------------------+ - | void | :ref:`_setup` **(** **)** |virtual| | - +-------------------------------+---------------------------------------------------------------------------------------------------------------------------+ - | :ref:`Status` | :ref:`_tick` **(** float delta **)** |virtual| | - +-------------------------------+---------------------------------------------------------------------------------------------------------------------------+ - | void | :ref:`abort` **(** **)** | - +-------------------------------+---------------------------------------------------------------------------------------------------------------------------+ - | void | :ref:`add_child` **(** :ref:`BTTask` task **)** | - +-------------------------------+---------------------------------------------------------------------------------------------------------------------------+ - | void | :ref:`add_child_at_index` **(** :ref:`BTTask` task, int idx **)** | - +-------------------------------+---------------------------------------------------------------------------------------------------------------------------+ - | :ref:`BTTask` | :ref:`clone` **(** **)** |const| | - +-------------------------------+---------------------------------------------------------------------------------------------------------------------------+ - | :ref:`Status` | :ref:`execute` **(** float delta **)** | - +-------------------------------+---------------------------------------------------------------------------------------------------------------------------+ - | :ref:`BTTask` | :ref:`get_child` **(** int idx **)** |const| | - +-------------------------------+---------------------------------------------------------------------------------------------------------------------------+ - | int | :ref:`get_child_count` **(** **)** |const| | - +-------------------------------+---------------------------------------------------------------------------------------------------------------------------+ - | int | :ref:`get_child_count_excluding_comments` **(** **)** |const| | - +-------------------------------+---------------------------------------------------------------------------------------------------------------------------+ - | int | :ref:`get_index` **(** **)** |const| | - +-------------------------------+---------------------------------------------------------------------------------------------------------------------------+ - | :ref:`BTTask` | :ref:`get_parent` **(** **)** |const| | - +-------------------------------+---------------------------------------------------------------------------------------------------------------------------+ - | :ref:`BTTask` | :ref:`get_root` **(** **)** |const| | - +-------------------------------+---------------------------------------------------------------------------------------------------------------------------+ - | String | :ref:`get_task_name` **(** **)** | - +-------------------------------+---------------------------------------------------------------------------------------------------------------------------+ - | bool | :ref:`has_child` **(** :ref:`BTTask` task **)** |const| | - +-------------------------------+---------------------------------------------------------------------------------------------------------------------------+ - | void | :ref:`initialize` **(** Node agent, :ref:`Blackboard` blackboard **)** | - +-------------------------------+---------------------------------------------------------------------------------------------------------------------------+ - | bool | :ref:`is_descendant_of` **(** :ref:`BTTask` task **)** |const| | - +-------------------------------+---------------------------------------------------------------------------------------------------------------------------+ - | bool | :ref:`is_root` **(** **)** |const| | - +-------------------------------+---------------------------------------------------------------------------------------------------------------------------+ - | :ref:`BTTask` | :ref:`next_sibling` **(** **)** |const| | - +-------------------------------+---------------------------------------------------------------------------------------------------------------------------+ - | void | :ref:`print_tree` **(** int initial_tabs=0 **)** | - +-------------------------------+---------------------------------------------------------------------------------------------------------------------------+ - | void | :ref:`remove_child` **(** :ref:`BTTask` task **)** | - +-------------------------------+---------------------------------------------------------------------------------------------------------------------------+ - | void | :ref:`remove_child_at_index` **(** int idx **)** | - +-------------------------------+---------------------------------------------------------------------------------------------------------------------------+ + +-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ + | void | :ref:`_enter` **(** **)** |virtual| | + +-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ + | void | :ref:`_exit` **(** **)** |virtual| | + +-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ + | String | :ref:`_generate_name` **(** **)** |virtual| |const| | + +-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ + | PackedStringArray | :ref:`_get_configuration_warnings` **(** **)** |virtual| |const| | + +-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ + | void | :ref:`_setup` **(** **)** |virtual| | + +-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ + | :ref:`Status` | :ref:`_tick` **(** float delta **)** |virtual| | + +-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ + | void | :ref:`abort` **(** **)** | + +-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ + | void | :ref:`add_child` **(** :ref:`BTTask` task **)** | + +-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ + | void | :ref:`add_child_at_index` **(** :ref:`BTTask` task, int idx **)** | + +-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ + | :ref:`BTTask` | :ref:`clone` **(** **)** |const| | + +-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ + | :ref:`Status` | :ref:`execute` **(** float delta **)** | + +-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ + | :ref:`BTTask` | :ref:`get_child` **(** int idx **)** |const| | + +-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ + | int | :ref:`get_child_count` **(** **)** |const| | + +-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ + | int | :ref:`get_child_count_excluding_comments` **(** **)** |const| | + +-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ + | int | :ref:`get_index` **(** **)** |const| | + +-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ + | :ref:`BTTask` | :ref:`get_parent` **(** **)** |const| | + +-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ + | :ref:`BTTask` | :ref:`get_root` **(** **)** |const| | + +-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ + | String | :ref:`get_task_name` **(** **)** | + +-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ + | bool | :ref:`has_child` **(** :ref:`BTTask` task **)** |const| | + +-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ + | void | :ref:`initialize` **(** Node agent, :ref:`Blackboard` blackboard, Node scene_root **)** | + +-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ + | bool | :ref:`is_descendant_of` **(** :ref:`BTTask` task **)** |const| | + +-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ + | bool | :ref:`is_root` **(** **)** |const| | + +-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ + | :ref:`BTTask` | :ref:`next_sibling` **(** **)** |const| | + +-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ + | void | :ref:`print_tree` **(** int initial_tabs=0 **)** | + +-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ + | void | :ref:`remove_child` **(** :ref:`BTTask` task **)** | + +-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ + | void | :ref:`remove_child_at_index` **(** int idx **)** | + +-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ .. rst-class:: classref-section-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 node that contains the behavior tree). 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` blackboard **)** +void **initialize** **(** Node agent, :ref:`Blackboard` blackboard, Node scene_root **)** Initilizes the task. Assigns :ref:`agent` and :ref:`blackboard`, and calls :ref:`_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 diff --git a/doc_classes/BBParam.xml b/doc_classes/BBParam.xml index 85e015a..cfd8e76 100644 --- a/doc_classes/BBParam.xml +++ b/doc_classes/BBParam.xml @@ -18,7 +18,7 @@ - + diff --git a/doc_classes/BTPlayer.xml b/doc_classes/BTPlayer.xml index ee232ca..2829e5a 100644 --- a/doc_classes/BTPlayer.xml +++ b/doc_classes/BTPlayer.xml @@ -40,6 +40,9 @@ If [code]true[/code], the behavior tree will be executed during update. + + Path to the node that will be used as the agent. Setting it after instantiation will have no effect. + [BehaviorTree] resource to instantiate and execute at runtime. diff --git a/doc_classes/BTTask.xml b/doc_classes/BTTask.xml index 60f1ae9..0c6720b 100644 --- a/doc_classes/BTTask.xml +++ b/doc_classes/BTTask.xml @@ -144,9 +144,10 @@ + 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). @@ -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]. + + Root node of the scene the behavior tree is used in (e.g., the owner of the node that contains the behavior tree). 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] + Last execution [enum BT.Status] returned by [method _tick]. diff --git a/doc_classes/BehaviorTree.xml b/doc_classes/BehaviorTree.xml index e5f9645..7cd61fc 100644 --- a/doc_classes/BehaviorTree.xml +++ b/doc_classes/BehaviorTree.xml @@ -38,8 +38,9 @@ + - 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). diff --git a/doc_classes/Blackboard.xml b/doc_classes/Blackboard.xml index b50e562..bde4856 100644 --- a/doc_classes/Blackboard.xml +++ b/doc_classes/Blackboard.xml @@ -16,7 +16,7 @@ - + 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. @@ -55,7 +55,7 @@ - + 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. From e36ea6d3e687ecb308b4296f25145afbb6b4e89b Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Thu, 2 May 2024 14:10:29 +0200 Subject: [PATCH 7/8] Better error handling in BTState, BTPlayer & BehaviorTree --- bt/behavior_tree.cpp | 2 ++ bt/bt_player.cpp | 2 +- bt/bt_state.cpp | 4 +++- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/bt/behavior_tree.cpp b/bt/behavior_tree.cpp index 5bd2a69..817b8a6 100644 --- a/bt/behavior_tree.cpp +++ b/bt/behavior_tree.cpp @@ -73,6 +73,8 @@ void BehaviorTree::copy_other(const Ref &p_other) { Ref BehaviorTree::instantiate(Node *p_agent, const Ref &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 inst = root_task->clone(); inst->initialize(p_agent, p_blackboard, p_scene_root); return inst; diff --git a/bt/bt_player.cpp b/bt/bt_player.cpp index e491ec8..5a5ea0e 100644 --- a/bt/bt_player.cpp +++ b/bt/bt_player.cpp @@ -55,7 +55,7 @@ void BTPlayer::_load_tree() { 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.owner is set)."); + 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()) { diff --git a/bt/bt_state.cpp b/bt/bt_state.cpp index 08c40a6..1a29f03 100644 --- a/bt/bt_state.cpp +++ b/bt/bt_state.cpp @@ -52,7 +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."); - tree_instance = behavior_tree->instantiate(get_agent(), get_blackboard(), get_owner()); + 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()) { From e5d04b9eda7b6988a7fe8c8fce2a5d990ca1592d Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Thu, 2 May 2024 19:35:42 +0200 Subject: [PATCH 8/8] Doc: Update doc pages and examples --- doc/source/classes/class_btplayer.rst | 2 +- doc/source/classes/class_bttask.rst | 4 ++-- doc/source/getting-started/accessing-nodes.rst | 13 ++++++++----- doc/source/getting-started/custom-tasks.rst | 2 +- doc/source/getting-started/using-blackboard.rst | 2 +- doc_classes/BTPlayer.xml | 2 +- doc_classes/BTTask.xml | 4 ++-- 7 files changed, 16 insertions(+), 13 deletions(-) diff --git a/doc/source/classes/class_btplayer.rst b/doc/source/classes/class_btplayer.rst index 067940e..790fa0f 100644 --- a/doc/source/classes/class_btplayer.rst +++ b/doc/source/classes/class_btplayer.rst @@ -19,7 +19,7 @@ Player of :ref:`BehaviorTree` resources. Description ----------- -BTPlayer node is used for the instantiation and playback of :ref:`BehaviorTree` resources at runtime. During instantiation, the behavior tree instance is initialized with a reference to the agent and the :ref:`blackboard`. Agent is the owner of the BTPlayer node (see :ref:`Node.owner`). +**BTPlayer** node is used to instantiate and play :ref:`BehaviorTree` resources at runtime. During initialization, the behavior tree instance is given references to the agent, the :ref:`blackboard`, and the current scene root. The agent can be specified by the :ref:`agent_node` property (defaults to the BTPlayer's parent node). For an introduction to behavior trees, see :ref:`BehaviorTree`. diff --git a/doc/source/classes/class_bttask.rst b/doc/source/classes/class_bttask.rst index cc9b4ea..999cd32 100644 --- a/doc/source/classes/class_bttask.rst +++ b/doc/source/classes/class_bttask.rst @@ -133,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` instance. Usually, agent is the owner of the :ref:`BTPlayer` node containing the :ref:`BehaviorTree` resource. +The agent is the contextual object for the :ref:`BehaviorTree` instance. This is usually the parent of the :ref:`BTPlayer` node that utilizes the :ref:`BehaviorTree` resource. .. rst-class:: classref-item-separator @@ -202,7 +202,7 @@ Node **scene_root** - Node **get_scene_root** **(** **)** -Root node of the scene the behavior tree is used in (e.g., the owner of the node that contains the behavior tree). Can be uses to retrieve ``NodePath`` references. +Root node of the scene the behavior tree is used in (e.g., the owner of the :ref:`BTPlayer` node). Can be uses to retrieve ``NodePath`` references. \ **Example:**\ diff --git a/doc/source/getting-started/accessing-nodes.rst b/doc/source/getting-started/accessing-nodes.rst index 11d8f00..882564e 100644 --- a/doc/source/getting-started/accessing-nodes.rst +++ b/doc/source/getting-started/accessing-nodes.rst @@ -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`. - **🛈 Note:** Agent is the owner of :ref:`BTPlayer` node. + **🛈 Note:** The root node of the agent's scene tree can be accessed with the + :ref:`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` in the editor with the type ``NodePath`` -and point it to the proper node in the :ref:`BTPlayer` blackboard plan. +and point it to the proper node in the :ref:`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`). .. code:: gdscript diff --git a/doc/source/getting-started/custom-tasks.rst b/doc/source/getting-started/custom-tasks.rst index 4cbc7c8..f1da51f 100644 --- a/doc/source/getting-started/custom-tasks.rst +++ b/doc/source/getting-started/custom-tasks.rst @@ -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 diff --git a/doc/source/getting-started/using-blackboard.rst b/doc/source/getting-started/using-blackboard.rst index 445ea6d..f957dbf 100644 --- a/doc/source/getting-started/using-blackboard.rst +++ b/doc/source/getting-started/using-blackboard.rst @@ -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 diff --git a/doc_classes/BTPlayer.xml b/doc_classes/BTPlayer.xml index 2829e5a..060a2f7 100644 --- a/doc_classes/BTPlayer.xml +++ b/doc_classes/BTPlayer.xml @@ -4,7 +4,7 @@ Player of [BehaviorTree] resources. - 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]. diff --git a/doc_classes/BTTask.xml b/doc_classes/BTTask.xml index 0c6720b..1c922ee 100644 --- a/doc_classes/BTTask.xml +++ b/doc_classes/BTTask.xml @@ -194,7 +194,7 @@ - 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. Provides access to the [Blackboard]. Blackboard is used to share data among tasks of the associated [BehaviorTree]. @@ -208,7 +208,7 @@ Returns [code]0[/code] when task is not [code]RUNNING[/code]. - Root node of the scene the behavior tree is used in (e.g., the owner of the node that contains the behavior tree). Can be uses to retrieve [NodePath] references. + 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