Add instancing to behavior tree

This commit is contained in:
Serhii Snitsaruk 2022-09-21 16:13:17 +02:00
parent 6cac198092
commit 7be7ca276d
7 changed files with 36 additions and 16 deletions

View File

@ -2,6 +2,7 @@
#include "behavior_tree.h" #include "behavior_tree.h"
#include "core/class_db.h" #include "core/class_db.h"
#include "core/error_macros.h"
#include "core/list.h" #include "core/list.h"
#include "core/object.h" #include "core/object.h"
#include "core/variant.h" #include "core/variant.h"
@ -32,6 +33,13 @@ Ref<BehaviorTree> BehaviorTree::clone() const {
return copy; return copy;
} }
Ref<BTTask> BehaviorTree::instance(Object *p_agent, const Ref<Blackboard> &p_blackboard) const {
ERR_FAIL_COND_V_MSG(root_task == nullptr, memnew(BTTask), "Trying to instance a behavior tree with no valid root task.");
Ref<BTTask> inst = root_task->clone();
inst->initialize(p_agent, p_blackboard);
return inst;
}
void BehaviorTree::_bind_methods() { void BehaviorTree::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_description", "p_value"), &BehaviorTree::set_description); ClassDB::bind_method(D_METHOD("set_description", "p_value"), &BehaviorTree::set_description);
ClassDB::bind_method(D_METHOD("get_description"), &BehaviorTree::get_description); ClassDB::bind_method(D_METHOD("get_description"), &BehaviorTree::get_description);
@ -39,6 +47,7 @@ void BehaviorTree::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_root_task"), &BehaviorTree::get_root_task); ClassDB::bind_method(D_METHOD("get_root_task"), &BehaviorTree::get_root_task);
ClassDB::bind_method(D_METHOD("init"), &BehaviorTree::init); ClassDB::bind_method(D_METHOD("init"), &BehaviorTree::init);
ClassDB::bind_method(D_METHOD("clone"), &BehaviorTree::clone); ClassDB::bind_method(D_METHOD("clone"), &BehaviorTree::clone);
ClassDB::bind_method(D_METHOD("instance", "p_agent", "p_blackboard"), &BehaviorTree::instance);
ADD_PROPERTY(PropertyInfo(Variant::STRING, "description", PROPERTY_HINT_MULTILINE_TEXT), "set_description", "get_description"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "description", PROPERTY_HINT_MULTILINE_TEXT), "set_description", "get_description");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "root_task", PROPERTY_HINT_RESOURCE_TYPE, "BTTask"), "set_root_task", "get_root_task"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "root_task", PROPERTY_HINT_RESOURCE_TYPE, "BTTask"), "set_root_task", "get_root_task");

View File

@ -3,10 +3,10 @@
#ifndef BEHAVIOR_TREE_H #ifndef BEHAVIOR_TREE_H
#define BEHAVIOR_TREE_H #define BEHAVIOR_TREE_H
#include "bt_task.h"
#include "core/object.h" #include "core/object.h"
#include "core/resource.h" #include "core/resource.h"
#include "modules/limboai/blackboard.h"
#include "bt_task.h"
class BehaviorTree : public Resource { class BehaviorTree : public Resource {
GDCLASS(BehaviorTree, Resource); GDCLASS(BehaviorTree, Resource);
@ -33,6 +33,7 @@ public:
void init(); void init();
Ref<BehaviorTree> clone() const; Ref<BehaviorTree> clone() const;
Ref<BTTask> instance(Object *p_agent, const Ref<Blackboard> &p_blackboard) const;
}; };
#endif // BEHAVIOR_TREE_H #endif // BEHAVIOR_TREE_H

View File

