Merge branch 'probability-selector'

This commit is contained in:
Serhii Snitsaruk 2023-09-26 16:38:50 +02:00
commit 00f9e03f02
7 changed files with 90 additions and 4 deletions

View File

@ -16,15 +16,20 @@
#include "core/error/error_macros.h" #include "core/error/error_macros.h"
double BTProbabilitySelector::get_weight(int p_index) const { double BTProbabilitySelector::get_weight(int p_index) const {
ERR_FAIL_INDEX_V(p_index, get_child_count(), 0.0);
ERR_FAIL_COND_V(get_child(p_index)->is_class_ptr(BTComment::get_class_ptr_static()), 0.0);
return _get_weight(p_index); return _get_weight(p_index);
} }
void BTProbabilitySelector::set_weight(int p_index, double p_weight) { void BTProbabilitySelector::set_weight(int p_index, double p_weight) {
ERR_FAIL_INDEX(p_index, get_child_count());
ERR_FAIL_COND(get_child(p_index)->is_class_ptr(BTComment::get_class_ptr_static()));
_set_weight(p_index, p_weight); _set_weight(p_index, p_weight);
} }
double BTProbabilitySelector::get_probability(int p_index) const { double BTProbabilitySelector::get_probability(int p_index) const {
ERR_FAIL_INDEX_V(p_index, get_child_count(), 0.0); ERR_FAIL_INDEX_V(p_index, get_child_count(), 0.0);
ERR_FAIL_COND_V(get_child(p_index)->is_class_ptr(BTComment::get_class_ptr_static()), 0.0);
double total = _get_total_weight(); double total = _get_total_weight();
return total == 0.0 ? 0.0 : _get_weight(p_index) / total; return total == 0.0 ? 0.0 : _get_weight(p_index) / total;
} }
@ -33,6 +38,7 @@ void BTProbabilitySelector::set_probability(int p_index, double p_probability) {
ERR_FAIL_INDEX(p_index, get_child_count()); ERR_FAIL_INDEX(p_index, get_child_count());
ERR_FAIL_COND(p_probability < 0.0); ERR_FAIL_COND(p_probability < 0.0);
ERR_FAIL_COND(p_probability >= 1.0); ERR_FAIL_COND(p_probability >= 1.0);
ERR_FAIL_COND(get_child(p_index)->is_class_ptr(BTComment::get_class_ptr_static()));
double others_total = _get_total_weight() - _get_weight(p_index); double others_total = _get_total_weight() - _get_weight(p_index);
double others_probability = 1.0 - p_probability; double others_probability = 1.0 - p_probability;
@ -44,6 +50,11 @@ void BTProbabilitySelector::set_probability(int p_index, double p_probability) {
} }
} }
bool BTProbabilitySelector::has_probability(int p_index) const {
ERR_FAIL_INDEX_V(p_index, get_child_count(), false);
return !get_child(p_index)->is_class_ptr(BTComment::get_class_ptr_static());
}
void BTProbabilitySelector::set_abort_on_failure(bool p_abort_on_failure) { void BTProbabilitySelector::set_abort_on_failure(bool p_abort_on_failure) {
abort_on_failure = p_abort_on_failure; abort_on_failure = p_abort_on_failure;
emit_changed(); emit_changed();
@ -115,6 +126,7 @@ void BTProbabilitySelector::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_total_weight"), &BTProbabilitySelector::get_total_weight); ClassDB::bind_method(D_METHOD("get_total_weight"), &BTProbabilitySelector::get_total_weight);
ClassDB::bind_method(D_METHOD("get_probability", "p_index"), &BTProbabilitySelector::get_probability); ClassDB::bind_method(D_METHOD("get_probability", "p_index"), &BTProbabilitySelector::get_probability);
ClassDB::bind_method(D_METHOD("set_probability", "p_index", "p_probability"), &BTProbabilitySelector::set_probability); ClassDB::bind_method(D_METHOD("set_probability", "p_index", "p_probability"), &BTProbabilitySelector::set_probability);
ClassDB::bind_method(D_METHOD("has_probability", "p_index"), &BTProbabilitySelector::has_probability);
ClassDB::bind_method(D_METHOD("get_abort_on_failure"), &BTProbabilitySelector::get_abort_on_failure); ClassDB::bind_method(D_METHOD("get_abort_on_failure"), &BTProbabilitySelector::get_abort_on_failure);
ClassDB::bind_method(D_METHOD("set_abort_on_failure", "p_value"), &BTProbabilitySelector::set_abort_on_failure); ClassDB::bind_method(D_METHOD("set_abort_on_failure", "p_value"), &BTProbabilitySelector::set_abort_on_failure);

View File

@ -12,6 +12,7 @@
#ifndef BT_PROBABILITY_SELECTOR_H #ifndef BT_PROBABILITY_SELECTOR_H
#define BT_PROBABILITY_SELECTOR_H #define BT_PROBABILITY_SELECTOR_H
#include "modules/limboai/bt/tasks/bt_comment.h"
#include "modules/limboai/bt/tasks/bt_composite.h" #include "modules/limboai/bt/tasks/bt_composite.h"
#include "core/core_string_names.h" #include "core/core_string_names.h"
@ -37,8 +38,10 @@ private:
_FORCE_INLINE_ double _get_total_weight() const { _FORCE_INLINE_ double _get_total_weight() const {
double total = 0.0; double total = 0.0;
for (int i = 0; i < get_child_count(); i++) { for (int i = 0; i < get_child_count(); i++) {
if (!get_child(i)->is_class_ptr(BTComment::get_class_ptr_static())) {
total += _get_weight(i); total += _get_weight(i);
} }
}
return total; return total;
} }
@ -57,6 +60,8 @@ public:
double get_probability(int p_index) const; double get_probability(int p_index) const;
void set_probability(int p_index, double p_probability); void set_probability(int p_index, double p_probability);
bool has_probability(int p_index) const;
void set_abort_on_failure(bool p_abort_on_failure); void set_abort_on_failure(bool p_abort_on_failure);
bool get_abort_on_failure() const; bool get_abort_on_failure() const;
}; };

