Merge pull request #24 from limbonaut/expose-behavior-tree-view
Expose `BehaviorTreeView` and `BehaviorTreeData` to use with custom in-game tools
This commit is contained in:
commit
74a3635e1c
|
@ -241,6 +241,8 @@ void BTPlayer::_bind_methods() {
|
||||||
ClassDB::bind_method(D_METHOD("restart"), &BTPlayer::restart);
|
ClassDB::bind_method(D_METHOD("restart"), &BTPlayer::restart);
|
||||||
ClassDB::bind_method(D_METHOD("get_last_status"), &BTPlayer::get_last_status);
|
ClassDB::bind_method(D_METHOD("get_last_status"), &BTPlayer::get_last_status);
|
||||||
|
|
||||||
|
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::OBJECT, "behavior_tree", PROPERTY_HINT_RESOURCE_TYPE, "BehaviorTree"), "set_behavior_tree", "get_behavior_tree");
|
||||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "update_mode", PROPERTY_HINT_ENUM, "Idle,Physics,Manual"), "set_update_mode", "get_update_mode");
|
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::BOOL, "active"), "set_active", "get_active");
|
||||||
|
|
|
@ -77,6 +77,8 @@ public:
|
||||||
void restart();
|
void restart();
|
||||||
int get_last_status() const { return last_status; }
|
int get_last_status() const { return last_status; }
|
||||||
|
|
||||||
|
Ref<BTTask> get_tree_instance() { return tree_instance; }
|
||||||
|
|
||||||
BTPlayer();
|
BTPlayer();
|
||||||
~BTPlayer();
|
~BTPlayer();
|
||||||
|
|
||||||
|
|
|
@ -101,6 +101,8 @@ void BTState::_bind_methods() {
|
||||||
ClassDB::bind_method(D_METHOD("set_behavior_tree", "p_value"), &BTState::set_behavior_tree);
|
ClassDB::bind_method(D_METHOD("set_behavior_tree", "p_value"), &BTState::set_behavior_tree);
|
||||||
ClassDB::bind_method(D_METHOD("get_behavior_tree"), &BTState::get_behavior_tree);
|
ClassDB::bind_method(D_METHOD("get_behavior_tree"), &BTState::get_behavior_tree);
|
||||||
|
|
||||||
|
ClassDB::bind_method(D_METHOD("get_tree_instance"), &BTState::get_tree_instance);
|
||||||
|
|
||||||
ClassDB::bind_method(D_METHOD("set_success_event", "p_event_name"), &BTState::set_success_event);
|
ClassDB::bind_method(D_METHOD("set_success_event", "p_event_name"), &BTState::set_success_event);
|
||||||
ClassDB::bind_method(D_METHOD("get_success_event"), &BTState::get_success_event);
|
ClassDB::bind_method(D_METHOD("get_success_event"), &BTState::get_success_event);
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,8 @@ public:
|
||||||
void set_behavior_tree(const Ref<BehaviorTree> &p_value);
|
void set_behavior_tree(const Ref<BehaviorTree> &p_value);
|
||||||
Ref<BehaviorTree> get_behavior_tree() const { return behavior_tree; }
|
Ref<BehaviorTree> get_behavior_tree() const { return behavior_tree; }
|
||||||
|
|
||||||
|
Ref<BTTask> get_tree_instance() const { return tree_instance; }
|
||||||
|
|
||||||
void set_success_event(String p_success_event) { success_event = p_success_event; }
|
void set_success_event(String p_success_event) { success_event = p_success_event; }
|
||||||
String get_success_event() const { return success_event; }
|
String get_success_event() const { return success_event; }
|
||||||
|
|
||||||
|
|
|
@ -58,6 +58,7 @@ def get_doc_classes():
|
||||||
"BBVector4",
|
"BBVector4",
|
||||||
"BBVector4i",
|
"BBVector4i",
|
||||||
"BehaviorTree",
|
"BehaviorTree",
|
||||||
|
"BehaviorTreeData",
|
||||||
"BehaviorTreeView",
|
"BehaviorTreeView",
|
||||||
"Blackboard",
|
"Blackboard",
|
||||||
"BlackboardPlan",
|
"BlackboardPlan",
|
||||||
|
|
|
@ -16,6 +16,12 @@
|
||||||
Returns the behavior tree's last execution status. See [enum BT.Status].
|
Returns the behavior tree's last execution status. See [enum BT.Status].
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
|
<method name="get_tree_instance">
|
||||||
|
<return type="BTTask" />
|
||||||
|
<description>
|
||||||
|
Returns the root task of the instantiated behavior tree.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
<method name="restart">
|
<method name="restart">
|
||||||
<return type="void" />
|
<return type="void" />
|
||||||
<description>
|
<description>
|
||||||
|
|
|
@ -8,6 +8,14 @@
|
||||||
</description>
|
</description>
|
||||||
<tutorials>
|
<tutorials>
|
||||||
</tutorials>
|
</tutorials>
|
||||||
|
<methods>
|
||||||
|
<method name="get_tree_instance" qualifiers="const">
|
||||||
|
<return type="BTTask" />
|
||||||
|
<description>
|
||||||
|
Returns the root task of the instantiated behavior tree.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
</methods>
|
||||||
<members>
|
<members>
|
||||||
<member name="behavior_tree" type="BehaviorTree" setter="set_behavior_tree" getter="get_behavior_tree">
|
<member name="behavior_tree" type="BehaviorTree" setter="set_behavior_tree" getter="get_behavior_tree">
|
||||||
A [BehaviorTree] resource that defines state behavior.
|
A [BehaviorTree] resource that defines state behavior.
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<class name="BehaviorTreeData" inherits="RefCounted" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
|
||||||
|
<brief_description>
|
||||||
|
Represents current state of a [BehaviorTree] instance.
|
||||||
|
</brief_description>
|
||||||
|
<description>
|
||||||
|
This class is used by the LimboAI debugger for the serialization and deserialization of [BehaviorTree] instance data.
|
||||||
|
Additionally, it can be used with [BehaviorTreeView] to visualize the current state of a [BehaviorTree] instance. It is meant to be utilized in custom in-game tools.
|
||||||
|
</description>
|
||||||
|
<tutorials>
|
||||||
|
</tutorials>
|
||||||
|
<methods>
|
||||||
|
<method name="create_from_tree_instance" qualifiers="static">
|
||||||
|
<return type="BehaviorTreeData" />
|
||||||
|
<param index="0" name="p_tree_instance" type="BTTask" />
|
||||||
|
<description>
|
||||||
|
Returns current state of the [param p_tree_instance] encoded as a [BehaviorTreeData], suitable for use with [BehaviorTreeView].
|
||||||
|
Behavior tree instance can be acquired with [method BTPlayer.get_tree_instance].
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
</methods>
|
||||||
|
</class>
|
|
@ -0,0 +1,20 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<class name="BehaviorTreeView" inherits="Control" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
|
||||||
|
<brief_description>
|
||||||
|
Visualizes the current state of a [BehaviorTree] instance.
|
||||||
|
</brief_description>
|
||||||
|
<description>
|
||||||
|
Visualizes the current state of a [BehaviorTree] instance. See also [BehaviorTreeData].
|
||||||
|
</description>
|
||||||
|
<tutorials>
|
||||||
|
</tutorials>
|
||||||
|
<methods>
|
||||||
|
<method name="update_tree">
|
||||||
|
<return type="void" />
|
||||||
|
<param index="0" name="p_behavior_tree_data" type="BehaviorTreeData" />
|
||||||
|
<description>
|
||||||
|
Updates the representation of a [BehaviorTree] instance. See also [BehaviorTreeData].
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
</methods>
|
||||||
|
</class>
|
|
@ -17,54 +17,14 @@
|
||||||
|
|
||||||
//**** BehaviorTreeData
|
//**** BehaviorTreeData
|
||||||
|
|
||||||
void BehaviorTreeData::serialize(Array &p_arr) {
|
Array BehaviorTreeData::serialize(const Ref<BTTask> &p_tree_instance, const NodePath &p_player_path, const String &p_bt_resource_path) {
|
||||||
p_arr.push_back(bt_player_path);
|
Array arr;
|
||||||
p_arr.push_back(bt_resource_path);
|
arr.push_back(p_player_path);
|
||||||
for (const TaskData &td : tasks) {
|
arr.push_back(p_bt_resource_path);
|
||||||
p_arr.push_back(td.id);
|
|
||||||
p_arr.push_back(td.name);
|
|
||||||
p_arr.push_back(td.is_custom_name);
|
|
||||||
p_arr.push_back(td.num_children);
|
|
||||||
p_arr.push_back(td.status);
|
|
||||||
p_arr.push_back(td.elapsed_time);
|
|
||||||
p_arr.push_back(td.type_name);
|
|
||||||
p_arr.push_back(td.script_path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void BehaviorTreeData::deserialize(const Array &p_arr) {
|
|
||||||
ERR_FAIL_COND(tasks.size() != 0);
|
|
||||||
ERR_FAIL_COND(p_arr.size() < 2);
|
|
||||||
|
|
||||||
ERR_FAIL_COND(p_arr[0].get_type() != Variant::NODE_PATH);
|
|
||||||
bt_player_path = p_arr[0];
|
|
||||||
|
|
||||||
ERR_FAIL_COND(p_arr[1].get_type() != Variant::STRING);
|
|
||||||
bt_resource_path = p_arr[1];
|
|
||||||
|
|
||||||
int idx = 2;
|
|
||||||
while (p_arr.size() > idx + 1) {
|
|
||||||
ERR_FAIL_COND(p_arr.size() < idx + 7);
|
|
||||||
ERR_FAIL_COND(p_arr[idx].get_type() != Variant::INT);
|
|
||||||
ERR_FAIL_COND(p_arr[idx + 1].get_type() != Variant::STRING);
|
|
||||||
ERR_FAIL_COND(p_arr[idx + 2].get_type() != Variant::BOOL);
|
|
||||||
ERR_FAIL_COND(p_arr[idx + 3].get_type() != Variant::INT);
|
|
||||||
ERR_FAIL_COND(p_arr[idx + 4].get_type() != Variant::INT);
|
|
||||||
ERR_FAIL_COND(p_arr[idx + 5].get_type() != Variant::FLOAT);
|
|
||||||
ERR_FAIL_COND(p_arr[idx + 6].get_type() != Variant::STRING);
|
|
||||||
ERR_FAIL_COND(p_arr[idx + 7].get_type() != Variant::STRING);
|
|
||||||
tasks.push_back(TaskData(p_arr[idx], p_arr[idx + 1], p_arr[idx + 2], p_arr[idx + 3], p_arr[idx + 4], p_arr[idx + 5], p_arr[idx + 6], p_arr[idx + 7]));
|
|
||||||
idx += 8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BehaviorTreeData::BehaviorTreeData(const Ref<BTTask> &p_instance, const NodePath &p_player_path, const String &p_bt_resource) {
|
|
||||||
bt_player_path = p_player_path;
|
|
||||||
bt_resource_path = p_bt_resource;
|
|
||||||
|
|
||||||
// Flatten tree into list depth first
|
// Flatten tree into list depth first
|
||||||
List<Ref<BTTask>> stack;
|
List<Ref<BTTask>> stack;
|
||||||
stack.push_back(p_instance);
|
stack.push_back(p_tree_instance);
|
||||||
int id = 0;
|
int id = 0;
|
||||||
while (stack.size()) {
|
while (stack.size()) {
|
||||||
Ref<BTTask> task = stack[0];
|
Ref<BTTask> task = stack[0];
|
||||||
|
@ -77,11 +37,75 @@ BehaviorTreeData::BehaviorTreeData(const Ref<BTTask> &p_instance, const NodePath
|
||||||
|
|
||||||
String script_path;
|
String script_path;
|
||||||
if (task->get_script()) {
|
if (task->get_script()) {
|
||||||
Ref<Resource> script = task->get_script();
|
Ref<Resource> s = task->get_script();
|
||||||
script_path = script->get_path();
|
script_path = s->get_path();
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.push_back(TaskData(
|
arr.push_back(id);
|
||||||
|
arr.push_back(task->get_task_name());
|
||||||
|
arr.push_back(!task->get_custom_name().is_empty());
|
||||||
|
arr.push_back(num_children);
|
||||||
|
arr.push_back(task->get_status());
|
||||||
|
arr.push_back(task->get_elapsed_time());
|
||||||
|
arr.push_back(task->get_class());
|
||||||
|
arr.push_back(script_path);
|
||||||
|
|
||||||
|
id += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ref<BehaviorTreeData> BehaviorTreeData::deserialize(const Array &p_array) {
|
||||||
|
ERR_FAIL_COND_V(p_array.size() < 2, nullptr);
|
||||||
|
ERR_FAIL_COND_V(p_array[0].get_type() != Variant::NODE_PATH, nullptr);
|
||||||
|
ERR_FAIL_COND_V(p_array[1].get_type() != Variant::STRING, nullptr);
|
||||||
|
|
||||||
|
Ref<BehaviorTreeData> data = memnew(BehaviorTreeData);
|
||||||
|
data->bt_player_path = p_array[0];
|
||||||
|
data->bt_resource_path = p_array[1];
|
||||||
|
|
||||||
|
int idx = 2;
|
||||||
|
while (p_array.size() > idx + 1) {
|
||||||
|
ERR_FAIL_COND_V(p_array.size() < idx + 7, nullptr);
|
||||||
|
ERR_FAIL_COND_V(p_array[idx].get_type() != Variant::INT, nullptr);
|
||||||
|
ERR_FAIL_COND_V(p_array[idx + 1].get_type() != Variant::STRING, nullptr);
|
||||||
|
ERR_FAIL_COND_V(p_array[idx + 2].get_type() != Variant::BOOL, nullptr);
|
||||||
|
ERR_FAIL_COND_V(p_array[idx + 3].get_type() != Variant::INT, nullptr);
|
||||||
|
ERR_FAIL_COND_V(p_array[idx + 4].get_type() != Variant::INT, nullptr);
|
||||||
|
ERR_FAIL_COND_V(p_array[idx + 5].get_type() != Variant::FLOAT, nullptr);
|
||||||
|
ERR_FAIL_COND_V(p_array[idx + 6].get_type() != Variant::STRING, nullptr);
|
||||||
|
ERR_FAIL_COND_V(p_array[idx + 7].get_type() != Variant::STRING, nullptr);
|
||||||
|
data->tasks.push_back(TaskData(p_array[idx], p_array[idx + 1], p_array[idx + 2], p_array[idx + 3], p_array[idx + 4], p_array[idx + 5], p_array[idx + 6], p_array[idx + 7]));
|
||||||
|
idx += 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ref<BehaviorTreeData> BehaviorTreeData::create_from_tree_instance(const Ref<BTTask> &p_tree_instance) {
|
||||||
|
Ref<BehaviorTreeData> data = memnew(BehaviorTreeData);
|
||||||
|
|
||||||
|
// Flatten tree into list depth first
|
||||||
|
List<Ref<BTTask>> stack;
|
||||||
|
stack.push_back(p_tree_instance);
|
||||||
|
int id = 0;
|
||||||
|
while (stack.size()) {
|
||||||
|
Ref<BTTask> task = stack[0];
|
||||||
|
stack.pop_front();
|
||||||
|
|
||||||
|
int num_children = task->get_child_count();
|
||||||
|
for (int i = 0; i < num_children; i++) {
|
||||||
|
stack.push_front(task->get_child(num_children - 1 - i));
|
||||||
|
}
|
||||||
|
|
||||||
|
String script_path;
|
||||||
|
if (task->get_script()) {
|
||||||
|
Ref<Resource> s = task->get_script();
|
||||||
|
script_path = s->get_path();
|
||||||
|
}
|
||||||
|
|
||||||
|
data->tasks.push_back(TaskData(
|
||||||
id,
|
id,
|
||||||
task->get_task_name(),
|
task->get_task_name(),
|
||||||
!task->get_custom_name().is_empty(),
|
!task->get_custom_name().is_empty(),
|
||||||
|
@ -92,4 +116,14 @@ BehaviorTreeData::BehaviorTreeData(const Ref<BTTask> &p_instance, const NodePath
|
||||||
script_path));
|
script_path));
|
||||||
id += 1;
|
id += 1;
|
||||||
}
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BehaviorTreeData::_bind_methods() {
|
||||||
|
// ClassDB::bind_static_method("BehaviorTreeData", D_METHOD("serialize", "p_tree_instance", "p_player_path", "p_bt_resource_path"), &BehaviorTreeData::serialize);
|
||||||
|
// ClassDB::bind_static_method("BehaviorTreeData", D_METHOD("deserialize", "p_array"), &BehaviorTreeData::deserialize);
|
||||||
|
ClassDB::bind_static_method("BehaviorTreeData", D_METHOD("create_from_tree_instance", "p_tree_instance"), &BehaviorTreeData::create_from_tree_instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
BehaviorTreeData::BehaviorTreeData() {
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,12 @@
|
||||||
|
|
||||||
#include "../../bt/tasks/bt_task.h"
|
#include "../../bt/tasks/bt_task.h"
|
||||||
|
|
||||||
class BehaviorTreeData {
|
class BehaviorTreeData : public RefCounted {
|
||||||
|
GDCLASS(BehaviorTreeData, RefCounted);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
static void _bind_methods();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
struct TaskData {
|
struct TaskData {
|
||||||
int id = 0;
|
int id = 0;
|
||||||
|
@ -44,11 +49,12 @@ public:
|
||||||
NodePath bt_player_path;
|
NodePath bt_player_path;
|
||||||
String bt_resource_path;
|
String bt_resource_path;
|
||||||
|
|
||||||
void serialize(Array &p_arr);
|
public:
|
||||||
void deserialize(const Array &p_arr);
|
static Array serialize(const Ref<BTTask> &p_tree_instance, const NodePath &p_player_path, const String &p_bt_resource_path);
|
||||||
|
static Ref<BehaviorTreeData> deserialize(const Array &p_array);
|
||||||
|
static Ref<BehaviorTreeData> create_from_tree_instance(const Ref<BTTask> &p_tree_instance);
|
||||||
|
|
||||||
BehaviorTreeData(const Ref<BTTask> &p_instance, const NodePath &p_player_path, const String &p_bt_resource);
|
BehaviorTreeData();
|
||||||
BehaviorTreeData() {}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // BEHAVIOR_TREE_DATA_H
|
#endif // BEHAVIOR_TREE_DATA_H
|
||||||
|
|
|
@ -61,7 +61,15 @@ void BehaviorTreeView::_item_collapsed(Object *p_obj) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void BehaviorTreeView::update_tree(const BehaviorTreeData &p_data) {
|
double BehaviorTreeView::_get_editor_scale() const {
|
||||||
|
if (Engine::get_singleton()->is_editor_hint()) {
|
||||||
|
return EDSCALE;
|
||||||
|
} else {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BehaviorTreeView::update_tree(const Ref<BehaviorTreeData> &p_data) {
|
||||||
// Remember selected.
|
// Remember selected.
|
||||||
int selected_id = -1;
|
int selected_id = -1;
|
||||||
if (tree->get_selected()) {
|
if (tree->get_selected()) {
|
||||||
|
@ -71,7 +79,7 @@ void BehaviorTreeView::update_tree(const BehaviorTreeData &p_data) {
|
||||||
tree->clear();
|
tree->clear();
|
||||||
TreeItem *parent = nullptr;
|
TreeItem *parent = nullptr;
|
||||||
List<Pair<TreeItem *, int>> parents;
|
List<Pair<TreeItem *, int>> parents;
|
||||||
for (const BehaviorTreeData::TaskData &task_data : p_data.tasks) {
|
for (const BehaviorTreeData::TaskData &task_data : p_data->tasks) {
|
||||||
// Figure out parent.
|
// Figure out parent.
|
||||||
parent = nullptr;
|
parent = nullptr;
|
||||||
if (parents.size()) {
|
if (parents.size()) {
|
||||||
|
@ -99,7 +107,7 @@ void BehaviorTreeView::update_tree(const BehaviorTreeData &p_data) {
|
||||||
|
|
||||||
String cors = (task_data.script_path.is_empty()) ? task_data.type_name : task_data.script_path;
|
String cors = (task_data.script_path.is_empty()) ? task_data.type_name : task_data.script_path;
|
||||||
item->set_icon(0, LimboUtility::get_singleton()->get_task_icon(cors));
|
item->set_icon(0, LimboUtility::get_singleton()->get_task_icon(cors));
|
||||||
item->set_icon_max_width(0, 16 * EDSCALE); // Force user icon size.
|
item->set_icon_max_width(0, 16 * _get_editor_scale()); // Force user icon size.
|
||||||
|
|
||||||
if (task_data.status == BTTask::SUCCESS) {
|
if (task_data.status == BTTask::SUCCESS) {
|
||||||
item->set_custom_draw(0, this, LW_NAME(_draw_success_status));
|
item->set_custom_draw(0, this, LW_NAME(_draw_success_status));
|
||||||
|
@ -164,10 +172,13 @@ void BehaviorTreeView::_do_update_theme_item_cache() {
|
||||||
theme_cache.sbf_failure->set_border_width(SIDE_LEFT, 4.0);
|
theme_cache.sbf_failure->set_border_width(SIDE_LEFT, 4.0);
|
||||||
theme_cache.sbf_failure->set_border_width(SIDE_RIGHT, 4.0);
|
theme_cache.sbf_failure->set_border_width(SIDE_RIGHT, 4.0);
|
||||||
|
|
||||||
double extra_spacing = EDITOR_GET("interface/theme/additional_spacing");
|
double extra_spacing = 0.0;
|
||||||
extra_spacing *= 2.0;
|
if (Engine::get_singleton()->is_editor_hint()) {
|
||||||
tree->set_column_custom_minimum_width(1, 18.0 * EDSCALE);
|
extra_spacing = EDITOR_GET("interface/theme/additional_spacing");
|
||||||
tree->set_column_custom_minimum_width(2, (50.0 + extra_spacing) * EDSCALE);
|
extra_spacing *= 2.0;
|
||||||
|
}
|
||||||
|
tree->set_column_custom_minimum_width(1, 18 * _get_editor_scale());
|
||||||
|
tree->set_column_custom_minimum_width(2, (50 + extra_spacing) * _get_editor_scale());
|
||||||
}
|
}
|
||||||
|
|
||||||
void BehaviorTreeView::_notification(int p_what) {
|
void BehaviorTreeView::_notification(int p_what) {
|
||||||
|
@ -175,7 +186,6 @@ void BehaviorTreeView::_notification(int p_what) {
|
||||||
case NOTIFICATION_READY: {
|
case NOTIFICATION_READY: {
|
||||||
tree->connect(LW_NAME(item_collapsed), callable_mp(this, &BehaviorTreeView::_item_collapsed));
|
tree->connect(LW_NAME(item_collapsed), callable_mp(this, &BehaviorTreeView::_item_collapsed));
|
||||||
} break;
|
} break;
|
||||||
case NOTIFICATION_POSTINITIALIZE:
|
|
||||||
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
|
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
|
||||||
case NOTIFICATION_TRANSLATION_CHANGED:
|
case NOTIFICATION_TRANSLATION_CHANGED:
|
||||||
case NOTIFICATION_THEME_CHANGED: {
|
case NOTIFICATION_THEME_CHANGED: {
|
||||||
|
@ -189,6 +199,7 @@ void BehaviorTreeView::_bind_methods() {
|
||||||
ClassDB::bind_method(D_METHOD("_draw_success_status"), &BehaviorTreeView::_draw_success_status);
|
ClassDB::bind_method(D_METHOD("_draw_success_status"), &BehaviorTreeView::_draw_success_status);
|
||||||
ClassDB::bind_method(D_METHOD("_draw_failure_status"), &BehaviorTreeView::_draw_failure_status);
|
ClassDB::bind_method(D_METHOD("_draw_failure_status"), &BehaviorTreeView::_draw_failure_status);
|
||||||
ClassDB::bind_method(D_METHOD("_item_collapsed"), &BehaviorTreeView::_item_collapsed);
|
ClassDB::bind_method(D_METHOD("_item_collapsed"), &BehaviorTreeView::_item_collapsed);
|
||||||
|
ClassDB::bind_method(D_METHOD("update_tree", "p_behavior_tree_data"), &BehaviorTreeView::update_tree);
|
||||||
}
|
}
|
||||||
|
|
||||||
BehaviorTreeView::BehaviorTreeView() {
|
BehaviorTreeView::BehaviorTreeView() {
|
||||||
|
|
|
@ -54,6 +54,7 @@ private:
|
||||||
void _draw_running_status(Object *p_obj, Rect2 p_rect);
|
void _draw_running_status(Object *p_obj, Rect2 p_rect);
|
||||||
void _draw_failure_status(Object *p_obj, Rect2 p_rect);
|
void _draw_failure_status(Object *p_obj, Rect2 p_rect);
|
||||||
void _item_collapsed(Object *p_obj);
|
void _item_collapsed(Object *p_obj);
|
||||||
|
double _get_editor_scale() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void _do_update_theme_item_cache();
|
void _do_update_theme_item_cache();
|
||||||
|
@ -63,7 +64,7 @@ protected:
|
||||||
static void _bind_methods();
|
static void _bind_methods();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void update_tree(const BehaviorTreeData &p_data);
|
void update_tree(const Ref<BehaviorTreeData> &p_data);
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
BehaviorTreeView();
|
BehaviorTreeView();
|
||||||
|
|
|
@ -184,8 +184,7 @@ void LimboDebugger::_on_bt_updated(int _status, NodePath p_path) {
|
||||||
if (p_path != tracked_player) {
|
if (p_path != tracked_player) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Array arr;
|
Array arr = BehaviorTreeData::serialize(active_trees.get(tracked_player), tracked_player, bt_resource_path);
|
||||||
BehaviorTreeData(active_trees.get(tracked_player), tracked_player, bt_resource_path).serialize(arr);
|
|
||||||
EngineDebugger::get_singleton()->send_message("limboai:bt_update", arr);
|
EngineDebugger::get_singleton()->send_message("limboai:bt_update", arr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,8 +192,7 @@ void LimboDebugger::_on_state_updated(float _delta, NodePath p_path) {
|
||||||
if (p_path != tracked_player) {
|
if (p_path != tracked_player) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Array arr;
|
Array arr = BehaviorTreeData::serialize(active_trees.get(tracked_player), tracked_player, bt_resource_path);
|
||||||
BehaviorTreeData(active_trees.get(tracked_player), tracked_player, bt_resource_path).serialize(arr);
|
|
||||||
EngineDebugger::get_singleton()->send_message("limboai:bt_update", arr);
|
EngineDebugger::get_singleton()->send_message("limboai:bt_update", arr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -90,8 +90,8 @@ String LimboDebuggerTab::get_selected_bt_player() {
|
||||||
return bt_player_list->get_item_text(bt_player_list->get_selected_items()[0]);
|
return bt_player_list->get_item_text(bt_player_list->get_selected_items()[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LimboDebuggerTab::update_behavior_tree(const BehaviorTreeData &p_data) {
|
void LimboDebuggerTab::update_behavior_tree(const Ref<BehaviorTreeData> &p_data) {
|
||||||
resource_header->set_text(p_data.bt_resource_path);
|
resource_header->set_text(p_data->bt_resource_path);
|
||||||
resource_header->set_disabled(false);
|
resource_header->set_disabled(false);
|
||||||
bt_view->update_tree(p_data);
|
bt_view->update_tree(p_data);
|
||||||
info_message->hide();
|
info_message->hide();
|
||||||
|
@ -315,9 +315,8 @@ bool LimboDebuggerPlugin::_capture(const String &p_message, const Array &p_data,
|
||||||
if (p_message == "limboai:active_bt_players") {
|
if (p_message == "limboai:active_bt_players") {
|
||||||
tab->update_active_bt_players(p_data);
|
tab->update_active_bt_players(p_data);
|
||||||
} else if (p_message == "limboai:bt_update") {
|
} else if (p_message == "limboai:bt_update") {
|
||||||
BehaviorTreeData data = BehaviorTreeData();
|
Ref<BehaviorTreeData> data = BehaviorTreeData::deserialize(p_data);
|
||||||
data.deserialize(p_data);
|
if (data->bt_player_path == NodePath(tab->get_selected_bt_player())) {
|
||||||
if (data.bt_player_path == NodePath(tab->get_selected_bt_player())) {
|
|
||||||
tab->update_behavior_tree(data);
|
tab->update_behavior_tree(data);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -84,7 +84,7 @@ public:
|
||||||
void update_active_bt_players(const Array &p_node_paths);
|
void update_active_bt_players(const Array &p_node_paths);
|
||||||
BehaviorTreeView *get_behavior_tree_view() const { return bt_view; }
|
BehaviorTreeView *get_behavior_tree_view() const { return bt_view; }
|
||||||
String get_selected_bt_player();
|
String get_selected_bt_player();
|
||||||
void update_behavior_tree(const BehaviorTreeData &p_data);
|
void update_behavior_tree(const Ref<BehaviorTreeData> &p_data);
|
||||||
|
|
||||||
void setup(Ref<EditorDebuggerSession> p_session, CompatWindowWrapper *p_wrapper);
|
void setup(Ref<EditorDebuggerSession> p_session, CompatWindowWrapper *p_wrapper);
|
||||||
LimboDebuggerTab();
|
LimboDebuggerTab();
|
||||||
|
|
|
@ -68,6 +68,7 @@ BTTimeLimit = "res://addons/limboai/icons/BTTimeLimit.svg"
|
||||||
BTWait = "res://addons/limboai/icons/BTWait.svg"
|
BTWait = "res://addons/limboai/icons/BTWait.svg"
|
||||||
BTWaitTicks = "res://addons/limboai/icons/BTWaitTicks.svg"
|
BTWaitTicks = "res://addons/limboai/icons/BTWaitTicks.svg"
|
||||||
BehaviorTree = "res://addons/limboai/icons/BehaviorTree.svg"
|
BehaviorTree = "res://addons/limboai/icons/BehaviorTree.svg"
|
||||||
|
BehaviorTreeView = "res://addons/limboai/icons/BehaviorTreeView.svg"
|
||||||
BlackboardPlan = "res://addons/limboai/icons/BlackboardPlan.svg"
|
BlackboardPlan = "res://addons/limboai/icons/BlackboardPlan.svg"
|
||||||
LimboAI = "res://addons/limboai/icons/LimboAI.svg"
|
LimboAI = "res://addons/limboai/icons/LimboAI.svg"
|
||||||
LimboDeselectAll = "res://addons/limboai/icons/LimboDeselectAll.svg"
|
LimboDeselectAll = "res://addons/limboai/icons/LimboDeselectAll.svg"
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="m0 0h16v16h-16z" fill="none"/><g fill="#8eef97"><path d="m3 1c-1.097 0-2 .903-2 2v10c0 1.097.903 2 2 2h10c1.097 0 2-.903 2-2v-10c0-1.097-.903-2-2-2zm0 2h10v10h-10z" fill-rule="nonzero"/><path d="m4.996 2.199h.973v8.794h-.973z"/><path d="m4.996 10.021h5.632v.973h-5.632z"/><path d="m4.996 6.054h5.632v.973h-5.632z"/><path d="m9.014 9.007h3.002v3h-3.002z"/><path d="m9.014 5h3.002v3h-3.002z"/></g></svg>
|
After Width: | Height: | Size: 556 B |
|
@ -128,6 +128,10 @@ static LimboUtility *_limbo_utility = nullptr;
|
||||||
|
|
||||||
void initialize_limboai_module(ModuleInitializationLevel p_level) {
|
void initialize_limboai_module(ModuleInitializationLevel p_level) {
|
||||||
if (p_level == MODULE_INITIALIZATION_LEVEL_SCENE) {
|
if (p_level == MODULE_INITIALIZATION_LEVEL_SCENE) {
|
||||||
|
#ifdef TOOLS_ENABLED
|
||||||
|
GDREGISTER_CLASS(BehaviorTreeView);
|
||||||
|
#endif // TOOLS_ENABLED
|
||||||
|
GDREGISTER_CLASS(BehaviorTreeData);
|
||||||
#ifdef LIMBOAI_GDEXTENSION
|
#ifdef LIMBOAI_GDEXTENSION
|
||||||
GDREGISTER_CLASS(LimboDebugger);
|
GDREGISTER_CLASS(LimboDebugger);
|
||||||
#endif
|
#endif
|
||||||
|
@ -251,7 +255,6 @@ void initialize_limboai_module(ModuleInitializationLevel p_level) {
|
||||||
GDREGISTER_CLASS(CompatShortcutBin);
|
GDREGISTER_CLASS(CompatShortcutBin);
|
||||||
GDREGISTER_CLASS(CompatScreenSelect);
|
GDREGISTER_CLASS(CompatScreenSelect);
|
||||||
GDREGISTER_CLASS(CompatWindowWrapper);
|
GDREGISTER_CLASS(CompatWindowWrapper);
|
||||||
GDREGISTER_CLASS(BehaviorTreeView);
|
|
||||||
GDREGISTER_CLASS(LimboDebuggerTab);
|
GDREGISTER_CLASS(LimboDebuggerTab);
|
||||||
GDREGISTER_CLASS(LimboDebuggerPlugin);
|
GDREGISTER_CLASS(LimboDebuggerPlugin);
|
||||||
GDREGISTER_CLASS(BlackboardPlanEditor);
|
GDREGISTER_CLASS(BlackboardPlanEditor);
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include "../util/limbo_compat.h"
|
#include "../util/limbo_compat.h"
|
||||||
|
|
||||||
#ifdef LIMBOAI_MODULE
|
#ifdef LIMBOAI_MODULE
|
||||||
|
#include "core/config/project_settings.h"
|
||||||
#include "core/error/error_macros.h"
|
#include "core/error/error_macros.h"
|
||||||
#include "core/object/script_language.h"
|
#include "core/object/script_language.h"
|
||||||
#include "core/os/os.h"
|
#include "core/os/os.h"
|
||||||
|
@ -71,48 +72,53 @@ String LimboUtility::get_status_name(int p_status) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
Ref<Texture2D> LimboUtility::get_task_icon(String p_class_or_script_path) const {
|
Ref<Texture2D> LimboUtility::get_task_icon(String p_class_or_script_path) const {
|
||||||
#if defined(TOOLS_ENABLED) && defined(LIMBOAI_MODULE)
|
|
||||||
ERR_FAIL_COND_V_MSG(p_class_or_script_path.is_empty(), Variant(), "BTTask: script path or class cannot be empty.");
|
ERR_FAIL_COND_V_MSG(p_class_or_script_path.is_empty(), Variant(), "BTTask: script path or class cannot be empty.");
|
||||||
|
|
||||||
Ref<Theme> theme = EditorNode::get_singleton()->get_editor_theme();
|
// * Using editor theme
|
||||||
ERR_FAIL_COND_V(theme.is_null(), nullptr);
|
#if defined(TOOLS_ENABLED) && defined(LIMBOAI_MODULE)
|
||||||
|
if (Engine::get_singleton()->is_editor_hint()) {
|
||||||
|
Ref<Theme> theme = EditorNode::get_singleton()->get_editor_theme();
|
||||||
|
ERR_FAIL_COND_V(theme.is_null(), nullptr);
|
||||||
|
|
||||||
if (p_class_or_script_path.begins_with("res:")) {
|
if (p_class_or_script_path.begins_with("res:")) {
|
||||||
Ref<Script> s = ResourceLoader::load(p_class_or_script_path, "Script");
|
Ref<Script> s = ResourceLoader::load(p_class_or_script_path, "Script");
|
||||||
if (s.is_null()) {
|
if (s.is_null()) {
|
||||||
return theme->get_icon(SNAME("FileBroken"), SNAME("EditorIcons"));
|
return theme->get_icon(SNAME("FileBroken"), SNAME("EditorIcons"));
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorData &ed = EditorNode::get_editor_data();
|
||||||
|
Ref<Texture2D> script_icon = ed.get_script_icon(s);
|
||||||
|
if (script_icon.is_valid()) {
|
||||||
|
return script_icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringName base_type = s->get_instance_base_type();
|
||||||
|
if (theme->has_icon(base_type, SNAME("EditorIcons"))) {
|
||||||
|
return theme->get_icon(base_type, SNAME("EditorIcons"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
EditorData &ed = EditorNode::get_editor_data();
|
if (theme->has_icon(p_class_or_script_path, SNAME("EditorIcons"))) {
|
||||||
Ref<Texture2D> script_icon = ed.get_script_icon(s);
|
return theme->get_icon(p_class_or_script_path, SNAME("EditorIcons"));
|
||||||
if (script_icon.is_valid()) {
|
|
||||||
return script_icon;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
StringName base_type = s->get_instance_base_type();
|
// Use an icon of one of the base classes: look up max 3 parents.
|
||||||
if (theme->has_icon(base_type, SNAME("EditorIcons"))) {
|
StringName class_name = p_class_or_script_path;
|
||||||
return theme->get_icon(base_type, SNAME("EditorIcons"));
|
for (int i = 0; i < 3; i++) {
|
||||||
|
class_name = ClassDB::get_parent_class(class_name);
|
||||||
|
if (theme->has_icon(class_name, SNAME("EditorIcons"))) {
|
||||||
|
return theme->get_icon(class_name, SNAME("EditorIcons"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return generic resource icon as a fallback.
|
||||||
|
return theme->get_icon(SNAME("Resource"), SNAME("EditorIcons"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (theme->has_icon(p_class_or_script_path, SNAME("EditorIcons"))) {
|
|
||||||
return theme->get_icon(p_class_or_script_path, SNAME("EditorIcons"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use an icon of one of the base classes: look up max 3 parents.
|
|
||||||
StringName class_name = p_class_or_script_path;
|
|
||||||
for (int i = 0; i < 3; i++) {
|
|
||||||
class_name = ClassDB::get_parent_class(class_name);
|
|
||||||
if (theme->has_icon(class_name, SNAME("EditorIcons"))) {
|
|
||||||
return theme->get_icon(class_name, SNAME("EditorIcons"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Return generic resource icon as a fallback.
|
|
||||||
return theme->get_icon(SNAME("Resource"), SNAME("EditorIcons"));
|
|
||||||
#endif // ! TOOLS_ENABLED && LIMBOAI_MODULE
|
#endif // ! TOOLS_ENABLED && LIMBOAI_MODULE
|
||||||
|
|
||||||
#ifdef LIMBOAI_GDEXTENSION
|
|
||||||
String path;
|
String path;
|
||||||
|
|
||||||
|
#ifdef LIMBOAI_GDEXTENSION
|
||||||
if (p_class_or_script_path.begins_with("res://")) {
|
if (p_class_or_script_path.begins_with("res://")) {
|
||||||
TypedArray<Dictionary> classes = ProjectSettings::get_singleton()->get_global_class_list();
|
TypedArray<Dictionary> classes = ProjectSettings::get_singleton()->get_global_class_list();
|
||||||
for (int i = 0; i < classes.size(); i++) {
|
for (int i = 0; i < classes.size(); i++) {
|
||||||
|
@ -127,16 +133,18 @@ Ref<Texture2D> LimboUtility::get_task_icon(String p_class_or_script_path) const
|
||||||
path = "res://addons/limboai/icons/" + sc->get_instance_base_type() + ".svg";
|
path = "res://addons/limboai/icons/" + sc->get_instance_base_type() + ".svg";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
// Assuming global class.
|
#endif // LIMBOAI_GDEXTENSION
|
||||||
|
|
||||||
|
if (path.is_empty() && !p_class_or_script_path.begins_with("res://")) {
|
||||||
|
// Trying addons/limboai/icons/
|
||||||
path = "res://addons/limboai/icons/" + p_class_or_script_path + ".svg";
|
path = "res://addons/limboai/icons/" + p_class_or_script_path + ".svg";
|
||||||
}
|
}
|
||||||
|
|
||||||
Ref<Texture2D> icon = RESOURCE_LOAD(path, "Texture2D");
|
if (RESOURCE_EXISTS(path, "Texture2D")) {
|
||||||
return icon;
|
Ref<Texture2D> icon = RESOURCE_LOAD(path, "Texture2D");
|
||||||
#endif // LIMBOAI_GDEXTENSION
|
return icon;
|
||||||
|
}
|
||||||
// TODO: GDExtension needs the icons too.
|
|
||||||
|
|
||||||
// * Class icons are not available at runtime as they are part of the editor theme.
|
// * Class icons are not available at runtime as they are part of the editor theme.
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
Loading…
Reference in New Issue