@ -21,8 +21,7 @@ void BTPlayer::_load_tree() {
ERR_FAIL_COND_MSG(!behavior_tree.is_valid(), "BTPlayer needs a valid behavior tree."); 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(), "Behavior tree has no valid root task."); ERR_FAIL_COND_MSG(!behavior_tree->get_root_task().is_valid(), "Behavior tree has no valid root task.");
_loaded_tree = behavior_tree; _loaded_tree = behavior_tree;
_root_task = _loaded_tree->get_root_task()->clone(); _root_task = _loaded_tree->instance(get_owner(), blackboard);
_root_task->initialize(get_owner(), blackboard);
} }
void BTPlayer::set_behavior_tree(const Ref<BehaviorTree> &p_tree) { void BTPlayer::set_behavior_tree(const Ref<BehaviorTree> &p_tree) {

View File

@ -93,7 +93,8 @@ void BTTask::initialize(Object *p_agent, const Ref<Blackboard> &p_blackboard) {
Ref<BTTask> BTTask::clone() const { Ref<BTTask> BTTask::clone() const {
Ref<BTTask> inst = duplicate(false); Ref<BTTask> inst = duplicate(false);
inst->parent = nullptr; inst->parent = nullptr;
CRASH_COND(inst->get_parent().is_valid()); inst->agent = nullptr;
inst->blackboard.unref();
for (int i = 0; i < children.size(); i++) { for (int i = 0; i < children.size(); i++) {
Ref<BTTask> c = get_child(i)->clone(); Ref<BTTask> c = get_child(i)->clone();
c->parent = inst.ptr(); c->parent = inst.ptr();
@ -186,6 +187,11 @@ void BTTask::remove_child(Ref<BTTask> p_child) {
} }
} }
void BTTask::remove_child_at_index(int p_idx) {
ERR_FAIL_INDEX(p_idx, get_child_count());
children.remove(p_idx);
}
bool BTTask::has_child(const Ref<BTTask> &p_child) const { bool BTTask::has_child(const Ref<BTTask> &p_child) const {
return children.find(p_child) != -1; return children.find(p_child) != -1;
} }
@ -277,6 +283,7 @@ void BTTask::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_child", "p_child"), &BTTask::add_child); ClassDB::bind_method(D_METHOD("add_child", "p_child"), &BTTask::add_child);
ClassDB::bind_method(D_METHOD("add_child_at_index", "p_child", "p_idx"), &BTTask::add_child_at_index); ClassDB::bind_method(D_METHOD("add_child_at_index", "p_child", "p_idx"), &BTTask::add_child_at_index);
ClassDB::bind_method(D_METHOD("remove_child", "p_child"), &BTTask::remove_child); ClassDB::bind_method(D_METHOD("remove_child", "p_child"), &BTTask::remove_child);
ClassDB::bind_method(D_METHOD("remove_child_at_index", "p_idx"), &BTTask::remove_child_at_index);
ClassDB::bind_method(D_METHOD("has_child", "p_child"), &BTTask::has_child); ClassDB::bind_method(D_METHOD("has_child", "p_child"), &BTTask::has_child);
ClassDB::bind_method(D_METHOD("is_descendant_of", "p_task"), &BTTask::is_descendant_of); ClassDB::bind_method(D_METHOD("is_descendant_of", "p_task"), &BTTask::is_descendant_of);
ClassDB::bind_method(D_METHOD("get_child_index", "p_child"), &BTTask::get_child_index); ClassDB::bind_method(D_METHOD("get_child_index", "p_child"), &BTTask::get_child_index);

View File

@ -57,8 +57,8 @@ public:
void set_custom_name(const String &p_name); void set_custom_name(const String &p_name);
String get_task_name() const; String get_task_name() const;
virtual void initialize(Object *p_agent, const Ref<Blackboard> &p_blackboard);
virtual Ref<BTTask> clone() const; virtual Ref<BTTask> clone() const;
virtual void initialize(Object *p_agent, const Ref<Blackboard> &p_blackboard);
int execute(float p_delta); int execute(float p_delta);
void cancel(); void cancel();
Ref<BTTask> get_child(int p_idx) const; Ref<BTTask> get_child(int p_idx) const;
@ -66,6 +66,7 @@ public:
void add_child(Ref<BTTask> p_child); void add_child(Ref<BTTask> p_child);
void add_child_at_index(Ref<BTTask> p_child, int p_idx); void add_child_at_index(Ref<BTTask> p_child, int p_idx);
void remove_child(Ref<BTTask> p_child); void remove_child(Ref<BTTask> p_child);
void remove_child_at_index(int p_idx);
bool has_child(const Ref<BTTask> &p_child) const; bool has_child(const Ref<BTTask> &p_child) const;
bool is_descendant_of(const Ref<BTTask> &p_task) const; bool is_descendant_of(const Ref<BTTask> &p_task) const;
int get_child_index(const Ref<BTTask> &p_child) const; int get_child_index(const Ref<BTTask> &p_child) const;

View File

@ -11,6 +11,7 @@
#include "modules/limboai/bt/actions/bt_fail.h" #include "modules/limboai/bt/actions/bt_fail.h"
#include "modules/limboai/bt/bt_task.h" #include "modules/limboai/bt/bt_task.h"
#include "modules/limboai/bt/decorators/bt_decorator.h" #include "modules/limboai/bt/decorators/bt_decorator.h"
#include "modules/limboai/bt/decorators/bt_new_scope.h"
String BTSubtree::_generate_name() const { String BTSubtree::_generate_name() const {
String s; String s;
@ -24,16 +25,18 @@ String BTSubtree::_generate_name() const {
return vformat("Subtree %s", s); return vformat("Subtree %s", s);
} }
Ref<BTTask> BTSubtree::clone() const { void BTSubtree::initialize(Object *p_agent, const Ref<Blackboard> &p_blackboard) {
Ref<BTTask> copy = BTDecorator::clone(); ERR_FAIL_COND_MSG(!subtree.is_valid(), "Subtree is not assigned.");
if (!Engine::get_singleton()->is_editor_hint()) { ERR_FAIL_COND_MSG(!subtree->get_root_task().is_valid(), "Subtree root task is not valid.");
ERR_FAIL_COND_V_MSG(!subtree.is_valid(), copy, "Subtree is not assigned."); ERR_FAIL_COND_MSG(get_child_count() != 0, "Subtree task shouldn't have children during initialization.");
ERR_FAIL_COND_V_MSG(!subtree->get_root_task().is_valid(), copy, "Subtree root task is not valid.");
ERR_FAIL_COND_V_MSG(get_child_count() != 0, copy, "Subtree prototype shouldn't have children.");
copy->add_child(subtree->get_root_task()->clone()); // while (get_child_count() > 0) {
} // remove_child_at_index(get_child_count() - 1);
return copy; // }
add_child(subtree->get_root_task()->clone());
BTNewScope::initialize(p_agent, p_blackboard);
} }
int BTSubtree::_tick(float p_delta) { int BTSubtree::_tick(float p_delta) {

View File

@ -26,7 +26,7 @@ public:
} }
Ref<BehaviorTree> get_subtree() const { return subtree; } Ref<BehaviorTree> get_subtree() const { return subtree; }
virtual Ref<BTTask> clone() const; virtual void initialize(Object *p_agent, const Ref<Blackboard> &p_blackboard);
virtual String get_configuration_warning() const; virtual String get_configuration_warning() const;
}; };