View File

@ -0,0 +1,66 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="BTProbabilitySelector" inherits="BTComposite" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
BT composite that chooses a child task to execute based on attached probabilities.
</brief_description>
<description>
BTProbabilitySelector chooses a child task to execute based on attached probabilities. It is typically used for decision-making purposes. Probability distribution is calculated based on weights assigned to each child task.
Returns [code]SUCCESS[/code] when a child task returns [code]SUCCESS[/code].
Returns [code]RUNNING[/code] when a child task returns [code]RUNNING[/code].
The behavior of BTProbabilitySelector when a child task results in [code]FAILURE[/code] depends on the value of the [member abort_on_failure] property:
- If [member abort_on_failure] is [code]false[/code], when a child task results in [code]FAILURE[/code], BTProbabilitySelector will normalize the probability distribution over the remaiming children and choose a new child task to be executed. If all child tasks fail, the composite will return [code]FAILURE[/code].
- If [member abort_on_failure] is [code]true[/code], when a child task results in [code]FAILURE[/code], BTProbabilitySelector will not choose another child task to be executed and will immediately return [code]FAILURE[/code].
</description>
<tutorials>
</tutorials>
<methods>
<method name="get_probability" qualifiers="const">
<return type="float" />
<param index="0" name="p_index" type="int" />
<description>
Returns the child task's selection probability.
</description>
</method>
<method name="get_total_weight" qualifiers="const">
<return type="float" />
<description>
Returns the total weight of all child tasks.
</description>
</method>
<method name="get_weight" qualifiers="const">
<return type="float" />
<param index="0" name="p_index" type="int" />
<description>
Returns the child task's weight within the weighted probability selection algorithm.
</description>
</method>
<method name="has_probability" qualifiers="const">
<return type="bool" />
<param index="0" name="p_index" type="int" />
<description>
Returns whether the child task at index [param p_index] participates within the weighted probability selection algorithm and has a probability assigned to it. Returns [code]false[/code] for [BTComment] tasks.
</description>
</method>
<method name="set_probability">
<return type="void" />
<param index="0" name="p_index" type="int" />
<param index="1" name="p_probability" type="float" />
<description>
Sets the child task's weight calculated based on the desired probability.
</description>
</method>
<method name="set_weight">
<return type="void" />
<param index="0" name="p_index" type="int" />
<param index="1" name="p_weight" type="float" />
<description>
Sets the child task's weight for the weighted probability selection algorithm.
</description>
</method>
</methods>
<members>
<member name="abort_on_failure" type="bool" setter="set_abort_on_failure" getter="get_abort_on_failure" default="false">
If [code]true[/code], BTProbabilitySelector will not choose another child to execute and will return [code]FAILURE[/code] when a child task results in [code]FAILURE[/code].
</member>
</members>
</class>

View File

