From ee12a56e964dcd78364adc3833c9d8b91df3d8ab Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Sun, 4 Aug 2024 12:36:44 +0200 Subject: [PATCH 1/2] BTPlayer: Allow changing BT instance at runtime --- bt/bt_instance.cpp | 4 ++ bt/bt_instance.h | 5 +- bt/bt_player.cpp | 13 ++++ bt/bt_player.h | 1 + doc/source/classes/class_behaviortree.rst | 2 +- doc/source/classes/class_btinstance.rst | 72 ++++++++++++++++++----- doc/source/classes/class_btplayer.rst | 28 ++++++--- doc_classes/BTInstance.xml | 18 ++++++ doc_classes/BTPlayer.xml | 7 +++ doc_classes/BehaviorTree.xml | 2 +- 10 files changed, 127 insertions(+), 25 deletions(-) diff --git a/bt/bt_instance.cpp b/bt/bt_instance.cpp index 50011b8..dbf0be5 100644 --- a/bt/bt_instance.cpp +++ b/bt/bt_instance.cpp @@ -127,6 +127,10 @@ void BTInstance::_bind_methods() { ClassDB::bind_method(D_METHOD("get_owner_node"), &BTInstance::get_owner_node); ClassDB::bind_method(D_METHOD("get_last_status"), &BTInstance::get_last_status); ClassDB::bind_method(D_METHOD("get_source_bt_path"), &BTInstance::get_source_bt_path); + ClassDB::bind_method(D_METHOD("get_agent"), &BTInstance::get_agent); + ClassDB::bind_method(D_METHOD("get_blackboard"), &BTInstance::get_blackboard); + + ClassDB::bind_method(D_METHOD("is_instance_valid"), &BTInstance::is_instance_valid); ClassDB::bind_method(D_METHOD("set_monitor_performance", "monitor"), &BTInstance::set_monitor_performance); ClassDB::bind_method(D_METHOD("get_monitor_performance"), &BTInstance::get_monitor_performance); diff --git a/bt/bt_instance.h b/bt/bt_instance.h index 83dbf78..ad75915 100644 --- a/bt/bt_instance.h +++ b/bt/bt_instance.h @@ -41,8 +41,11 @@ public: _FORCE_INLINE_ Ref get_root_task() const { return root_task; } _FORCE_INLINE_ Node *get_owner_node() const { return owner_node_id ? Object::cast_to(OBJECT_DB_GET_INSTANCE(owner_node_id)) : nullptr; } _FORCE_INLINE_ BT::Status get_last_status() const { return last_status; } - _FORCE_INLINE_ bool is_instance_valid() const { return root_task.is_valid(); } _FORCE_INLINE_ String get_source_bt_path() const { return source_bt_path; } + _FORCE_INLINE_ Node *get_agent() const { return root_task.is_valid() ? root_task->get_agent() : nullptr; } + _FORCE_INLINE_ Ref get_blackboard() const { return root_task.is_valid() ? root_task->get_blackboard() : Ref(); } + + _FORCE_INLINE_ bool is_instance_valid() const { return root_task.is_valid(); } BT::Status update(double p_delta); diff --git a/bt/bt_player.cpp b/bt/bt_player.cpp index 72d1736..955592a 100644 --- a/bt/bt_player.cpp +++ b/bt/bt_player.cpp @@ -69,6 +69,18 @@ void BTPlayer::_update_blackboard_plan() { blackboard_plan->set_base_plan(behavior_tree.is_valid() ? behavior_tree->get_blackboard_plan() : nullptr); } +void BTPlayer::set_bt_instance(const Ref &p_bt_instance) { + ERR_FAIL_COND_MSG(p_bt_instance.is_null(), "BTPlayer: Failed to set behavior tree instance - instance is null."); + ERR_FAIL_COND_MSG(!p_bt_instance->is_instance_valid(), "BTPlayer: Failed to set behavior tree instance - instance is not valid."); + + bt_instance = p_bt_instance; + blackboard = p_bt_instance->get_blackboard(); + agent_node = p_bt_instance->get_agent()->get_path(); + + blackboard_plan.unref(); + behavior_tree.unref(); +} + void BTPlayer::set_behavior_tree(const Ref &p_tree) { if (Engine::get_singleton()->is_editor_hint()) { if (behavior_tree.is_valid() && behavior_tree->is_connected(LW_NAME(plan_changed), callable_mp(this, &BTPlayer::_update_blackboard_plan))) { @@ -217,6 +229,7 @@ void BTPlayer::_bind_methods() { ClassDB::bind_method(D_METHOD("restart"), &BTPlayer::restart); ClassDB::bind_method(D_METHOD("get_bt_instance"), &BTPlayer::get_bt_instance); + ClassDB::bind_method(D_METHOD("set_bt_instance", "bt_instance"), &BTPlayer::set_bt_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"); diff --git a/bt/bt_player.h b/bt/bt_player.h index 62f6d9f..639dde1 100644 --- a/bt/bt_player.h +++ b/bt/bt_player.h @@ -77,6 +77,7 @@ public: void restart(); Ref get_bt_instance() { return bt_instance; } + void set_bt_instance(const Ref &p_bt_instance); BTPlayer(); ~BTPlayer(); diff --git a/doc/source/classes/class_behaviortree.rst b/doc/source/classes/class_behaviortree.rst index f35f715..c773137 100644 --- a/doc/source/classes/class_behaviortree.rst +++ b/doc/source/classes/class_behaviortree.rst @@ -174,7 +174,7 @@ Returns the root task of the BehaviorTree resource. :ref:`BTInstance` **instantiate**\ (\ agent\: ``Node``, blackboard\: :ref:`Blackboard`, instance_owner\: ``Node``\ ) |const| :ref:`🔗` -Instantiates the behavior tree and returns :ref:`BTInstance`. ``instance_owner`` should be the scene node that will own the behavior tree instance. This is typically a :ref:`BTPlayer`, :ref:`BTState`, or a custom player node that controls the behavior tree execution. +Instantiates the behavior tree and returns :ref:`BTInstance`. ``instance_owner`` should be the scene node that will own the behavior tree instance. This is typically a :ref:`BTPlayer`, :ref:`BTState`, or a custom player node that controls the behavior tree execution. Make sure to pass a :ref:`Blackboard` with values populated from :ref:`blackboard_plan`. See also ``BlackboardPlan.populate_blackboard`` & ``BlackboardPlan.create_blackboard``. .. rst-class:: classref-item-separator diff --git a/doc/source/classes/class_btinstance.rst b/doc/source/classes/class_btinstance.rst index 0a698f5..4f84a7d 100644 --- a/doc/source/classes/class_btinstance.rst +++ b/doc/source/classes/class_btinstance.rst @@ -41,21 +41,27 @@ Methods .. table:: :widths: auto - +-------------------------------+-----------------------------------------------------------------------------------------+ - | :ref:`Status` | :ref:`get_last_status`\ (\ ) |const| | - +-------------------------------+-----------------------------------------------------------------------------------------+ - | ``Node`` | :ref:`get_owner_node`\ (\ ) |const| | - +-------------------------------+-----------------------------------------------------------------------------------------+ - | :ref:`BTTask` | :ref:`get_root_task`\ (\ ) |const| | - +-------------------------------+-----------------------------------------------------------------------------------------+ - | ``String`` | :ref:`get_source_bt_path`\ (\ ) |const| | - +-------------------------------+-----------------------------------------------------------------------------------------+ - | |void| | :ref:`register_with_debugger`\ (\ ) | - +-------------------------------+-----------------------------------------------------------------------------------------+ - | |void| | :ref:`unregister_with_debugger`\ (\ ) | - +-------------------------------+-----------------------------------------------------------------------------------------+ - | :ref:`Status` | :ref:`update`\ (\ delta\: ``float``\ ) | - +-------------------------------+-----------------------------------------------------------------------------------------+ + +-------------------------------------+-----------------------------------------------------------------------------------------+ + | ``Node`` | :ref:`get_agent`\ (\ ) |const| | + +-------------------------------------+-----------------------------------------------------------------------------------------+ + | :ref:`Blackboard` | :ref:`get_blackboard`\ (\ ) |const| | + +-------------------------------------+-----------------------------------------------------------------------------------------+ + | :ref:`Status` | :ref:`get_last_status`\ (\ ) |const| | + +-------------------------------------+-----------------------------------------------------------------------------------------+ + | ``Node`` | :ref:`get_owner_node`\ (\ ) |const| | + +-------------------------------------+-----------------------------------------------------------------------------------------+ + | :ref:`BTTask` | :ref:`get_root_task`\ (\ ) |const| | + +-------------------------------------+-----------------------------------------------------------------------------------------+ + | ``String`` | :ref:`get_source_bt_path`\ (\ ) |const| | + +-------------------------------------+-----------------------------------------------------------------------------------------+ + | ``bool`` | :ref:`is_instance_valid`\ (\ ) |const| | + +-------------------------------------+-----------------------------------------------------------------------------------------+ + | |void| | :ref:`register_with_debugger`\ (\ ) | + +-------------------------------------+-----------------------------------------------------------------------------------------+ + | |void| | :ref:`unregister_with_debugger`\ (\ ) | + +-------------------------------------+-----------------------------------------------------------------------------------------+ + | :ref:`Status` | :ref:`update`\ (\ delta\: ``float``\ ) | + +-------------------------------------+-----------------------------------------------------------------------------------------+ .. rst-class:: classref-section-separator @@ -117,6 +123,30 @@ If ``true``, adds a performance monitor for this instance to "Debugger->Monitors Method Descriptions ------------------- +.. _class_BTInstance_method_get_agent: + +.. rst-class:: classref-method + +``Node`` **get_agent**\ (\ ) |const| :ref:`🔗` + +Returns the agent of the behavior tree instance. + +.. rst-class:: classref-item-separator + +---- + +.. _class_BTInstance_method_get_blackboard: + +.. rst-class:: classref-method + +:ref:`Blackboard` **get_blackboard**\ (\ ) |const| :ref:`🔗` + +Returns the blackboard of the behavior tree instance. + +.. rst-class:: classref-item-separator + +---- + .. _class_BTInstance_method_get_last_status: .. rst-class:: classref-method @@ -165,6 +195,18 @@ Returns the file path to the behavior tree resource that was used to create this ---- +.. _class_BTInstance_method_is_instance_valid: + +.. rst-class:: classref-method + +``bool`` **is_instance_valid**\ (\ ) |const| :ref:`🔗` + +Returns ``true`` if the behavior tree instance is properly initialized and can be used. + +.. rst-class:: classref-item-separator + +---- + .. _class_BTInstance_method_register_with_debugger: .. rst-class:: classref-method diff --git a/doc/source/classes/class_btplayer.rst b/doc/source/classes/class_btplayer.rst index ed71cad..e4a6b8d 100644 --- a/doc/source/classes/class_btplayer.rst +++ b/doc/source/classes/class_btplayer.rst @@ -55,13 +55,15 @@ Methods .. table:: :widths: auto - +-------------------------------------+----------------------------------------------------------------------+ - | :ref:`BTInstance` | :ref:`get_bt_instance`\ (\ ) | - +-------------------------------------+----------------------------------------------------------------------+ - | |void| | :ref:`restart`\ (\ ) | - +-------------------------------------+----------------------------------------------------------------------+ - | |void| | :ref:`update`\ (\ delta\: ``float``\ ) | - +-------------------------------------+----------------------------------------------------------------------+ + +-------------------------------------+------------------------------------------------------------------------------------------------------------------------+ + | :ref:`BTInstance` | :ref:`get_bt_instance`\ (\ ) | + +-------------------------------------+------------------------------------------------------------------------------------------------------------------------+ + | |void| | :ref:`restart`\ (\ ) | + +-------------------------------------+------------------------------------------------------------------------------------------------------------------------+ + | |void| | :ref:`set_bt_instance`\ (\ bt_instance\: :ref:`BTInstance`\ ) | + +-------------------------------------+------------------------------------------------------------------------------------------------------------------------+ + | |void| | :ref:`update`\ (\ delta\: ``float``\ ) | + +-------------------------------------+------------------------------------------------------------------------------------------------------------------------+ .. rst-class:: classref-section-separator @@ -289,6 +291,18 @@ Resets the behavior tree's execution. Each running task will be aborted and the ---- +.. _class_BTPlayer_method_set_bt_instance: + +.. rst-class:: classref-method + +|void| **set_bt_instance**\ (\ bt_instance\: :ref:`BTInstance`\ ) :ref:`🔗` + +Sets the :ref:`BTInstance` to play. This method is useful when you want to switch to a different behavior tree instance at runtime. See also :ref:`BehaviorTree.instantiate`. + +.. rst-class:: classref-item-separator + +---- + .. _class_BTPlayer_method_update: .. rst-class:: classref-method diff --git a/doc_classes/BTInstance.xml b/doc_classes/BTInstance.xml index faf2a24..a8dffb1 100644 --- a/doc_classes/BTInstance.xml +++ b/doc_classes/BTInstance.xml @@ -9,6 +9,18 @@ + + + + Returns the agent of the behavior tree instance. + + + + + + Returns the blackboard of the behavior tree instance. + + @@ -33,6 +45,12 @@ Returns the file path to the behavior tree resource that was used to create this instance. + + + + Returns [code]true[/code] if the behavior tree instance is properly initialized and can be used. + + diff --git a/doc_classes/BTPlayer.xml b/doc_classes/BTPlayer.xml index 5c0cb5c..431cdfb 100644 --- a/doc_classes/BTPlayer.xml +++ b/doc_classes/BTPlayer.xml @@ -22,6 +22,13 @@ Resets the behavior tree's execution. Each running task will be aborted and the next tree execution will start anew. This method does not reset [Blackboard]. + + + + + Sets the [BTInstance] to play. This method is useful when you want to switch to a different behavior tree instance at runtime. See also [member BehaviorTree.instantiate]. + + diff --git a/doc_classes/BehaviorTree.xml b/doc_classes/BehaviorTree.xml index 58a1fbf..bc30c5d 100644 --- a/doc_classes/BehaviorTree.xml +++ b/doc_classes/BehaviorTree.xml @@ -40,7 +40,7 @@ - Instantiates the behavior tree and returns [BTInstance]. [param instance_owner] should be the scene node that will own the behavior tree instance. This is typically a [BTPlayer], [BTState], or a custom player node that controls the behavior tree execution. + Instantiates the behavior tree and returns [BTInstance]. [param instance_owner] should be the scene node that will own the behavior tree instance. This is typically a [BTPlayer], [BTState], or a custom player node that controls the behavior tree execution. Make sure to pass a [Blackboard] with values populated from [member blackboard_plan]. See also [BlackboardPlan.populate_blackboard] & [BlackboardPlan.create_blackboard]. From 40863313dd1836e770e06b5c17204749b06e1296 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Sun, 4 Aug 2024 12:41:25 +0200 Subject: [PATCH 2/2] Check if blackboard is null in `BehaviorTree.instantiate()` --- bt/behavior_tree.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/bt/behavior_tree.cpp b/bt/behavior_tree.cpp index c6a5cf3..9daff79 100644 --- a/bt/behavior_tree.cpp +++ b/bt/behavior_tree.cpp @@ -81,6 +81,7 @@ Ref BehaviorTree::instantiate(Node *p_agent, const Ref & ERR_FAIL_COND_V_MSG(root_task == nullptr, nullptr, "BehaviorTree: Instantiation failed - BT has no valid root task."); ERR_FAIL_NULL_V_MSG(p_agent, nullptr, "BehaviorTree: Instantiation failed - agent can't be null."); ERR_FAIL_NULL_V_MSG(p_instance_owner, nullptr, "BehaviorTree: Instantiation failed -- instance owner can't be null."); + ERR_FAIL_NULL_V_MSG(p_blackboard, nullptr, "BehaviorTree: Instantiation failed - blackboard can't be null."); Node *scene_root = p_instance_owner->get_owner(); ERR_FAIL_NULL_V_MSG(scene_root, nullptr, "BehaviorTree: Instantiation failed - can't get scene root, because instance_owner not owned by a scene node. Hint: Try my_player.set_owner(get_owner())."); Ref root_copy = root_task->clone();