diff --git a/bt/behavior_tree.cpp b/bt/behavior_tree.cpp index 9daff79..64d5f4a 100644 --- a/bt/behavior_tree.cpp +++ b/bt/behavior_tree.cpp @@ -77,13 +77,13 @@ 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, Node *p_instance_owner) const { +Ref BehaviorTree::instantiate(Node *p_agent, const Ref &p_blackboard, Node *p_instance_owner, Node *p_custom_scene_root) const { 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())."); + Node *scene_root = p_custom_scene_root ? p_custom_scene_root : p_instance_owner->get_owner(); + ERR_FAIL_NULL_V_MSG(scene_root, nullptr, "BehaviorTree: Instantiation failed - unable to establish scene root. This is likely due to the instance owner not being owned by a scene node and custom_scene_root being null."); Ref root_copy = root_task->clone(); root_copy->initialize(p_agent, p_blackboard, scene_root); return BTInstance::create(root_copy, get_path(), p_instance_owner); @@ -119,7 +119,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", "instance_owner"), &BehaviorTree::instantiate); + ClassDB::bind_method(D_METHOD("instantiate", "agent", "blackboard", "instance_owner", "custom_scene_root"), &BehaviorTree::instantiate, DEFVAL(Variant())); 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/behavior_tree.h b/bt/behavior_tree.h index 58b2db7..d6944c1 100644 --- a/bt/behavior_tree.h +++ b/bt/behavior_tree.h @@ -59,7 +59,7 @@ public: Ref clone() const; void copy_other(const Ref &p_other); - Ref instantiate(Node *p_agent, const Ref &p_blackboard, Node *p_instance_owner) const; + Ref instantiate(Node *p_agent, const Ref &p_blackboard, Node *p_instance_owner, Node *p_custom_scene_root = nullptr) const; BehaviorTree(); ~BehaviorTree(); diff --git a/bt/bt_player.cpp b/bt/bt_player.cpp index 955592a..27687a4 100644 --- a/bt/bt_player.cpp +++ b/bt/bt_player.cpp @@ -48,8 +48,9 @@ void BTPlayer::_load_tree() { ERR_FAIL_COND_MSG(!behavior_tree->get_root_task().is_valid(), "BTPlayer: Initialization failed - behavior tree has no valid root task."); Node *agent = GET_NODE(this, agent_node); ERR_FAIL_NULL_MSG(agent, vformat("BTPlayer: Initialization failed - can't get agent with path '%s'.", agent_node)); - Node *scene_root = get_owner(); - ERR_FAIL_NULL_MSG(scene_root, "BTPlayer: Initialization failed - can't get scene root (make sure the BTPlayer's owner property is set)."); + Node *scene_root = scene_root_hint ? scene_root_hint : get_owner(); + ERR_FAIL_COND_MSG(scene_root == nullptr, + "BTPlayer: Initialization failed - unable to establish scene root. This is likely due to BTPlayer not being owned by a scene node. Check BTPlayer.set_scene_root_hint()."); bt_instance = behavior_tree->instantiate(agent, blackboard, this); ERR_FAIL_COND_MSG(bt_instance.is_null(), "BTPlayer: Failed to instantiate behavior tree."); #ifdef DEBUG_ENABLED @@ -81,6 +82,15 @@ void BTPlayer::set_bt_instance(const Ref &p_bt_instance) { behavior_tree.unref(); } +void BTPlayer::set_scene_root_hint(Node *p_scene_root) { + ERR_FAIL_NULL_MSG(p_scene_root, "BTPlayer: Failed to set scene root hint - scene root is null."); + if (bt_instance.is_valid()) { + ERR_PRINT("BTPlayer: Scene root hint shouldn't be set after the behavior tree is instantiated. This change will not affect the current behavior tree instance."); + } + + scene_root_hint = p_scene_root; +} + 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))) { @@ -231,6 +241,8 @@ void BTPlayer::_bind_methods() { 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); + ClassDB::bind_method(D_METHOD("set_scene_root_hint", "scene_root"), &BTPlayer::set_scene_root_hint); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "behavior_tree", PROPERTY_HINT_RESOURCE_TYPE, "BehaviorTree"), "set_behavior_tree", "get_behavior_tree"); ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "agent_node"), "set_agent_node", "get_agent_node"); ADD_PROPERTY(PropertyInfo(Variant::INT, "update_mode", PROPERTY_HINT_ENUM, "Idle,Physics,Manual"), "set_update_mode", "get_update_mode"); diff --git a/bt/bt_player.h b/bt/bt_player.h index 639dde1..325175c 100644 --- a/bt/bt_player.h +++ b/bt/bt_player.h @@ -43,6 +43,7 @@ private: UpdateMode update_mode = UpdateMode::PHYSICS; bool active = true; Ref blackboard; + Node *scene_root_hint = nullptr; Ref bt_instance; @@ -79,6 +80,8 @@ public: Ref get_bt_instance() { return bt_instance; } void set_bt_instance(const Ref &p_bt_instance); + void set_scene_root_hint(Node *p_scene_root); + BTPlayer(); ~BTPlayer(); diff --git a/bt/bt_state.cpp b/bt/bt_state.cpp index e482e27..9a4d6c5 100644 --- a/bt/bt_state.cpp +++ b/bt/bt_state.cpp @@ -37,6 +37,13 @@ void BTState::set_behavior_tree(const Ref &p_tree) { _update_blackboard_plan(); } +void BTState::set_scene_root_hint(Node *p_scene_root) { + ERR_FAIL_NULL_MSG(p_scene_root, "BTState: Failed to set scene root hint - scene root is null."); + ERR_FAIL_COND_MSG(bt_instance.is_valid(), "BTState: Scene root hint shouldn't be set after initialization. This change will not affect the current behavior tree instance."); + + scene_root_hint = p_scene_root; +} + #ifdef DEBUG_ENABLED void BTState::_set_monitor_performance(bool p_monitor) { monitor_performance = p_monitor; @@ -61,10 +68,10 @@ void BTState::_update_blackboard_plan() { void BTState::_setup() { LimboState::_setup(); ERR_FAIL_COND_MSG(behavior_tree.is_null(), "BTState: BehaviorTree is not assigned."); - 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)."); + Node *scene_root = scene_root_hint ? scene_root_hint : get_owner(); + ERR_FAIL_NULL_MSG(scene_root, "BTState: Initialization failed - unable to establish scene root. This is likely due to BTState not being owned by a scene node. Check BTState.set_scene_root_hint()."); bt_instance = behavior_tree->instantiate(get_agent(), get_blackboard(), this); - ERR_FAIL_COND_MSG(bt_instance.is_null(), "BTState: Initialization failed - can't instantiate behavior tree."); + ERR_FAIL_COND_MSG(bt_instance.is_null(), "BTState: Initialization failed - failed to instantiate behavior tree."); #ifdef DEBUG_ENABLED bt_instance->register_with_debugger(); @@ -137,6 +144,8 @@ void BTState::_bind_methods() { ClassDB::bind_method(D_METHOD("set_failure_event", "event"), &BTState::set_failure_event); ClassDB::bind_method(D_METHOD("get_failure_event"), &BTState::get_failure_event); + ClassDB::bind_method(D_METHOD("set_scene_root_hint", "scene_root"), &BTState::set_scene_root_hint); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "behavior_tree", PROPERTY_HINT_RESOURCE_TYPE, "BehaviorTree"), "set_behavior_tree", "get_behavior_tree"); ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "success_event"), "set_success_event", "get_success_event"); ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "failure_event"), "set_failure_event", "get_failure_event"); diff --git a/bt/bt_state.h b/bt/bt_state.h index f601917..ed687a3 100644 --- a/bt/bt_state.h +++ b/bt/bt_state.h @@ -25,6 +25,7 @@ private: Ref bt_instance; StringName success_event; StringName failure_event; + Node *scene_root_hint = nullptr; protected: static void _bind_methods(); @@ -50,6 +51,8 @@ public: void set_failure_event(const StringName &p_failure_event) { failure_event = p_failure_event; } StringName get_failure_event() const { return failure_event; } + void set_scene_root_hint(Node *p_node); + BTState(); #ifdef DEBUG_ENABLED diff --git a/doc/source/classes/class_behaviortree.rst b/doc/source/classes/class_behaviortree.rst index c773137..c066836 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`\ (\ other\: :ref:`BehaviorTree`\ ) | - +-----------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ - | :ref:`BTTask` | :ref:`get_root_task`\ (\ ) |const| | - +-----------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ - | :ref:`BTInstance` | :ref:`instantiate`\ (\ agent\: ``Node``, blackboard\: :ref:`Blackboard`, instance_owner\: ``Node``\ ) |const| | - +-----------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ - | |void| | :ref:`set_root_task`\ (\ task\: :ref:`BTTask`\ ) | - +-----------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + +-----------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + | :ref:`BehaviorTree` | :ref:`clone`\ (\ ) |const| | + +-----------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + | |void| | :ref:`copy_other`\ (\ other\: :ref:`BehaviorTree`\ ) | + +-----------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + | :ref:`BTTask` | :ref:`get_root_task`\ (\ ) |const| | + +-----------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + | :ref:`BTInstance` | :ref:`instantiate`\ (\ agent\: ``Node``, blackboard\: :ref:`Blackboard`, instance_owner\: ``Node``, custom_scene_root\: ``Node`` = null\ ) |const| | + +-----------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + | |void| | :ref:`set_root_task`\ (\ task\: :ref:`BTTask`\ ) | + +-----------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ .. rst-class:: classref-section-separator @@ -172,9 +172,11 @@ Returns the root task of the BehaviorTree resource. .. rst-class:: classref-method -:ref:`BTInstance` **instantiate**\ (\ agent\: ``Node``, blackboard\: :ref:`Blackboard`, instance_owner\: ``Node``\ ) |const| :ref:`🔗` +:ref:`BTInstance` **instantiate**\ (\ agent\: ``Node``, blackboard\: :ref:`Blackboard`, instance_owner\: ``Node``, custom_scene_root\: ``Node`` = null\ ) |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. Make sure to pass a :ref:`Blackboard` with values populated from :ref:`blackboard_plan`. See also ``BlackboardPlan.populate_blackboard`` & ``BlackboardPlan.create_blackboard``. +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 :ref:`BlackboardPlan.populate_blackboard` & :ref:`BlackboardPlan.create_blackboard`. + +If ``custom_scene_root`` is not ``null``, it will be used as the scene root for the newly instantiated behavior tree; otherwise, the scene root will be set to ``instance_owner.owner``. Scene root is essential for :ref:`BBNode` instances to work properly. .. rst-class:: classref-item-separator diff --git a/doc/source/classes/class_btplayer.rst b/doc/source/classes/class_btplayer.rst index e4a6b8d..60bd8c7 100644 --- a/doc/source/classes/class_btplayer.rst +++ b/doc/source/classes/class_btplayer.rst @@ -62,6 +62,8 @@ Methods +-------------------------------------+------------------------------------------------------------------------------------------------------------------------+ | |void| | :ref:`set_bt_instance`\ (\ bt_instance\: :ref:`BTInstance`\ ) | +-------------------------------------+------------------------------------------------------------------------------------------------------------------------+ + | |void| | :ref:`set_scene_root_hint`\ (\ scene_root\: ``Node``\ ) | + +-------------------------------------+------------------------------------------------------------------------------------------------------------------------+ | |void| | :ref:`update`\ (\ delta\: ``float``\ ) | +-------------------------------------+------------------------------------------------------------------------------------------------------------------------+ @@ -297,7 +299,19 @@ Resets the behavior tree's execution. Each running task will be aborted and the |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`. +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_set_scene_root_hint: + +.. rst-class:: classref-method + +|void| **set_scene_root_hint**\ (\ scene_root\: ``Node``\ ) :ref:`🔗` + +Sets the ``Node`` that will be used as the scene root for the newly instantiated behavior tree. Should be called before the **BTPlayer** is added to the scene tree (before ``NOTIFICATION_READY``). This is typically useful when creating **BTPlayer** nodes dynamically from code. .. rst-class:: classref-item-separator diff --git a/doc/source/classes/class_btstate.rst b/doc/source/classes/class_btstate.rst index c72c2a3..b9fdcef 100644 --- a/doc/source/classes/class_btstate.rst +++ b/doc/source/classes/class_btstate.rst @@ -47,9 +47,11 @@ Methods .. table:: :widths: auto - +-------------------------------------+----------------------------------------------------------------------------+ - | :ref:`BTInstance` | :ref:`get_bt_instance`\ (\ ) |const| | - +-------------------------------------+----------------------------------------------------------------------------+ + +-------------------------------------+---------------------------------------------------------------------------------------------------+ + | :ref:`BTInstance` | :ref:`get_bt_instance`\ (\ ) |const| | + +-------------------------------------+---------------------------------------------------------------------------------------------------+ + | |void| | :ref:`set_scene_root_hint`\ (\ scene_root\: ``Node``\ ) | + +-------------------------------------+---------------------------------------------------------------------------------------------------+ .. rst-class:: classref-section-separator @@ -136,6 +138,18 @@ Method Descriptions Returns the behavior tree instance. +.. rst-class:: classref-item-separator + +---- + +.. _class_BTState_method_set_scene_root_hint: + +.. rst-class:: classref-method + +|void| **set_scene_root_hint**\ (\ scene_root\: ``Node``\ ) :ref:`🔗` + +Sets the ``Node`` that will be used as the scene root for the newly instantiated behavior tree. Should be called before the state machine is initialized. This is typically useful when creating **BTState** nodes dynamically from code. + .. |virtual| replace:: :abbr:`virtual (This method should typically be overridden by the user to have any effect.)` .. |const| replace:: :abbr:`const (This method has no side effects. It doesn't modify any of the instance's member variables.)` .. |vararg| replace:: :abbr:`vararg (This method accepts any number of arguments after the ones described here.)` diff --git a/doc_classes/BTPlayer.xml b/doc_classes/BTPlayer.xml index 431cdfb..7ce6ffd 100644 --- a/doc_classes/BTPlayer.xml +++ b/doc_classes/BTPlayer.xml @@ -26,7 +26,14 @@ - 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]. + Sets the [BTInstance] to play. This method is useful when you want to switch to a different behavior tree instance at runtime. See also [method BehaviorTree.instantiate]. + + + + + + + Sets the [Node] that will be used as the scene root for the newly instantiated behavior tree. Should be called before the [BTPlayer] is added to the scene tree (before [code]NOTIFICATION_READY[/code]). This is typically useful when creating [BTPlayer] nodes dynamically from code. diff --git a/doc_classes/BTState.xml b/doc_classes/BTState.xml index 7c72b13..d9d644d 100644 --- a/doc_classes/BTState.xml +++ b/doc_classes/BTState.xml @@ -15,6 +15,13 @@ Returns the behavior tree instance. + + + + + Sets the [Node] that will be used as the scene root for the newly instantiated behavior tree. Should be called before the state machine is initialized. This is typically useful when creating [BTState] nodes dynamically from code. + + diff --git a/doc_classes/BehaviorTree.xml b/doc_classes/BehaviorTree.xml index bc30c5d..f2a4f2d 100644 --- a/doc_classes/BehaviorTree.xml +++ b/doc_classes/BehaviorTree.xml @@ -39,8 +39,10 @@ + - 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]. + 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 [method BlackboardPlan.populate_blackboard] & [method BlackboardPlan.create_blackboard]. + If [param custom_scene_root] is not [code]null[/code], it will be used as the scene root for the newly instantiated behavior tree; otherwise, the scene root will be set to [code]instance_owner.owner[/code]. Scene root is essential for [BBNode] instances to work properly.