@ -271,7 +271,7 @@ void LimboAIEditor::_on_tree_rmb(const Vector2 &p_menu_pos) {
ERR_FAIL_COND_MSG(task.is_null(), "LimboAIEditor: get_selected() returned null"); ERR_FAIL_COND_MSG(task.is_null(), "LimboAIEditor: get_selected() returned null");
if (task_tree->selected_has_probability()) { if (task_tree->selected_has_probability()) {
menu->add_item(TTR("Edit Probability"), ACTION_EDIT_PROBABILITY); menu->add_icon_item(theme_cache.percent_icon, TTR("Edit Probability"), ACTION_EDIT_PROBABILITY);
} }
menu->add_icon_shortcut(theme_cache.rename_task_icon, ED_GET_SHORTCUT("limbo_ai/rename_task"), ACTION_RENAME); menu->add_icon_shortcut(theme_cache.rename_task_icon, ED_GET_SHORTCUT("limbo_ai/rename_task"), ACTION_RENAME);
menu->add_icon_item(theme_cache.edit_script_icon, TTR("Edit Script"), ACTION_EDIT_SCRIPT); menu->add_icon_item(theme_cache.edit_script_icon, TTR("Edit Script"), ACTION_EDIT_SCRIPT);
@ -799,6 +799,7 @@ void LimboAIEditor::_update_theme_item_cache() {
theme_cache.move_task_up_icon = get_theme_icon(SNAME("MoveUp"), SNAME("EditorIcons")); theme_cache.move_task_up_icon = get_theme_icon(SNAME("MoveUp"), SNAME("EditorIcons"));
theme_cache.open_debugger_icon = get_theme_icon(SNAME("Debug"), SNAME("EditorIcons")); theme_cache.open_debugger_icon = get_theme_icon(SNAME("Debug"), SNAME("EditorIcons"));
theme_cache.open_doc_icon = get_theme_icon(SNAME("Help"), SNAME("EditorIcons")); theme_cache.open_doc_icon = get_theme_icon(SNAME("Help"), SNAME("EditorIcons"));
theme_cache.percent_icon = get_theme_icon(SNAME("LimboPercent"), SNAME("EditorIcons"));
theme_cache.remove_task_icon = get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")); theme_cache.remove_task_icon = get_theme_icon(SNAME("Remove"), SNAME("EditorIcons"));
theme_cache.rename_task_icon = get_theme_icon(SNAME("Rename"), SNAME("EditorIcons")); theme_cache.rename_task_icon = get_theme_icon(SNAME("Rename"), SNAME("EditorIcons"));
} }

View File

@ -66,6 +66,7 @@ private:
Ref<Texture2D> move_task_up_icon; Ref<Texture2D> move_task_up_icon;
Ref<Texture2D> open_debugger_icon; Ref<Texture2D> open_debugger_icon;
Ref<Texture2D> open_doc_icon; Ref<Texture2D> open_doc_icon;
Ref<Texture2D> percent_icon;
Ref<Texture2D> remove_task_icon; Ref<Texture2D> remove_task_icon;
Ref<Texture2D> rename_task_icon; Ref<Texture2D> rename_task_icon;
} theme_cache; } theme_cache;

View File

@ -38,7 +38,7 @@ void TaskTree::_update_item(TreeItem *p_item) {
if (p_item->get_parent()) { if (p_item->get_parent()) {
Ref<BTProbabilitySelector> sel = p_item->get_parent()->get_metadata(0); Ref<BTProbabilitySelector> sel = p_item->get_parent()->get_metadata(0);
if (sel.is_valid()) { if (sel.is_valid() && sel->has_probability(p_item->get_index())) {
p_item->set_custom_draw(0, this, SNAME("_draw_probability")); p_item->set_custom_draw(0, this, SNAME("_draw_probability"));
p_item->set_cell_mode(0, TreeItem::CELL_MODE_CUSTOM); p_item->set_cell_mode(0, TreeItem::CELL_MODE_CUSTOM);
} }
@ -236,7 +236,7 @@ double TaskTree::get_selected_probability_percent() const {
bool TaskTree::selected_has_probability() const { bool TaskTree::selected_has_probability() const {
bool result = false; bool result = false;
Ref<BTTask> selected = get_selected(); Ref<BTTask> selected = get_selected();
if (selected.is_valid()) { if (selected.is_valid() && !selected->is_class_ptr(BTComment::get_class_ptr_static())) {
Ref<BTProbabilitySelector> probability_selector = selected->get_parent(); Ref<BTProbabilitySelector> probability_selector = selected->get_parent();
result = probability_selector.is_valid(); result = probability_selector.is_valid();
} }

1
icons/LimboPercent.svg Normal file
View File

@ -0,0 +1 @@
<svg enable-background="new 0 0 16 16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><g fill="#e0e0e0"><path d="m7.82 4.9c0-1.6-1.3-2.9-2.9-2.9-1.61 0-2.92 1.3-2.92 2.89 0 1.64 1.29 2.93 2.93 2.93 1.6 0 2.89-1.31 2.89-2.92zm-2.89 2.19c-1.23 0-2.2-.97-2.2-2.2 0-1.2.98-2.17 2.19-2.17 1.2 0 2.17.98 2.17 2.17.01 1.22-.97 2.21-2.16 2.2z"/><path d="m14 11.08c0-1.6-1.3-2.9-2.9-2.9-1.61 0-2.93 1.3-2.92 2.89 0 1.64 1.29 2.93 2.93 2.93 1.6 0 2.89-1.31 2.89-2.92zm-2.89 2.19c-1.23 0-2.2-.97-2.2-2.2 0-1.2.98-2.17 2.19-2.17 1.2 0 2.17.98 2.17 2.17 0 1.22-.97 2.2-2.16 2.2z"/><path d="m.72 7.27h14.56v1.46h-14.56z" transform="matrix(.7071 -.7071 .7071 .7071 -3.3139 8.0001)"/></g></svg>

After

Width:  |  Height:  |  Size: 686 B