Compare commits

..

4 Commits

Author SHA1 Message Date
Serhii Snitsaruk c30c5a4d7a
BlackboardPlan: Auto-fill type info when adding a missing variable for a mapping 2024-05-14 22:55:25 +02:00
Serhii Snitsaruk ef1c1e5192
Fix circular ref & non-tools compilation errors 2024-05-14 22:03:29 +02:00
Serhii Snitsaruk 3b12288ae0
BlackboardPlan: Serialize only non-empty mapping values 2024-05-14 20:25:18 +02:00
Serhii Snitsaruk a1cdff2e2e
Fix issues with mapping in BTSubtree 2024-05-14 19:47:05 +02:00
13 changed files with 106 additions and 39 deletions

View File

@ -28,8 +28,12 @@ bool BlackboardPlan::_set(const StringName &p_name, const Variant &p_value) {
// * Mapping // * Mapping
if (name_str.begins_with("mapping/")) { if (name_str.begins_with("mapping/")) {
StringName mapped_var_name = name_str.get_slicec('/', 1); StringName mapped_var_name = name_str.get_slicec('/', 1);
ERR_FAIL_COND_V(!has_var(mapped_var_name), false); StringName value = p_value;
parent_scope_mapping[mapped_var_name] = p_value; if (value == StringName()) {
parent_scope_mapping.erase(mapped_var_name);
} else {
parent_scope_mapping[mapped_var_name] = value;
}
return true; return true;
} }
@ -137,16 +141,25 @@ void BlackboardPlan::_get_property_list(List<PropertyInfo> *p_list) const {
if (is_mapping_enabled()) { if (is_mapping_enabled()) {
p_list->push_back(PropertyInfo(Variant::NIL, "Mapping", PROPERTY_HINT_NONE, "mapping/", PROPERTY_USAGE_GROUP)); p_list->push_back(PropertyInfo(Variant::NIL, "Mapping", PROPERTY_HINT_NONE, "mapping/", PROPERTY_USAGE_GROUP));
for (const Pair<StringName, BBVariable> &p : var_list) { for (const Pair<StringName, BBVariable> &p : var_list) {
p_list->push_back(PropertyInfo(Variant::STRING_NAME, "mapping/" + p.first, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT)); // Serialize only non-empty mappings.
PropertyUsageFlags usage = has_mapping(p.first) ? PROPERTY_USAGE_DEFAULT : PROPERTY_USAGE_EDITOR;
p_list->push_back(PropertyInfo(Variant::STRING_NAME, "mapping/" + p.first, PROPERTY_HINT_NONE, "", usage));
} }
} }
} }
bool BlackboardPlan::_property_can_revert(const StringName &p_name) const { bool BlackboardPlan::_property_can_revert(const StringName &p_name) const {
if (String(p_name).begins_with("mapping/")) {
return true;
}
return base.is_valid() && base->var_map.has(p_name); return base.is_valid() && base->var_map.has(p_name);
} }
bool BlackboardPlan::_property_get_revert(const StringName &p_name, Variant &r_property) const { bool BlackboardPlan::_property_get_revert(const StringName &p_name, Variant &r_property) const {
if (String(p_name).begins_with("mapping/")) {
r_property = StringName();
return true;
}
if (base->var_map.has(p_name)) { if (base->var_map.has(p_name)) {
r_property = base->var_map[p_name].get_value(); r_property = base->var_map[p_name].get_value();
return true; return true;
@ -382,7 +395,7 @@ Ref<Blackboard> BlackboardPlan::create_blackboard(Node *p_node, const Ref<Blackb
ERR_FAIL_COND_V(p_node == nullptr && prefetch_nodepath_vars, memnew(Blackboard)); ERR_FAIL_COND_V(p_node == nullptr && prefetch_nodepath_vars, memnew(Blackboard));
Ref<Blackboard> bb = memnew(Blackboard); Ref<Blackboard> bb = memnew(Blackboard);
bb->set_parent(p_parent_scope); bb->set_parent(p_parent_scope);
populate_blackboard(bb, false, p_node); populate_blackboard(bb, true, p_node);
return bb; return bb;
} }

View File

@ -48,14 +48,17 @@ void BehaviorTree::set_blackboard_plan(const Ref<BlackboardPlan> &p_plan) {
blackboard_plan->connect(LW_NAME(changed), callable_mp(this, &BehaviorTree::_plan_changed)); blackboard_plan->connect(LW_NAME(changed), callable_mp(this, &BehaviorTree::_plan_changed));
} }
_set_editor_behavior_tree_hint();
_plan_changed(); _plan_changed();
} }
void BehaviorTree::set_root_task(const Ref<BTTask> &p_value) { void BehaviorTree::set_root_task(const Ref<BTTask> &p_value) {
#ifdef TOOLS_ENABLED
_unset_editor_behavior_tree_hint(); _unset_editor_behavior_tree_hint();
#endif // TOOLS_ENABLED
root_task = p_value; root_task = p_value;
#ifdef TOOLS_ENABLED
_set_editor_behavior_tree_hint(); _set_editor_behavior_tree_hint();
#endif // TOOLS_ENABLED
emit_changed(); emit_changed();
} }
@ -88,18 +91,22 @@ void BehaviorTree::_plan_changed() {
emit_changed(); emit_changed();
} }
#ifdef TOOLS_ENABLED
void BehaviorTree::_set_editor_behavior_tree_hint() { void BehaviorTree::_set_editor_behavior_tree_hint() {
if (root_task.is_valid()) { if (root_task.is_valid()) {
root_task->data.behavior_tree = Ref<BehaviorTree>(this); root_task->data.behavior_tree_id = this->get_instance_id();
} }
} }
void BehaviorTree::_unset_editor_behavior_tree_hint() { void BehaviorTree::_unset_editor_behavior_tree_hint() {
if (root_task.is_valid()) { if (root_task.is_valid()) {
root_task->data.behavior_tree.unref(); root_task->data.behavior_tree_id = ObjectID();
} }
} }
#endif // TOOLS_ENABLED
void BehaviorTree::_bind_methods() { void BehaviorTree::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_description", "description"), &BehaviorTree::set_description); ClassDB::bind_method(D_METHOD("set_description", "description"), &BehaviorTree::set_description);
ClassDB::bind_method(D_METHOD("get_description"), &BehaviorTree::get_description); ClassDB::bind_method(D_METHOD("get_description"), &BehaviorTree::get_description);

View File

@ -33,8 +33,11 @@ private:
Ref<BTTask> root_task; Ref<BTTask> root_task;
void _plan_changed(); void _plan_changed();
#ifdef TOOLS_ENABLED
void _set_editor_behavior_tree_hint(); void _set_editor_behavior_tree_hint();
void _unset_editor_behavior_tree_hint(); void _unset_editor_behavior_tree_hint();
#endif // TOOLS_ENABLED
protected: protected:
static void _bind_methods(); static void _bind_methods();

View File

@ -377,18 +377,22 @@ void BTTask::print_tree(int p_initial_tabs) {
} }
} }
#ifdef TOOLS_ENABLED
Ref<BehaviorTree> BTTask::editor_get_behavior_tree() { Ref<BehaviorTree> BTTask::editor_get_behavior_tree() {
BTTask *task = this; BTTask *task = this;
while (task->data.behavior_tree.is_null() && task->get_parent().is_valid()) { while (task->data.behavior_tree_id.is_null() && task->get_parent().is_valid()) {
task = task->data.parent; task = task->data.parent;
} }
return task->data.behavior_tree; return Object::cast_to<BehaviorTree>(ObjectDB::get_instance(task->data.behavior_tree_id));
} }
void BTTask::editor_set_behavior_tree(const Ref<BehaviorTree> &p_bt) { void BTTask::editor_set_behavior_tree(const Ref<BehaviorTree> &p_bt) {
data.behavior_tree = p_bt; data.behavior_tree_id = p_bt->get_instance_id();
} }
#endif // TOOLS_ENABLED
void BTTask::_bind_methods() { void BTTask::_bind_methods() {
// Public Methods. // Public Methods.
ClassDB::bind_method(D_METHOD("is_root"), &BTTask::is_root); ClassDB::bind_method(D_METHOD("is_root"), &BTTask::is_root);
@ -410,7 +414,9 @@ void BTTask::_bind_methods() {
ClassDB::bind_method(D_METHOD("print_tree", "initial_tabs"), &BTTask::print_tree, Variant(0)); ClassDB::bind_method(D_METHOD("print_tree", "initial_tabs"), &BTTask::print_tree, Variant(0));
ClassDB::bind_method(D_METHOD("get_task_name"), &BTTask::get_task_name); ClassDB::bind_method(D_METHOD("get_task_name"), &BTTask::get_task_name);
ClassDB::bind_method(D_METHOD("abort"), &BTTask::abort); ClassDB::bind_method(D_METHOD("abort"), &BTTask::abort);
#ifdef TOOLS_ENABLED
ClassDB::bind_method(D_METHOD("editor_get_behavior_tree"), &BTTask::editor_get_behavior_tree); ClassDB::bind_method(D_METHOD("editor_get_behavior_tree"), &BTTask::editor_get_behavior_tree);
#endif // TOOLS_ENABLED
// Properties, setters and getters. // Properties, setters and getters.
ClassDB::bind_method(D_METHOD("get_agent"), &BTTask::get_agent); ClassDB::bind_method(D_METHOD("get_agent"), &BTTask::get_agent);

View File

@ -85,7 +85,7 @@ private:
double elapsed = 0.0; double elapsed = 0.0;
bool display_collapsed = false; bool display_collapsed = false;
#ifdef TOOLS_ENABLED #ifdef TOOLS_ENABLED
Ref<BehaviorTree> behavior_tree; ObjectID behavior_tree_id;
#endif #endif
} data; } data;

View File

@ -11,28 +11,45 @@
#include "bt_new_scope.h" #include "bt_new_scope.h"
#include "../../behavior_tree.h"
void BTNewScope::set_blackboard_plan(const Ref<BlackboardPlan> &p_plan) { void BTNewScope::set_blackboard_plan(const Ref<BlackboardPlan> &p_plan) {
blackboard_plan = p_plan; blackboard_plan = p_plan;
if (blackboard_plan.is_null()) { if (blackboard_plan.is_null()) {
blackboard_plan.instantiate(); blackboard_plan.instantiate();
} }
_update_blackboard_plan(); _update_blackboard_plan();
#ifdef TOOLS_ENABLED
if (Engine::get_singleton()->is_editor_hint()) {
callable_mp(this, &BTNewScope::_set_parent_scope_plan_from_bt).call_deferred();
}
#endif // TOOLS_ENABLED
emit_changed(); emit_changed();
} }
#ifdef TOOLS_ENABLED
void BTNewScope::_set_parent_scope_plan_from_bt() {
ERR_FAIL_NULL(get_blackboard_plan());
Ref<BehaviorTree> bt = get_root()->editor_get_behavior_tree();
ERR_FAIL_NULL(bt);
get_blackboard_plan()->set_parent_scope_plan(bt->get_blackboard_plan());
}
#endif // TOOLS_ENABLED
void BTNewScope::initialize(Node *p_agent, const Ref<Blackboard> &p_blackboard, Node *p_scene_root) { void BTNewScope::initialize(Node *p_agent, const Ref<Blackboard> &p_blackboard, Node *p_scene_root) {
ERR_FAIL_COND(p_agent == nullptr); ERR_FAIL_COND(p_agent == nullptr);
ERR_FAIL_COND(p_blackboard == nullptr); ERR_FAIL_COND(p_blackboard == nullptr);
Ref<Blackboard> bb; Ref<Blackboard> bb;
if (blackboard_plan.is_valid()) { if (blackboard_plan.is_valid()) {
bb = blackboard_plan->create_blackboard(p_agent); bb = blackboard_plan->create_blackboard(p_agent, p_blackboard);
} else { } else {
bb = Ref<Blackboard>(memnew(Blackboard)); bb = Ref<Blackboard>(memnew(Blackboard));
}
bb->set_parent(p_blackboard); bb->set_parent(p_blackboard);
}
BTDecorator::initialize(p_agent, bb, p_scene_root); BTDecorator::initialize(p_agent, bb, p_scene_root);
} }

View File

@ -23,6 +23,10 @@ class BTNewScope : public BTDecorator {
private: private:
Ref<BlackboardPlan> blackboard_plan; Ref<BlackboardPlan> blackboard_plan;
#ifdef TOOLS_ENABLED
void _set_parent_scope_plan_from_bt();
#endif // TOOLS_ENABLED
protected: protected:
static void _bind_methods(); static void _bind_methods();

View File

@ -30,10 +30,6 @@ void BTSubtree::_update_blackboard_plan() {
set_blackboard_plan(Ref<BlackboardPlan>(memnew(BlackboardPlan))); set_blackboard_plan(Ref<BlackboardPlan>(memnew(BlackboardPlan)));
} }
get_blackboard_plan()->set_base_plan(subtree.is_valid() ? subtree->get_blackboard_plan() : nullptr); get_blackboard_plan()->set_base_plan(subtree.is_valid() ? subtree->get_blackboard_plan() : nullptr);
#ifdef TOOLS_ENABLED
get_blackboard_plan()->set_parent_scope_plan(get_root()->editor_get_behavior_tree()->get_blackboard_plan());
#endif // TOOLS_ENABLED
} }
String BTSubtree::_generate_name() { String BTSubtree::_generate_name() {

View File

@ -54,8 +54,9 @@ void BlackboardPlanEditor::_add_var() {
var_name = String(default_var_name) + itos(suffix); var_name = String(default_var_name) + itos(suffix);
} }
BBVariable var(Variant::Type::FLOAT); BBVariable var(default_type, default_hint, default_hint_string);
plan->add_var(var_name, var); plan->add_var(var_name, var);
reset_defaults();
_refresh(); _refresh();
} }
@ -127,10 +128,18 @@ void BlackboardPlanEditor::edit_plan(const Ref<BlackboardPlan> &p_plan) {
_refresh(); _refresh();
} }
void BlackboardPlanEditor::set_next_var_name(const StringName &p_name) { void BlackboardPlanEditor::set_defaults(const StringName &p_var_name, Variant::Type p_type, PropertyHint p_hint, String p_hint_string) {
if (String(p_name).is_valid_identifier()) { default_var_name = p_var_name;
default_var_name = p_name; default_type = p_type;
} default_hint = p_hint;
default_hint_string = p_hint_string;
}
void BlackboardPlanEditor::reset_defaults() {
default_var_name = "var";
default_type = Variant::FLOAT;
default_hint = PROPERTY_HINT_NONE;
default_hint_string = "";
} }
void BlackboardPlanEditor::_show_button_popup(Button *p_button, PopupMenu *p_popup, int p_index) { void BlackboardPlanEditor::_show_button_popup(Button *p_button, PopupMenu *p_popup, int p_index) {
@ -233,7 +242,7 @@ void BlackboardPlanEditor::_drag_button_gui_input(const Ref<InputEvent> &p_event
void BlackboardPlanEditor::_visibility_changed() { void BlackboardPlanEditor::_visibility_changed() {
if (!is_visible() && plan.is_valid()) { if (!is_visible() && plan.is_valid()) {
plan->notify_property_list_changed(); plan->notify_property_list_changed();
default_var_name = "var"; reset_defaults();
} }
} }
@ -367,7 +376,7 @@ void BlackboardPlanEditor::_notification(int p_what) {
} }
BlackboardPlanEditor::BlackboardPlanEditor() { BlackboardPlanEditor::BlackboardPlanEditor() {
default_var_name = "var"; reset_defaults();
set_title(TTR("Manage Blackboard Plan")); set_title(TTR("Manage Blackboard Plan"));

View File

@ -59,6 +59,9 @@ private:
Ref<BlackboardPlan> plan; Ref<BlackboardPlan> plan;
StringName default_var_name; StringName default_var_name;
Variant::Type default_type = Variant::NIL;
PropertyHint default_hint = PROPERTY_HINT_NONE;
String default_hint_string;
VBoxContainer *rows_vbox; VBoxContainer *rows_vbox;
Button *add_var_tool; Button *add_var_tool;
@ -99,7 +102,8 @@ public:
_FORCE_INLINE_ static BlackboardPlanEditor *get_singleton() { return singleton; } _FORCE_INLINE_ static BlackboardPlanEditor *get_singleton() { return singleton; }
void edit_plan(const Ref<BlackboardPlan> &p_plan); void edit_plan(const Ref<BlackboardPlan> &p_plan);
void set_next_var_name(const StringName &p_name); void set_defaults(const StringName &p_name, Variant::Type p_type = Variant::FLOAT, PropertyHint p_hint = PROPERTY_HINT_NONE, String p_hint_string = "");
void reset_defaults();
BlackboardPlanEditor(); BlackboardPlanEditor();
}; };

View File

@ -300,7 +300,7 @@ void EditorPropertyBBParam::update_property() {
void EditorPropertyBBParam::setup(PropertyHint p_hint, const String &p_hint_text, const Ref<BlackboardPlan> &p_plan) { void EditorPropertyBBParam::setup(PropertyHint p_hint, const String &p_hint_text, const Ref<BlackboardPlan> &p_plan) {
param_type = p_hint_text; param_type = p_hint_text;
property_hint = p_hint; property_hint = p_hint;
variable_editor->setup(p_plan); variable_editor->setup(p_plan, false);
variable_editor->set_name_split_ratio(0.0); variable_editor->set_name_split_ratio(0.0);
} }

View File

@ -76,7 +76,7 @@ void EditorPropertyVariableName::_update_status() {
BUTTON_SET_ICON(status_btn, theme_cache.var_empty_icon); BUTTON_SET_ICON(status_btn, theme_cache.var_empty_icon);
status_btn->set_tooltip_text(TTR("Variable name not specified.\nClick to open the blackboard plan.")); status_btn->set_tooltip_text(TTR("Variable name not specified.\nClick to open the blackboard plan."));
} else if (plan->has_var(var_name)) { } else if (plan->has_var(var_name)) {
if (type_hint == Variant::NIL || plan->get_var(var_name).get_type() == type_hint) { if (expected_type == Variant::NIL || plan->get_var(var_name).get_type() == expected_type) {
BUTTON_SET_ICON(status_btn, theme_cache.var_exists_icon); BUTTON_SET_ICON(status_btn, theme_cache.var_exists_icon);
status_btn->set_tooltip_text(TTR("This variable is present in the blackboard plan.\nClick to open the blackboard plan.")); status_btn->set_tooltip_text(TTR("This variable is present in the blackboard plan.\nClick to open the blackboard plan."));
} else { } else {
@ -84,7 +84,7 @@ void EditorPropertyVariableName::_update_status() {
status_btn->set_tooltip_text(TTR(vformat( status_btn->set_tooltip_text(TTR(vformat(
"The %s variable in the blackboard plan is not of the same type as this variable (expected %s).\nClick to open the blackboard plan and fix the variable type.", "The %s variable in the blackboard plan is not of the same type as this variable (expected %s).\nClick to open the blackboard plan and fix the variable type.",
LimboUtility::get_singleton()->decorate_var(var_name), LimboUtility::get_singleton()->decorate_var(var_name),
Variant::get_type_name(type_hint)))); Variant::get_type_name(expected_type))));
} }
} else if (name_edit->get_text().begins_with("_")) { } else if (name_edit->get_text().begins_with("_")) {
BUTTON_SET_ICON(status_btn, theme_cache.var_private_icon); BUTTON_SET_ICON(status_btn, theme_cache.var_private_icon);
@ -98,7 +98,7 @@ void EditorPropertyVariableName::_update_status() {
void EditorPropertyVariableName::_status_pressed() { void EditorPropertyVariableName::_status_pressed() {
ERR_FAIL_NULL(plan); ERR_FAIL_NULL(plan);
if (!plan->has_var(name_edit->get_text())) { if (!plan->has_var(name_edit->get_text())) {
BlackboardPlanEditor::get_singleton()->set_next_var_name(name_edit->get_text()); BlackboardPlanEditor::get_singleton()->set_defaults(name_edit->get_text(), expected_type, expected_hint, expected_hint_string);
} }
BlackboardPlanEditor::get_singleton()->edit_plan(plan); BlackboardPlanEditor::get_singleton()->edit_plan(plan);
BlackboardPlanEditor::get_singleton()->popup_centered(); BlackboardPlanEditor::get_singleton()->popup_centered();
@ -131,10 +131,12 @@ void EditorPropertyVariableName::_update_property() {
_update_status(); _update_status();
} }
void EditorPropertyVariableName::setup(const Ref<BlackboardPlan> &p_plan, bool p_allow_empty, Variant::Type p_type_hint) { void EditorPropertyVariableName::setup(const Ref<BlackboardPlan> &p_plan, bool p_allow_empty, Variant::Type p_type, PropertyHint p_hint, String p_hint_string) {
plan = p_plan; plan = p_plan;
allow_empty = p_allow_empty; allow_empty = p_allow_empty;
type_hint = p_type_hint; expected_type = p_type;
expected_hint = p_hint;
expected_hint_string = p_hint_string;
_update_status(); _update_status();
} }
@ -226,14 +228,18 @@ bool EditorInspectorPluginVariableName::_parse_property(Object *p_object, const
} }
Ref<BlackboardPlan> plan; Ref<BlackboardPlan> plan;
Variant::Type type_hint = Variant::NIL; Variant::Type expected_type = Variant::NIL;
PropertyHint expected_hint = PROPERTY_HINT_NONE;
String expected_hint_string;
if (is_mapping) { if (is_mapping) {
plan.reference_ptr(Object::cast_to<BlackboardPlan>(p_object)); plan.reference_ptr(Object::cast_to<BlackboardPlan>(p_object));
ERR_FAIL_NULL_V(plan, false); ERR_FAIL_NULL_V(plan, false);
String var_name = p_path.trim_prefix("mapping/"); String var_name = p_path.trim_prefix("mapping/");
if (plan->has_var(var_name)) { if (plan->has_var(var_name)) {
BBVariable variable = plan->get_var(var_name); BBVariable variable = plan->get_var(var_name);
type_hint = variable.get_type(); expected_type = variable.get_type();
expected_hint = variable.get_hint();
expected_hint_string = variable.get_hint_string();
} }
plan = plan->get_parent_scope_plan(); plan = plan->get_parent_scope_plan();
ERR_FAIL_NULL_V(plan, false); ERR_FAIL_NULL_V(plan, false);
@ -242,8 +248,8 @@ bool EditorInspectorPluginVariableName::_parse_property(Object *p_object, const
} }
EditorPropertyVariableName *ed = memnew(EditorPropertyVariableName); EditorPropertyVariableName *ed = memnew(EditorPropertyVariableName);
ed->setup(plan, is_mapping, type_hint); ed->setup(plan, is_mapping, expected_type, expected_hint, expected_hint_string);
add_property_editor(p_path, ed, type_hint); add_property_editor(p_path, ed, expected_type);
return true; return true;
} }

View File

@ -45,7 +45,9 @@ private:
Ref<BlackboardPlan> plan; Ref<BlackboardPlan> plan;
bool allow_empty = false; bool allow_empty = false;
Variant::Type type_hint = Variant::NIL; Variant::Type expected_type = Variant::NIL;
PropertyHint expected_hint = PROPERTY_HINT_NONE;
String expected_hint_string;
LineEdit *name_edit; LineEdit *name_edit;
Button *drop_btn; Button *drop_btn;
@ -73,7 +75,7 @@ public:
virtual void _update_property() override; virtual void _update_property() override;
#endif #endif
void setup(const Ref<BlackboardPlan> &p_plan, bool p_allow_empty = false, Variant::Type p_type_hint = Variant::NIL); void setup(const Ref<BlackboardPlan> &p_plan, bool p_allow_empty, Variant::Type p_type = Variant::FLOAT, PropertyHint p_hint = PROPERTY_HINT_NONE, String p_hint_string = "");
EditorPropertyVariableName(); EditorPropertyVariableName();
}; };