Display and allow setting name/index of BlendSpace points

This commit is contained in:
vaner-org 2025-09-01 13:07:09 +05:30
parent 778cf54dab
commit f873fa3c3a
13 changed files with 1244 additions and 179 deletions

View file

@ -17,8 +17,17 @@
<param index="0" name="node" type="AnimationRootNode" />
<param index="1" name="pos" type="float" />
<param index="2" name="at_index" type="int" default="-1" />
<param index="3" name="name" type="StringName" default="&amp;&quot;&quot;" />
<description>
Adds a new point that represents a [param node] on the virtual axis at a given position set by [param pos]. You can insert it at a specific index using the [param at_index] argument. If you use the default value for [param at_index], the point is inserted at the end of the blend points array.
Adds a new point with [param name] that represents a [param node] on the virtual axis at a given position set by [param pos]. You can insert it at a specific index using the [param at_index] argument. If you use the default value for [param at_index], the point is inserted at the end of the blend points array.
[b]Note:[/b] If no name is provided, safe index is used as reference. In the future, empty names will be deprecated, so explicitly passing a name is recommended.
</description>
</method>
<method name="find_blend_point_by_name" qualifiers="const">
<return type="int" />
<param index="0" name="name" type="StringName" />
<description>
Returns the index of the blend point with the given [param name]. Returns [code]-1[/code] if no blend point with that name is found.
</description>
</method>
<method name="get_blend_point_count" qualifiers="const">
@ -27,6 +36,13 @@
Returns the number of points on the blend axis.
</description>
</method>
<method name="get_blend_point_name" qualifiers="const">
<return type="StringName" />
<param index="0" name="point" type="int" />
<description>
Returns the name of the blend point at index [param point].
</description>
</method>
<method name="get_blend_point_node" qualifiers="const">
<return type="AnimationRootNode" />
<param index="0" name="point" type="int" />
@ -48,6 +64,22 @@
Removes the point at index [param point] from the blend axis.
</description>
</method>
<method name="reorder_blend_point">
<return type="void" />
<param index="0" name="from_index" type="int" />
<param index="1" name="to_index" type="int" />
<description>
Swaps the blend points at indices [param from_index] and [param to_index], exchanging their positions and properties.
</description>
</method>
<method name="set_blend_point_name">
<return type="void" />
<param index="0" name="point" type="int" />
<param index="1" name="name" type="StringName" />
<description>
Sets the name of the blend point at index [param point]. If the name conflicts with an existing point, a unique name will be generated automatically.
</description>
</method>
<method name="set_blend_point_node">
<return type="void" />
<param index="0" name="point" type="int" />

View file

@ -18,8 +18,10 @@
<param index="0" name="node" type="AnimationRootNode" />
<param index="1" name="pos" type="Vector2" />
<param index="2" name="at_index" type="int" default="-1" />
<param index="3" name="name" type="StringName" default="&amp;&quot;&quot;" />
<description>
Adds a new point that represents a [param node] at the position set by [param pos]. You can insert it at a specific index using the [param at_index] argument. If you use the default value for [param at_index], the point is inserted at the end of the blend points array.
Adds a new point with [param name] that represents a [param node] at the position set by [param pos]. You can insert it at a specific index using the [param at_index] argument. If you use the default value for [param at_index], the point is inserted at the end of the blend points array.
[b]Note:[/b] If no name is provided, safe index is used as reference. In the future, empty names will be deprecated, so explicitly passing a name is recommended.
</description>
</method>
<method name="add_triangle">
@ -32,12 +34,26 @@
Creates a new triangle using three points [param x], [param y], and [param z]. Triangles can overlap. You can insert the triangle at a specific index using the [param at_index] argument. If you use the default value for [param at_index], the point is inserted at the end of the blend points array.
</description>
</method>
<method name="find_blend_point_by_name" qualifiers="const">
<return type="int" />
<param index="0" name="name" type="StringName" />
<description>
Returns the index of the blend point with the given [param name]. Returns [code]-1[/code] if no blend point with that name is found.
</description>
</method>
<method name="get_blend_point_count" qualifiers="const">
<return type="int" />
<description>
Returns the number of points in the blend space.
</description>
</method>
<method name="get_blend_point_name" qualifiers="const">
<return type="StringName" />
<param index="0" name="point" type="int" />
<description>
Returns the name of the blend point at index [param point].
</description>
</method>
<method name="get_blend_point_node" qualifiers="const">
<return type="AnimationRootNode" />
<param index="0" name="point" type="int" />
@ -80,6 +96,22 @@
Removes the triangle at index [param triangle] from the blend space.
</description>
</method>
<method name="reorder_blend_point">
<return type="void" />
<param index="0" name="from_index" type="int" />
<param index="1" name="to_index" type="int" />
<description>
Swaps the blend points at indices [param from_index] and [param to_index], exchanging their positions and properties.
</description>
</method>
<method name="set_blend_point_name">
<return type="void" />
<param index="0" name="point" type="int" />
<param index="1" name="name" type="StringName" />
<description>
Sets the name of the blend point at index [param point]. If the name conflicts with an existing point, a unique name will be generated automatically.
</description>
</method>
<method name="set_blend_point_node">
<return type="void" />
<param index="0" name="point" type="int" />

View file

@ -46,6 +46,7 @@
#include "scene/gui/panel_container.h"
#include "scene/gui/separator.h"
#include "scene/gui/spin_box.h"
#include "scene/main/timer.h"
StringName AnimationNodeBlendSpace1DEditor::get_blend_position_path() const {
StringName path = AnimationTreeEditor::get_singleton()->get_base_path() + "blend_position";
@ -60,12 +61,20 @@ void AnimationNodeBlendSpace1DEditor::_blend_space_gui_input(const Ref<InputEven
Ref<InputEventKey> k = p_event;
if (tool_select->is_pressed() && k.is_valid() && k->is_pressed() && k->get_keycode() == Key::KEY_DELETE && !k->is_echo()) {
if (selected_point != -1) {
if (!read_only) {
_erase_selected();
}
if (k.is_valid() && k->is_pressed() && !k->is_echo()) {
if (k->get_keycode() == Key::ESCAPE && editing_point != -1) {
_cancel_inline_edit();
accept_event();
return;
}
if (tool_select->is_pressed() && k->get_keycode() == Key::KEY_DELETE) {
if (selected_point != -1) {
if (!read_only) {
_erase_selected();
}
accept_event();
}
}
}
@ -128,16 +137,26 @@ void AnimationNodeBlendSpace1DEditor::_blend_space_gui_input(const Ref<InputEven
blend_space_draw->queue_redraw(); // why not
// try to see if a point can be selected
selected_point = -1;
_update_tool_erase();
_set_selected_point(-1);
// Check if clicking on text areas first.
for (int i = 0; i < text_rects.size(); i++) {
if (text_rects[i].has_point(mb->get_position())) {
_set_selected_point(i);
dragging_selected_attempt = true;
drag_from = mb->get_position();
_update_edited_point_name();
return;
}
}
// Then check point positions.
for (int i = 0; i < points.size(); i++) {
if (Math::abs(float(points[i] - mb->get_position().x)) < 10 * EDSCALE) {
selected_point = i;
_set_selected_point(i);
Ref<AnimationNode> node = blend_space->get_blend_point_node(i);
EditorNode::get_singleton()->push_item(node.ptr(), "", true);
if (mb->is_double_click() && AnimationTreeEditor::get_singleton()->can_edit(node)) {
_open_editor();
return;
@ -145,19 +164,24 @@ void AnimationNodeBlendSpace1DEditor::_blend_space_gui_input(const Ref<InputEven
dragging_selected_attempt = true;
drag_from = mb->get_position();
_update_tool_erase();
_update_edited_point_pos();
_update_edited_point_name();
return;
}
}
// If no point was selected, select host BlendSpace1D node.
if (selected_point == -1) {
EditorNode::get_singleton()->push_item(blend_space.ptr(), "", true);
}
}
if (mb.is_valid() && !mb->is_pressed() && dragging_selected_attempt && mb->get_button_index() == MouseButton::LEFT) {
// Check if releasing over text without any dragging - if so, start text editing instead.
if (!read_only && !dragging_selected) {
for (int i = 0; i < text_rects.size(); i++) {
if (text_rects[i].has_point(mb->get_position())) {
_start_inline_edit(i);
dragging_selected_attempt = false;
return;
}
}
}
if (!read_only) {
if (dragging_selected) {
// move
@ -170,7 +194,7 @@ void AnimationNodeBlendSpace1DEditor::_blend_space_gui_input(const Ref<InputEven
updating = true;
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Move Node Point"));
undo_redo->create_action(TTR("Move BlendSpace1D Node Point"));
undo_redo->add_do_method(blend_space.ptr(), "set_blend_point_position", selected_point, point);
undo_redo->add_undo_method(blend_space.ptr(), "set_blend_point_position", selected_point, blend_space->get_blend_point_position(selected_point));
undo_redo->add_do_method(this, "_update_space");
@ -221,6 +245,28 @@ void AnimationNodeBlendSpace1DEditor::_blend_space_gui_input(const Ref<InputEven
blend_space_draw->queue_redraw();
}
// Handle mousewheel for reordering points.
Ref<InputEventMouseButton> mw = p_event;
if (mw.is_valid() && mw->is_pressed() && (mw->get_button_index() == MouseButton::WHEEL_UP || mw->get_button_index() == MouseButton::WHEEL_DOWN)) {
if (!read_only && tool_select->is_pressed()) {
int hovered_point = -1;
for (int i = 0; i < points.size(); i++) {
if (Math::abs(float(points[i] - mw->get_position().x)) < 10 * EDSCALE) {
hovered_point = i;
break;
}
}
if (hovered_point != -1) {
int direction = (mw->get_button_index() == MouseButton::WHEEL_DOWN) ? -1 : 1;
int new_index = hovered_point + direction;
_set_selected_point(hovered_point);
_edit_point_index(new_index);
accept_event();
}
}
}
}
void AnimationNodeBlendSpace1DEditor::_blend_space_draw() {
@ -229,6 +275,9 @@ void AnimationNodeBlendSpace1DEditor::_blend_space_draw() {
return;
}
// Clear text rectangles for fresh click detection.
text_rects.clear();
Color linecolor = get_theme_color(SceneStringName(font_color), SNAME("Label"));
Color linecolor_soft = linecolor;
linecolor_soft.a *= 0.5;
@ -282,31 +331,33 @@ void AnimationNodeBlendSpace1DEditor::_blend_space_draw() {
for (int i = 0; i < blend_space->get_blend_point_count(); i++) {
float point = blend_space->get_blend_point_position(i);
if (!read_only) {
if (dragging_selected && selected_point == i) {
point += drag_ofs.x;
if (snap->is_pressed()) {
point = Math::snapped(point, blend_space->get_snap());
}
if (!read_only && dragging_selected && selected_point == i) {
point += drag_ofs.x;
if (snap->is_pressed()) {
point = Math::snapped(point, blend_space->get_snap());
}
}
point = (point - blend_space->get_min_space()) / (blend_space->get_max_space() - blend_space->get_min_space());
point *= s.width;
points.push_back(point);
Vector2 gui_point = Vector2(point, s.height / 2.0);
Vector2 gui_point = (Vector2(point, s.height / 2.0) - icon->get_size() / 2.0).floor();
blend_space_draw->draw_texture(i == selected_point ? icon_selected : icon, gui_point);
gui_point -= (icon->get_size() / 2.0);
if (point >= 0.0 && point <= s.width && editing_point != i) {
String name_text = show_indices ? itos(i) : String(blend_space->get_blend_point_name(i));
Vector2 text_size = font->get_string_size(name_text, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size);
Vector2 text_pos = Vector2(CLAMP(point - text_size.x / 2.0, 0, s.width - text_size.x), gui_point.y - 4 * EDSCALE);
gui_point = gui_point.floor();
Color name_color = i == selected_point ? get_theme_color(SNAME("accent_color"), EditorStringName(Editor)) : linecolor;
blend_space_draw->draw_string(font, text_pos, name_text, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, name_color);
if (i == selected_point) {
blend_space_draw->draw_texture(icon_selected, gui_point);
} else {
blend_space_draw->draw_texture(icon, gui_point);
if (text_rects.size() <= i) {
text_rects.resize(i + 1);
}
text_rects.write[i] = Rect2(Vector2(text_pos.x, text_pos.y - font->get_ascent(font_size)), text_size);
}
}
@ -413,6 +464,19 @@ void AnimationNodeBlendSpace1DEditor::_file_opened(const String &p_file) {
}
}
String AnimationNodeBlendSpace1DEditor::_get_safe_name(const Ref<AnimationNodeBlendSpace1D> &p_blend_space, const String &p_name) {
String final_name = p_name;
// Append a number suffix if there's a naming conflict.
int suffix = 1;
while (p_blend_space->find_blend_point_by_name(final_name) != -1) {
suffix++;
final_name = p_name + " " + itos(suffix);
}
return final_name;
}
void AnimationNodeBlendSpace1DEditor::_add_menu_type(int p_index) {
Ref<AnimationRootNode> node;
if (p_index == MENU_LOAD_FILE) {
@ -448,7 +512,7 @@ void AnimationNodeBlendSpace1DEditor::_add_menu_type(int p_index) {
updating = true;
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Add Node Point"));
undo_redo->add_do_method(blend_space.ptr(), "add_blend_point", node, add_point_pos);
undo_redo->add_do_method(blend_space.ptr(), "add_blend_point", node, add_point_pos, -1, _get_safe_name(blend_space, node->get_class().replace_first("AnimationNode", "")));
undo_redo->add_undo_method(blend_space.ptr(), "remove_blend_point", blend_space->get_blend_point_count());
undo_redo->add_do_method(this, "_update_space");
undo_redo->add_undo_method(this, "_update_space");
@ -467,7 +531,7 @@ void AnimationNodeBlendSpace1DEditor::_add_animation_type(int p_index) {
updating = true;
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Add Animation Point"));
undo_redo->add_do_method(blend_space.ptr(), "add_blend_point", anim, add_point_pos);
undo_redo->add_do_method(blend_space.ptr(), "add_blend_point", anim, add_point_pos, -1, _get_safe_name(blend_space, animations_to_add[p_index]));
undo_redo->add_undo_method(blend_space.ptr(), "remove_blend_point", blend_space->get_blend_point_count());
undo_redo->add_do_method(this, "_update_space");
undo_redo->add_undo_method(this, "_update_space");
@ -508,10 +572,19 @@ void AnimationNodeBlendSpace1DEditor::_update_edited_point_pos() {
updating = true;
edit_value->set_value(pos);
index_edit->set_max(blend_space->get_blend_point_count() - 1);
index_edit->set_value(selected_point);
index_edit->set_editable(blend_space->get_blend_point_count() > 1 && !read_only);
updating = false;
}
}
void AnimationNodeBlendSpace1DEditor::_update_edited_point_name() {
if (updating) {
return;
}
}
void AnimationNodeBlendSpace1DEditor::_update_tool_erase() {
bool point_valid = selected_point >= 0 && selected_point < blend_space->get_blend_point_count();
tool_erase->set_disabled(!point_valid || read_only);
@ -521,8 +594,10 @@ void AnimationNodeBlendSpace1DEditor::_update_tool_erase() {
if (AnimationTreeEditor::get_singleton()->can_edit(an)) {
open_editor->show();
open_editor_sep->show();
} else {
open_editor->hide();
open_editor_sep->hide();
}
if (!read_only) {
@ -539,19 +614,21 @@ void AnimationNodeBlendSpace1DEditor::_erase_selected() {
if (selected_point != -1) {
updating = true;
Ref<AnimationRootNode> node = blend_space->get_blend_point_node(selected_point);
float position = blend_space->get_blend_point_position(selected_point);
String point_name = blend_space->get_blend_point_name(selected_point);
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Remove BlendSpace1D Point"));
undo_redo->add_do_method(blend_space.ptr(), "remove_blend_point", selected_point);
undo_redo->add_undo_method(blend_space.ptr(), "add_blend_point", blend_space->get_blend_point_node(selected_point), blend_space->get_blend_point_position(selected_point), selected_point);
undo_redo->add_undo_method(blend_space.ptr(), "add_blend_point", node, position, selected_point, point_name);
undo_redo->add_do_method(this, "_update_space");
undo_redo->add_undo_method(this, "_update_space");
undo_redo->commit_action();
// Return selection to host BlendSpace1D node.
EditorNode::get_singleton()->push_item(blend_space.ptr(), "", true);
_set_selected_point(-1);
updating = false;
_update_tool_erase();
blend_space_draw->queue_redraw();
}
@ -577,11 +654,77 @@ void AnimationNodeBlendSpace1DEditor::_edit_point_pos(double) {
blend_space_draw->queue_redraw();
}
void AnimationNodeBlendSpace1DEditor::_edit_point_index(double p_index) {
if (updating || selected_point < 0 || selected_point >= blend_space->get_blend_point_count()) {
return;
}
int new_index = (int)p_index;
if (new_index < 0 || new_index >= blend_space->get_blend_point_count() || new_index == selected_point) {
return;
}
updating = true;
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Reorder BlendSpace1D Point Index"));
undo_redo->add_do_method(blend_space.ptr(), "reorder_blend_point", selected_point, new_index);
undo_redo->add_undo_method(blend_space.ptr(), "reorder_blend_point", new_index, selected_point);
undo_redo->add_do_method(this, "_update_space");
undo_redo->add_undo_method(this, "_update_space");
undo_redo->add_do_method(this, "_set_selected_point", new_index);
undo_redo->add_undo_method(this, "_set_selected_point", selected_point);
undo_redo->add_do_method(this, "_show_indices_with_cooldown");
undo_redo->add_undo_method(this, "_show_indices_with_cooldown");
undo_redo->commit_action();
updating = false;
blend_space_draw->queue_redraw();
}
void AnimationNodeBlendSpace1DEditor::_set_selected_point(int p_index) {
selected_point = p_index;
_update_tool_erase();
if (p_index != -1) {
_update_edited_point_pos();
Ref<AnimationNode> node = blend_space->get_blend_point_node(p_index);
EditorNode::get_singleton()->push_item(node.ptr(), "", true);
} else {
// Return selection to host BlendSpace1D node.
EditorNode::get_singleton()->push_item(blend_space.ptr(), "", true);
}
}
void AnimationNodeBlendSpace1DEditor::_edit_point_name(const String &p_name) {
if (updating || selected_point == -1 || p_name.is_empty()) {
return;
}
String old_name = blend_space->get_blend_point_name(selected_point);
if (p_name == old_name) {
return;
}
String safe_name = _get_safe_name(blend_space, p_name);
updating = true;
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Change BlendSpace1D Point Name"));
undo_redo->add_do_method(blend_space.ptr(), "set_blend_point_name", selected_point, safe_name);
undo_redo->add_undo_method(blend_space.ptr(), "set_blend_point_name", selected_point, old_name);
undo_redo->add_do_method(this, "_update_space");
undo_redo->add_undo_method(this, "_update_space");
undo_redo->add_do_method(this, "_update_edited_point_name");
undo_redo->add_undo_method(this, "_update_edited_point_name");
undo_redo->commit_action();
updating = false;
blend_space_draw->queue_redraw();
}
void AnimationNodeBlendSpace1DEditor::_open_editor() {
if (selected_point >= 0 && selected_point < blend_space->get_blend_point_count()) {
Ref<AnimationNode> an = blend_space->get_blend_point_node(selected_point);
ERR_FAIL_COND(an.is_null());
AnimationTreeEditor::get_singleton()->enter_editor(itos(selected_point));
AnimationTreeEditor::get_singleton()->enter_editor(blend_space->get_blend_point_name(selected_point));
}
}
@ -634,6 +777,9 @@ void AnimationNodeBlendSpace1DEditor::_bind_methods() {
ClassDB::bind_method("_update_tool_erase", &AnimationNodeBlendSpace1DEditor::_update_tool_erase);
ClassDB::bind_method("_update_edited_point_pos", &AnimationNodeBlendSpace1DEditor::_update_edited_point_pos);
ClassDB::bind_method("_update_edited_point_name", &AnimationNodeBlendSpace1DEditor::_update_edited_point_name);
ClassDB::bind_method("_set_selected_point", &AnimationNodeBlendSpace1DEditor::_set_selected_point);
ClassDB::bind_method("_show_indices_with_cooldown", &AnimationNodeBlendSpace1DEditor::_show_indices_with_cooldown);
}
bool AnimationNodeBlendSpace1DEditor::can_edit(const Ref<AnimationNode> &p_node) {
@ -653,6 +799,7 @@ void AnimationNodeBlendSpace1DEditor::edit(const Ref<AnimationNode> &p_node) {
tool_create->set_disabled(read_only);
edit_value->set_editable(!read_only);
index_edit->set_editable(!read_only);
label_value->set_editable(!read_only);
min_value->set_editable(!read_only);
max_value->set_editable(!read_only);
@ -660,6 +807,133 @@ void AnimationNodeBlendSpace1DEditor::edit(const Ref<AnimationNode> &p_node) {
interpolation->set_disabled(read_only);
}
void AnimationNodeBlendSpace1DEditor::_start_inline_edit(int p_point) {
if (editing_point != -1 || p_point < 0 || p_point >= blend_space->get_blend_point_count()) {
return;
}
editing_point = p_point;
_set_selected_point(p_point);
inline_editor = memnew(LineEdit);
blend_space_draw->add_child(inline_editor);
inline_editor->add_theme_color_override(SceneStringName(font_color), get_theme_color(SNAME("accent_color"), EditorStringName(Editor)));
inline_editor->add_theme_color_override("font_selected_color", Color::named("white"));
inline_editor->add_theme_color_override("selection_color", get_theme_color(SNAME("accent_color"), EditorStringName(Editor)));
StyleBoxEmpty *empty_style = memnew(StyleBoxEmpty);
empty_style->set_content_margin_all(0);
inline_editor->add_theme_style_override(CoreStringName(normal), empty_style);
inline_editor->add_theme_style_override("focus", memnew(StyleBoxEmpty));
inline_editor->add_theme_style_override("read_only", memnew(StyleBoxEmpty));
inline_editor->add_theme_constant_override("minimum_character_width", 0);
inline_editor->set_flat(true);
inline_editor->set_text(blend_space->get_blend_point_name(p_point));
inline_editor->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
inline_editor->set_expand_to_text_length_enabled(true);
if (p_point < text_rects.size() && p_point < points.size()) {
Rect2 text_rect = text_rects[p_point];
inline_editor_point_x = points[p_point];
float editor_width = text_rect.size.x;
inline_editor->set_size(Vector2(editor_width, text_rect.size.y));
Size2 s = blend_space_draw->get_size();
float editor_x = inline_editor_point_x - editor_width / 2.0;
editor_x = CLAMP(editor_x, 0.0f, s.width - editor_width);
inline_editor->set_position(Vector2(editor_x, text_rect.position.y - 1 * EDSCALE));
}
inline_editor->connect(SceneStringName(text_changed), callable_mp(this, &AnimationNodeBlendSpace1DEditor::_inline_editor_text_changed));
inline_editor->connect(SceneStringName(text_submitted), callable_mp(this, &AnimationNodeBlendSpace1DEditor::_finish_inline_edit_with_text));
inline_editor->connect(SceneStringName(focus_exited), callable_mp(this, &AnimationNodeBlendSpace1DEditor::_finish_inline_edit));
inline_editor->grab_focus();
inline_editor->select_all();
blend_space_draw->queue_redraw();
}
void AnimationNodeBlendSpace1DEditor::_finish_inline_edit() {
if (editing_point == -1 || !inline_editor) {
return;
}
String new_name = inline_editor->get_text();
_edit_point_name(new_name);
_cancel_inline_edit();
}
void AnimationNodeBlendSpace1DEditor::_finish_inline_edit_with_text(const String &p_text) {
if (editing_point == -1 || !inline_editor) {
return;
}
_edit_point_name(p_text);
_cancel_inline_edit();
}
void AnimationNodeBlendSpace1DEditor::_cancel_inline_edit() {
if (inline_editor) {
inline_editor->queue_free();
inline_editor = nullptr;
}
editing_point = -1;
blend_space_draw->queue_redraw();
}
void AnimationNodeBlendSpace1DEditor::_inline_editor_text_changed(const String &p_text) {
if (!inline_editor) {
return;
}
inline_editor->set_size(Vector2(0, inline_editor->get_size().y));
Vector2 editor_size = inline_editor->get_size();
Size2 s = blend_space_draw->get_size();
float editor_x = inline_editor_point_x - editor_size.x / 2.0;
editor_x = CLAMP(editor_x, 0.0f, s.width - editor_size.x);
inline_editor->set_position(Vector2(editor_x, inline_editor->get_position().y));
}
void AnimationNodeBlendSpace1DEditor::_index_edit_focus_entered() {
if (index_focus_cooldown_timer->is_stopped() == false) {
index_focus_cooldown_timer->stop();
}
index_edit_has_focus = true;
show_indices = true;
blend_space_draw->queue_redraw();
}
void AnimationNodeBlendSpace1DEditor::_index_edit_focus_exited() {
index_edit_has_focus = false;
index_focus_cooldown_timer->start();
}
void AnimationNodeBlendSpace1DEditor::_index_focus_cooldown_timeout() {
if (!index_edit_has_focus) {
show_indices = false;
blend_space_draw->queue_redraw();
}
}
void AnimationNodeBlendSpace1DEditor::_show_indices_with_cooldown() {
if (index_focus_cooldown_timer->is_stopped() == false) {
index_focus_cooldown_timer->stop();
}
show_indices = true;
index_focus_cooldown_timer->start();
blend_space_draw->queue_redraw();
}
AnimationNodeBlendSpace1DEditor *AnimationNodeBlendSpace1DEditor::singleton = nullptr;
AnimationNodeBlendSpace1DEditor::AnimationNodeBlendSpace1DEditor() {
@ -677,7 +951,7 @@ AnimationNodeBlendSpace1DEditor::AnimationNodeBlendSpace1DEditor() {
tool_select->set_button_group(bg);
top_hb->add_child(tool_select);
tool_select->set_pressed(true);
tool_select->set_tooltip_text(TTR("Select and move points.\nRMB: Create point at position clicked.\nShift+LMB+Drag: Set the blending position within the space."));
tool_select->set_tooltip_text(TTR("Select and move points.\nRMB: Create point at position clicked.\nShift+LMB+Drag: Set the blending position within the space.\nScroll: Increment or decrement index."));
tool_select->connect(SceneStringName(pressed), callable_mp(this, &AnimationNodeBlendSpace1DEditor::_tool_switch).bind(0));
tool_create = memnew(Button);
@ -722,23 +996,46 @@ AnimationNodeBlendSpace1DEditor::AnimationNodeBlendSpace1DEditor() {
snap_value->set_accessibility_name(TTRC("Grid Step"));
top_hb->add_child(memnew(VSeparator));
top_hb->add_child(memnew(Label(TTR("Sync:"))));
top_hb->add_child(memnew(Label(TTR("Sync"))));
sync = memnew(CheckBox);
top_hb->add_child(sync);
sync->connect(SceneStringName(toggled), callable_mp(this, &AnimationNodeBlendSpace1DEditor::_config_changed));
top_hb->add_child(memnew(VSeparator));
top_hb->add_child(memnew(Label(TTR("Blend:"))));
top_hb->add_child(memnew(Label(TTR("Blend"))));
interpolation = memnew(OptionButton);
top_hb->add_child(interpolation);
interpolation->connect(SceneStringName(item_selected), callable_mp(this, &AnimationNodeBlendSpace1DEditor::_config_changed));
top_hb->add_spacer();
edit_hb = memnew(HBoxContainer);
top_hb->add_child(edit_hb);
edit_hb->add_child(memnew(VSeparator));
edit_hb->add_child(memnew(Label(TTR("Point"))));
open_editor = memnew(Button);
edit_hb->add_child(open_editor);
open_editor->set_text(TTR("Open Editor"));
open_editor->connect(SceneStringName(pressed), callable_mp(this, &AnimationNodeBlendSpace1DEditor::_open_editor), CONNECT_DEFERRED);
open_editor_sep = memnew(VSeparator);
edit_hb->add_child(open_editor_sep);
edit_hb->add_child(memnew(Label(TTR("Index"))));
index_edit = memnew(SpinBox);
edit_hb->add_child(index_edit);
index_edit->set_min(0);
index_edit->set_step(1);
index_edit->set_allow_greater(false);
index_edit->set_allow_lesser(false);
index_edit->set_accessibility_name(TTRC("Blend Point Index"));
index_edit->set_tooltip_text(TTR("Index of the blend point.\nValues outside of the valid range will be clamped to the nearest index."));
index_edit->connect(SceneStringName(value_changed), callable_mp(this, &AnimationNodeBlendSpace1DEditor::_edit_point_index));
index_edit->get_line_edit()->connect(SceneStringName(focus_entered), callable_mp(this, &AnimationNodeBlendSpace1DEditor::_index_edit_focus_entered));
index_edit->get_line_edit()->connect(SceneStringName(focus_exited), callable_mp(this, &AnimationNodeBlendSpace1DEditor::_index_edit_focus_exited));
edit_hb->add_child(memnew(VSeparator));
edit_hb->add_child(memnew(Label(TTR("Position"))));
edit_value = memnew(SpinBox);
edit_hb->add_child(edit_value);
edit_value->set_min(-1000);
@ -747,13 +1044,9 @@ AnimationNodeBlendSpace1DEditor::AnimationNodeBlendSpace1DEditor() {
edit_value->set_accessibility_name(TTRC("Blend Value"));
edit_value->connect(SceneStringName(value_changed), callable_mp(this, &AnimationNodeBlendSpace1DEditor::_edit_point_pos));
open_editor = memnew(Button);
edit_hb->add_child(open_editor);
open_editor->set_text(TTR("Open Editor"));
open_editor->connect(SceneStringName(pressed), callable_mp(this, &AnimationNodeBlendSpace1DEditor::_open_editor), CONNECT_DEFERRED);
edit_hb->hide();
open_editor->hide();
open_editor_sep->hide();
VBoxContainer *main_vb = memnew(VBoxContainer);
add_child(main_vb);
@ -830,5 +1123,12 @@ AnimationNodeBlendSpace1DEditor::AnimationNodeBlendSpace1DEditor() {
open_file->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILE);
open_file->connect("file_selected", callable_mp(this, &AnimationNodeBlendSpace1DEditor::_file_opened));
// Create timer for index focus cooldown (1.5 seconds).
index_focus_cooldown_timer = memnew(Timer);
add_child(index_focus_cooldown_timer);
index_focus_cooldown_timer->set_wait_time(1.5);
index_focus_cooldown_timer->set_one_shot(true);
index_focus_cooldown_timer->connect("timeout", callable_mp(this, &AnimationNodeBlendSpace1DEditor::_index_focus_cooldown_timeout));
set_custom_minimum_size(Size2(0, 150 * EDSCALE));
}

View file

@ -41,6 +41,7 @@ class LineEdit;
class OptionButton;
class PanelContainer;
class SpinBox;
class Timer;
class VSeparator;
class AnimationNodeBlendSpace1DEditor : public AnimationTreeNodeEditorPlugin {
@ -68,6 +69,8 @@ class AnimationNodeBlendSpace1DEditor : public AnimationTreeNodeEditorPlugin {
HBoxContainer *edit_hb = nullptr;
SpinBox *edit_value = nullptr;
Button *open_editor = nullptr;
VSeparator *open_editor_sep = nullptr;
SpinBox *index_edit = nullptr;
int selected_point = -1;
@ -101,15 +104,36 @@ class AnimationNodeBlendSpace1DEditor : public AnimationTreeNodeEditorPlugin {
Vector2 drag_from;
Vector2 drag_ofs;
Vector<Rect2> text_rects;
int editing_point = -1;
LineEdit *inline_editor = nullptr;
float inline_editor_point_x = 0.0f;
bool index_edit_has_focus = false;
bool show_indices = false;
Timer *index_focus_cooldown_timer = nullptr;
void _add_menu_type(int p_index);
void _add_animation_type(int p_index);
void _tool_switch(int p_tool);
void _update_edited_point_pos();
void _update_edited_point_name();
void _update_tool_erase();
void _erase_selected();
void _edit_point_pos(double);
void _edit_point_name(const String &p_name);
void _edit_point_index(double p_index);
void _set_selected_point(int p_index);
void _start_inline_edit(int p_point);
void _finish_inline_edit();
void _finish_inline_edit_with_text(const String &p_text);
void _cancel_inline_edit();
void _inline_editor_text_changed(const String &p_text);
void _open_editor();
void _index_edit_focus_entered();
void _index_edit_focus_exited();
void _index_focus_cooldown_timeout();
void _show_indices_with_cooldown();
EditorFileDialog *open_file = nullptr;
Ref<AnimationNode> file_loaded;
@ -122,6 +146,7 @@ class AnimationNodeBlendSpace1DEditor : public AnimationTreeNodeEditorPlugin {
};
StringName get_blend_position_path() const;
String _get_safe_name(const Ref<AnimationNodeBlendSpace1D> &p_blend_space, const String &p_name);
protected:
void _notification(int p_what);
@ -129,6 +154,7 @@ protected:
public:
static AnimationNodeBlendSpace1DEditor *get_singleton() { return singleton; }
void refresh_editor() { _update_space(); }
virtual bool can_edit(const Ref<AnimationNode> &p_node) override;
virtual void edit(const Ref<AnimationNode> &p_node) override;
AnimationNodeBlendSpace1DEditor();

View file

@ -52,6 +52,7 @@
#include "scene/gui/panel_container.h"
#include "scene/gui/separator.h"
#include "scene/gui/spin_box.h"
#include "scene/main/timer.h"
#include "scene/main/window.h"
bool AnimationNodeBlendSpace2DEditor::can_edit(const Ref<AnimationNode> &p_node) {
@ -86,6 +87,7 @@ void AnimationNodeBlendSpace2DEditor::edit(const Ref<AnimationNode> &p_node) {
label_y->set_editable(!read_only);
edit_x->set_editable(!read_only);
edit_y->set_editable(!read_only);
index_edit->set_editable(!read_only);
tool_triangle->set_disabled(read_only);
auto_triangles->set_disabled(read_only);
sync->set_disabled(read_only);
@ -104,12 +106,20 @@ void AnimationNodeBlendSpace2DEditor::_blend_space_gui_input(const Ref<InputEven
}
Ref<InputEventKey> k = p_event;
if (tool_select->is_pressed() && k.is_valid() && k->is_pressed() && k->get_keycode() == Key::KEY_DELETE && !k->is_echo()) {
if (selected_point != -1 || selected_triangle != -1) {
if (!read_only) {
_erase_selected();
}
if (k.is_valid() && k->is_pressed() && !k->is_echo()) {
if (k->get_keycode() == Key::ESCAPE && editing_point != -1) {
_cancel_inline_edit();
accept_event();
return;
}
if (tool_select->is_pressed() && k->get_keycode() == Key::KEY_DELETE) {
if (selected_point != -1 || selected_triangle != -1) {
if (!read_only) {
_erase_selected();
}
accept_event();
}
}
}
@ -168,17 +178,28 @@ void AnimationNodeBlendSpace2DEditor::_blend_space_gui_input(const Ref<InputEven
if (mb.is_valid() && mb->is_pressed() && tool_select->is_pressed() && !mb->is_shift_pressed() && !mb->is_command_or_control_pressed() && mb->get_button_index() == MouseButton::LEFT) {
blend_space_draw->queue_redraw(); //update anyway
//try to see if a point can be selected
selected_point = -1;
selected_triangle = -1;
_update_tool_erase();
//try to see if a point can be selected
_set_selected_point(-1);
selected_triangle = -1;
// Check if clicking on text areas first.
for (int i = 0; i < text_rects.size(); i++) {
if (text_rects[i].has_point(mb->get_position())) {
_set_selected_point(i);
dragging_selected_attempt = true;
drag_from = mb->get_position();
_update_edited_point_name();
return;
}
}
// Then check point positions.
for (int i = 0; i < points.size(); i++) {
if (points[i].distance_to(mb->get_position()) < 10 * EDSCALE) {
selected_point = i;
Ref<AnimationNode> node = blend_space->get_blend_point_node(i);
EditorNode::get_singleton()->push_item(node.ptr(), "", true);
_set_selected_point(i);
Ref<AnimationNode> node = blend_space->get_blend_point_node(i);
if (mb->is_double_click() && AnimationTreeEditor::get_singleton()->can_edit(node)) {
_open_editor();
return;
@ -186,8 +207,7 @@ void AnimationNodeBlendSpace2DEditor::_blend_space_gui_input(const Ref<InputEven
dragging_selected_attempt = true;
drag_from = mb->get_position();
_update_tool_erase();
_update_edited_point_pos();
_update_edited_point_name();
return;
}
}
@ -210,17 +230,12 @@ void AnimationNodeBlendSpace2DEditor::_blend_space_gui_input(const Ref<InputEven
}
}
}
// If no point or triangle was selected, select host BlendSpace2D node.
if (selected_point == -1 && selected_triangle == -1) {
EditorNode::get_singleton()->push_item(blend_space.ptr(), "", true);
}
}
if (mb.is_valid() && mb->is_pressed() && tool_triangle->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
blend_space_draw->queue_redraw(); //update anyway
//try to see if a point can be selected
selected_point = -1;
_set_selected_point(-1);
for (int i = 0; i < points.size(); i++) {
if (making_triangle.has(i)) {
@ -254,6 +269,17 @@ void AnimationNodeBlendSpace2DEditor::_blend_space_gui_input(const Ref<InputEven
}
if (mb.is_valid() && !mb->is_pressed() && dragging_selected_attempt && mb->get_button_index() == MouseButton::LEFT) {
// Check if releasing over text without actual dragging - if so, start text editing instead.
if (!read_only && !dragging_selected) {
for (int i = 0; i < text_rects.size(); i++) {
if (text_rects[i].has_point(mb->get_position())) {
_start_inline_edit(i);
dragging_selected_attempt = false;
return;
}
}
}
if (dragging_selected) {
//move
Vector2 point = blend_space->get_blend_point_position(selected_point);
@ -265,7 +291,7 @@ void AnimationNodeBlendSpace2DEditor::_blend_space_gui_input(const Ref<InputEven
if (!read_only) {
updating = true;
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Move Node Point"));
undo_redo->create_action(TTR("Move BlendSpace2D Node Point"));
undo_redo->add_do_method(blend_space.ptr(), "set_blend_point_position", selected_point, point);
undo_redo->add_undo_method(blend_space.ptr(), "set_blend_point_position", selected_point, blend_space->get_blend_point_position(selected_point));
undo_redo->add_do_method(this, "_update_space");
@ -327,6 +353,29 @@ void AnimationNodeBlendSpace2DEditor::_blend_space_gui_input(const Ref<InputEven
blend_space_draw->queue_redraw();
}
// Handle mousewheel for reordering points.
Ref<InputEventMouseButton> mw = p_event;
if (mw.is_valid() && mw->is_pressed() && (mw->get_button_index() == MouseButton::WHEEL_UP || mw->get_button_index() == MouseButton::WHEEL_DOWN)) {
if (!read_only && tool_select->is_pressed()) {
// Check if hovering over a point.
int hovered_point = -1;
for (int i = 0; i < points.size(); i++) {
if (points[i].distance_to(mw->get_position()) < 10 * EDSCALE) {
hovered_point = i;
break;
}
}
if (hovered_point != -1) {
int direction = (mw->get_button_index() == MouseButton::WHEEL_DOWN) ? -1 : 1;
int new_index = hovered_point + direction;
_set_selected_point(hovered_point);
_edit_point_index(new_index);
accept_event();
}
}
}
}
void AnimationNodeBlendSpace2DEditor::_file_opened(const String &p_file) {
@ -338,6 +387,19 @@ void AnimationNodeBlendSpace2DEditor::_file_opened(const String &p_file) {
}
}
String AnimationNodeBlendSpace2DEditor::_get_safe_name(const Ref<AnimationNodeBlendSpace2D> &p_blend_space, const String &p_name) {
String final_name = p_name;
// Append a number suffix if there's a naming conflict.
int suffix = 1;
while (p_blend_space->find_blend_point_by_name(final_name) != -1) {
suffix++;
final_name = p_name + " " + itos(suffix);
}
return final_name;
}
void AnimationNodeBlendSpace2DEditor::_add_menu_type(int p_index) {
Ref<AnimationRootNode> node;
if (p_index == MENU_LOAD_FILE) {
@ -373,7 +435,7 @@ void AnimationNodeBlendSpace2DEditor::_add_menu_type(int p_index) {
updating = true;
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Add Node Point"));
undo_redo->add_do_method(blend_space.ptr(), "add_blend_point", node, add_point_pos);
undo_redo->add_do_method(blend_space.ptr(), "add_blend_point", node, add_point_pos, -1, _get_safe_name(blend_space, node->get_class().replace_first("AnimationNode", "")));
undo_redo->add_undo_method(blend_space.ptr(), "remove_blend_point", blend_space->get_blend_point_count());
undo_redo->add_do_method(this, "_update_space");
undo_redo->add_undo_method(this, "_update_space");
@ -392,7 +454,7 @@ void AnimationNodeBlendSpace2DEditor::_add_animation_type(int p_index) {
updating = true;
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Add Animation Point"));
undo_redo->add_do_method(blend_space.ptr(), "add_blend_point", anim, add_point_pos);
undo_redo->add_do_method(blend_space.ptr(), "add_blend_point", anim, add_point_pos, -1, _get_safe_name(blend_space, animations_to_add[p_index]));
undo_redo->add_undo_method(blend_space.ptr(), "remove_blend_point", blend_space->get_blend_point_count());
undo_redo->add_do_method(this, "_update_space");
undo_redo->add_undo_method(this, "_update_space");
@ -411,8 +473,10 @@ void AnimationNodeBlendSpace2DEditor::_update_tool_erase() {
Ref<AnimationNode> an = blend_space->get_blend_point_node(selected_point);
if (AnimationTreeEditor::get_singleton()->can_edit(an)) {
open_editor->show();
open_editor_sep->show();
} else {
open_editor->hide();
open_editor_sep->hide();
}
if (!read_only) {
edit_hb->show();
@ -455,6 +519,9 @@ void AnimationNodeBlendSpace2DEditor::_blend_space_draw() {
return;
}
// Clear text rectangles for fresh click detection.
text_rects.clear();
Color linecolor = get_theme_color(SceneStringName(font_color), SNAME("Label"));
Color linecolor_soft = linecolor;
linecolor_soft.a *= 0.5;
@ -563,26 +630,39 @@ void AnimationNodeBlendSpace2DEditor::_blend_space_draw() {
points.clear();
for (int i = 0; i < blend_space->get_blend_point_count(); i++) {
Vector2 point = blend_space->get_blend_point_position(i);
if (!read_only) {
if (dragging_selected && selected_point == i) {
point += drag_ofs;
if (snap->is_pressed()) {
point = point.snapped(blend_space->get_snap());
}
if (!read_only && dragging_selected && selected_point == i) {
point += drag_ofs;
if (snap->is_pressed()) {
point = point.snapped(blend_space->get_snap());
}
}
point = (point - blend_space->get_min_space()) / (blend_space->get_max_space() - blend_space->get_min_space());
point *= s;
point.y = s.height - point.y;
points.push_back(point);
point -= (icon->get_size() / 2);
point = point.floor();
if (i == selected_point) {
blend_space_draw->draw_texture(icon_selected, point);
} else {
blend_space_draw->draw_texture(icon, point);
Vector2 gui_point = (point - icon->get_size() / 2).floor();
blend_space_draw->draw_texture(i == selected_point ? icon_selected : icon, gui_point);
if (point.x >= 0.0 && point.x <= s.width && point.y >= 0.0 && point.y <= s.height && editing_point != i) {
String name_text = show_indices ? itos(i) : String(blend_space->get_blend_point_name(i));
Vector2 text_size = font->get_string_size(name_text, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size);
float half_icon_h = icon->get_size().y / 2.0;
float above_pos_y = point.y - half_icon_h - 4 * EDSCALE;
float text_x = CLAMP(point.x - text_size.x / 2.0, 0, s.width - text_size.x);
float text_y = above_pos_y >= text_size.y ? above_pos_y : point.y + half_icon_h + font->get_ascent(font_size);
Vector2 text_pos = Vector2(text_x, text_y);
Color name_color = i == selected_point ? get_theme_color(SNAME("accent_color"), EditorStringName(Editor)) : linecolor;
blend_space_draw->draw_string(font, text_pos, name_text, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, name_color);
if (text_rects.size() <= i) {
text_rects.resize(i + 1);
}
text_rects.write[i] = Rect2(Vector2(text_pos.x, text_pos.y - font->get_ascent(font_size)), text_size);
}
}
@ -727,9 +807,14 @@ void AnimationNodeBlendSpace2DEditor::_erase_selected() {
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
if (selected_point != -1) {
updating = true;
Ref<AnimationRootNode> node = blend_space->get_blend_point_node(selected_point);
Vector2 position = blend_space->get_blend_point_position(selected_point);
String point_name = blend_space->get_blend_point_name(selected_point);
undo_redo->create_action(TTR("Remove BlendSpace2D Point"));
undo_redo->add_do_method(blend_space.ptr(), "remove_blend_point", selected_point);
undo_redo->add_undo_method(blend_space.ptr(), "add_blend_point", blend_space->get_blend_point_node(selected_point), blend_space->get_blend_point_position(selected_point), selected_point);
undo_redo->add_undo_method(blend_space.ptr(), "add_blend_point", node, position, selected_point, point_name);
//restore triangles using this point
for (int i = 0; i < blend_space->get_triangle_count(); i++) {
@ -745,11 +830,9 @@ void AnimationNodeBlendSpace2DEditor::_erase_selected() {
undo_redo->add_undo_method(this, "_update_space");
undo_redo->commit_action();
// Return selection to host BlendSpace2D node.
EditorNode::get_singleton()->push_item(blend_space.ptr(), "", true);
_set_selected_point(-1);
updating = false;
_update_tool_erase();
blend_space_draw->queue_redraw();
} else if (selected_triangle != -1) {
@ -787,17 +870,26 @@ void AnimationNodeBlendSpace2DEditor::_update_edited_point_pos() {
updating = true;
edit_x->set_value(pos.x);
edit_y->set_value(pos.y);
index_edit->set_max(blend_space->get_blend_point_count() - 1);
index_edit->set_value(selected_point);
index_edit->set_editable(blend_space->get_blend_point_count() > 1 && !read_only);
updating = false;
}
}
void AnimationNodeBlendSpace2DEditor::_update_edited_point_name() {
if (updating) {
return;
}
}
void AnimationNodeBlendSpace2DEditor::_edit_point_pos(double) {
if (updating) {
return;
}
updating = true;
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Move Node Point"));
undo_redo->create_action(TTR("Move BlendSpace2D Node Point"));
undo_redo->add_do_method(blend_space.ptr(), "set_blend_point_position", selected_point, Vector2(edit_x->get_value(), edit_y->get_value()));
undo_redo->add_undo_method(blend_space.ptr(), "set_blend_point_position", selected_point, blend_space->get_blend_point_position(selected_point));
undo_redo->add_do_method(this, "_update_space");
@ -810,6 +902,71 @@ void AnimationNodeBlendSpace2DEditor::_edit_point_pos(double) {
blend_space_draw->queue_redraw();
}
void AnimationNodeBlendSpace2DEditor::_edit_point_index(double p_index) {
if (updating || selected_point < 0 || selected_point >= blend_space->get_blend_point_count()) {
return;
}
int new_index = (int)p_index;
if (new_index < 0 || new_index >= blend_space->get_blend_point_count() || new_index == selected_point) {
return;
}
updating = true;
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Reorder BlendSpace2D Point Index"));
undo_redo->add_do_method(blend_space.ptr(), "reorder_blend_point", selected_point, new_index);
undo_redo->add_undo_method(blend_space.ptr(), "reorder_blend_point", new_index, selected_point);
undo_redo->add_do_method(this, "_update_space");
undo_redo->add_undo_method(this, "_update_space");
undo_redo->add_do_method(this, "_set_selected_point", new_index);
undo_redo->add_undo_method(this, "_set_selected_point", selected_point);
undo_redo->add_do_method(this, "_show_indices_with_cooldown");
undo_redo->add_undo_method(this, "_show_indices_with_cooldown");
undo_redo->commit_action();
updating = false;
blend_space_draw->queue_redraw();
}
void AnimationNodeBlendSpace2DEditor::_set_selected_point(int p_index) {
selected_point = p_index;
_update_tool_erase();
if (p_index != -1) {
_update_edited_point_pos();
Ref<AnimationNode> node = blend_space->get_blend_point_node(p_index);
EditorNode::get_singleton()->push_item(node.ptr(), "", true);
} else {
EditorNode::get_singleton()->push_item(blend_space.ptr(), "", true);
}
}
void AnimationNodeBlendSpace2DEditor::_edit_point_name(const String &p_name) {
if (updating || selected_point == -1 || p_name.is_empty()) {
return;
}
String old_name = blend_space->get_blend_point_name(selected_point);
if (p_name == old_name) {
return;
}
String safe_name = _get_safe_name(blend_space, p_name);
updating = true;
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Change BlendSpace2D Point Name"));
undo_redo->add_do_method(blend_space.ptr(), "set_blend_point_name", selected_point, safe_name);
undo_redo->add_undo_method(blend_space.ptr(), "set_blend_point_name", selected_point, old_name);
undo_redo->add_do_method(this, "_update_space");
undo_redo->add_undo_method(this, "_update_space");
undo_redo->add_do_method(this, "_update_edited_point_name");
undo_redo->add_undo_method(this, "_update_edited_point_name");
undo_redo->commit_action();
updating = false;
blend_space_draw->queue_redraw();
}
void AnimationNodeBlendSpace2DEditor::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_THEME_CHANGED: {
@ -864,10 +1021,40 @@ void AnimationNodeBlendSpace2DEditor::_open_editor() {
if (selected_point >= 0 && selected_point < blend_space->get_blend_point_count()) {
Ref<AnimationNode> an = blend_space->get_blend_point_node(selected_point);
ERR_FAIL_COND(an.is_null());
AnimationTreeEditor::get_singleton()->enter_editor(itos(selected_point));
AnimationTreeEditor::get_singleton()->enter_editor(blend_space->get_blend_point_name(selected_point));
}
}
void AnimationNodeBlendSpace2DEditor::_index_edit_focus_entered() {
if (index_focus_cooldown_timer->is_stopped() == false) {
index_focus_cooldown_timer->stop();
}
index_edit_has_focus = true;
show_indices = true;
blend_space_draw->queue_redraw();
}
void AnimationNodeBlendSpace2DEditor::_index_edit_focus_exited() {
index_edit_has_focus = false;
index_focus_cooldown_timer->start();
}
void AnimationNodeBlendSpace2DEditor::_index_focus_cooldown_timeout() {
if (!index_edit_has_focus) {
show_indices = false;
blend_space_draw->queue_redraw();
}
}
void AnimationNodeBlendSpace2DEditor::_show_indices_with_cooldown() {
if (index_focus_cooldown_timer->is_stopped() == false) {
index_focus_cooldown_timer->stop();
}
show_indices = true;
index_focus_cooldown_timer->start();
blend_space_draw->queue_redraw();
}
void AnimationNodeBlendSpace2DEditor::_auto_triangles_toggled() {
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Toggle Auto Triangles"));
@ -883,6 +1070,106 @@ void AnimationNodeBlendSpace2DEditor::_bind_methods() {
ClassDB::bind_method("_update_tool_erase", &AnimationNodeBlendSpace2DEditor::_update_tool_erase);
ClassDB::bind_method("_update_edited_point_pos", &AnimationNodeBlendSpace2DEditor::_update_edited_point_pos);
ClassDB::bind_method("_update_edited_point_name", &AnimationNodeBlendSpace2DEditor::_update_edited_point_name);
ClassDB::bind_method("_set_selected_point", &AnimationNodeBlendSpace2DEditor::_set_selected_point);
ClassDB::bind_method("_show_indices_with_cooldown", &AnimationNodeBlendSpace2DEditor::_show_indices_with_cooldown);
}
void AnimationNodeBlendSpace2DEditor::_start_inline_edit(int p_point) {
if (editing_point != -1 || p_point < 0 || p_point >= blend_space->get_blend_point_count()) {
return;
}
editing_point = p_point;
_set_selected_point(p_point);
inline_editor = memnew(LineEdit);
blend_space_draw->add_child(inline_editor);
inline_editor->add_theme_color_override(SceneStringName(font_color), get_theme_color(SNAME("accent_color"), EditorStringName(Editor)));
inline_editor->add_theme_color_override("font_selected_color", Color::named("white"));
inline_editor->add_theme_color_override("selection_color", get_theme_color(SNAME("accent_color"), EditorStringName(Editor)));
StyleBoxEmpty *empty_style = memnew(StyleBoxEmpty);
empty_style->set_content_margin_all(0);
inline_editor->add_theme_style_override(CoreStringName(normal), empty_style);
inline_editor->add_theme_style_override("focus", memnew(StyleBoxEmpty));
inline_editor->add_theme_style_override("read_only", memnew(StyleBoxEmpty));
inline_editor->add_theme_constant_override("minimum_character_width", 0);
inline_editor->set_flat(true);
inline_editor->set_text(blend_space->get_blend_point_name(p_point));
inline_editor->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
inline_editor->set_expand_to_text_length_enabled(true);
if (p_point < text_rects.size() && p_point < points.size()) {
Rect2 text_rect = text_rects[p_point];
inline_editor_point_x = points[p_point].x;
float editor_width = text_rect.size.x;
inline_editor->set_size(Vector2(editor_width, text_rect.size.y));
Size2 s = blend_space_draw->get_size();
float editor_x = inline_editor_point_x - editor_width / 2.0;
editor_x = CLAMP(editor_x, 0.0f, s.width - editor_width);
inline_editor->set_position(Vector2(editor_x, text_rect.position.y - 1 * EDSCALE));
}
inline_editor->connect(SceneStringName(text_changed), callable_mp(this, &AnimationNodeBlendSpace2DEditor::_inline_editor_text_changed));
inline_editor->connect(SceneStringName(text_submitted), callable_mp(this, &AnimationNodeBlendSpace2DEditor::_finish_inline_edit_with_text));
inline_editor->connect(SceneStringName(focus_exited), callable_mp(this, &AnimationNodeBlendSpace2DEditor::_finish_inline_edit));
inline_editor->grab_focus();
inline_editor->select_all();
blend_space_draw->queue_redraw();
}
void AnimationNodeBlendSpace2DEditor::_finish_inline_edit() {
if (editing_point == -1 || !inline_editor) {
return;
}
String new_name = inline_editor->get_text();
_edit_point_name(new_name);
_cancel_inline_edit();
}
void AnimationNodeBlendSpace2DEditor::_finish_inline_edit_with_text(const String &p_text) {
if (editing_point == -1 || !inline_editor) {
return;
}
_edit_point_name(p_text);
_cancel_inline_edit();
}
void AnimationNodeBlendSpace2DEditor::_cancel_inline_edit() {
if (inline_editor) {
inline_editor->queue_free();
inline_editor = nullptr;
}
editing_point = -1;
blend_space_draw->queue_redraw();
}
void AnimationNodeBlendSpace2DEditor::_inline_editor_text_changed(const String &p_text) {
if (!inline_editor) {
return;
}
inline_editor->set_size(Vector2(0, inline_editor->get_size().y));
Vector2 editor_size = inline_editor->get_size();
Size2 s = blend_space_draw->get_size();
float editor_x = inline_editor_point_x - editor_size.x / 2.0;
editor_x = CLAMP(editor_x, 0.0f, s.width - editor_size.x);
inline_editor->set_position(Vector2(editor_x, inline_editor->get_position().y));
}
AnimationNodeBlendSpace2DEditor *AnimationNodeBlendSpace2DEditor::singleton = nullptr;
@ -903,7 +1190,7 @@ AnimationNodeBlendSpace2DEditor::AnimationNodeBlendSpace2DEditor() {
tool_select->set_button_group(bg);
top_hb->add_child(tool_select);
tool_select->set_pressed(true);
tool_select->set_tooltip_text(TTR("Select and move points.\nRMB: Create point at position clicked.\nShift+LMB+Drag: Set the blending position within the space."));
tool_select->set_tooltip_text(TTR("Select and move points.\nRMB: Create point at position clicked.\nShift+LMB+Drag: Set the blending position within the space.\nScroll: Increment or decrement index."));
tool_select->connect(SceneStringName(pressed), callable_mp(this, &AnimationNodeBlendSpace2DEditor::_tool_switch).bind(0));
tool_create = memnew(Button);
@ -976,22 +1263,46 @@ AnimationNodeBlendSpace2DEditor::AnimationNodeBlendSpace2DEditor() {
top_hb->add_child(memnew(VSeparator));
top_hb->add_child(memnew(Label(TTR("Sync:"))));
top_hb->add_child(memnew(Label(TTR("Sync"))));
sync = memnew(CheckBox);
top_hb->add_child(sync);
sync->connect(SceneStringName(toggled), callable_mp(this, &AnimationNodeBlendSpace2DEditor::_config_changed));
top_hb->add_child(memnew(VSeparator));
top_hb->add_child(memnew(Label(TTR("Blend:"))));
top_hb->add_child(memnew(Label(TTR("Blend"))));
interpolation = memnew(OptionButton);
top_hb->add_child(interpolation);
interpolation->connect(SceneStringName(item_selected), callable_mp(this, &AnimationNodeBlendSpace2DEditor::_config_changed));
top_hb->add_spacer();
edit_hb = memnew(HBoxContainer);
top_hb->add_child(edit_hb);
open_editor = memnew(Button);
edit_hb->add_child(open_editor);
open_editor->set_text(TTR("Open Editor"));
open_editor->connect(SceneStringName(pressed), callable_mp(this, &AnimationNodeBlendSpace2DEditor::_open_editor), CONNECT_DEFERRED);
open_editor_sep = memnew(VSeparator);
edit_hb->add_child(open_editor_sep);
edit_hb->add_child(memnew(Label(TTR("Index"))));
index_edit = memnew(SpinBox);
edit_hb->add_child(index_edit);
index_edit->set_min(0);
index_edit->set_step(1);
index_edit->set_allow_greater(false);
index_edit->set_allow_lesser(false);
index_edit->set_accessibility_name(TTRC("Blend Point Index"));
index_edit->set_tooltip_text(TTR("Index of the blend point.\nValues outside of the valid range will be clamped to the nearest index."));
index_edit->connect(SceneStringName(value_changed), callable_mp(this, &AnimationNodeBlendSpace2DEditor::_edit_point_index));
index_edit->get_line_edit()->connect(SceneStringName(focus_entered), callable_mp(this, &AnimationNodeBlendSpace2DEditor::_index_edit_focus_entered));
index_edit->get_line_edit()->connect(SceneStringName(focus_exited), callable_mp(this, &AnimationNodeBlendSpace2DEditor::_index_edit_focus_exited));
edit_hb->add_child(memnew(VSeparator));
edit_hb->add_child(memnew(Label(TTR("Point"))));
edit_hb->add_child(memnew(Label(TTR("Position"))));
edit_x = memnew(SpinBox);
edit_hb->add_child(edit_x);
edit_x->set_min(-1000);
@ -1006,12 +1317,10 @@ AnimationNodeBlendSpace2DEditor::AnimationNodeBlendSpace2DEditor() {
edit_y->set_max(1000);
edit_y->set_accessibility_name(TTRC("Blend Y Value"));
edit_y->connect(SceneStringName(value_changed), callable_mp(this, &AnimationNodeBlendSpace2DEditor::_edit_point_pos));
open_editor = memnew(Button);
edit_hb->add_child(open_editor);
open_editor->set_text(TTR("Open Editor"));
open_editor->connect(SceneStringName(pressed), callable_mp(this, &AnimationNodeBlendSpace2DEditor::_open_editor), CONNECT_DEFERRED);
edit_hb->hide();
open_editor->hide();
open_editor_sep->hide();
HBoxContainer *main_hb = memnew(HBoxContainer);
add_child(main_hb);
@ -1119,6 +1428,13 @@ AnimationNodeBlendSpace2DEditor::AnimationNodeBlendSpace2DEditor() {
open_file->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILE);
open_file->connect("file_selected", callable_mp(this, &AnimationNodeBlendSpace2DEditor::_file_opened));
// Create timer for index focus cooldown (1.5 seconds).
index_focus_cooldown_timer = memnew(Timer);
add_child(index_focus_cooldown_timer);
index_focus_cooldown_timer->set_wait_time(1.5);
index_focus_cooldown_timer->set_one_shot(true);
index_focus_cooldown_timer->connect("timeout", callable_mp(this, &AnimationNodeBlendSpace2DEditor::_index_focus_cooldown_timeout));
selected_point = -1;
selected_triangle = -1;

View file

@ -41,6 +41,7 @@ class LineEdit;
class OptionButton;
class PanelContainer;
class SpinBox;
class Timer;
class VSeparator;
class AnimationNodeBlendSpace2DEditor : public AnimationTreeNodeEditorPlugin {
@ -75,6 +76,8 @@ class AnimationNodeBlendSpace2DEditor : public AnimationTreeNodeEditorPlugin {
SpinBox *edit_x = nullptr;
SpinBox *edit_y = nullptr;
Button *open_editor = nullptr;
VSeparator *open_editor_sep = nullptr;
SpinBox *index_edit = nullptr;
int selected_point;
int selected_triangle;
@ -109,6 +112,14 @@ class AnimationNodeBlendSpace2DEditor : public AnimationTreeNodeEditorPlugin {
Vector2 drag_from;
Vector2 drag_ofs;
Vector<Rect2> text_rects;
int editing_point = -1;
LineEdit *inline_editor = nullptr;
float inline_editor_point_x = 0.0f;
bool index_edit_has_focus = false;
bool show_indices = false;
Timer *index_focus_cooldown_timer = nullptr;
Vector<int> making_triangle;
void _add_menu_type(int p_index);
@ -116,14 +127,28 @@ class AnimationNodeBlendSpace2DEditor : public AnimationTreeNodeEditorPlugin {
void _tool_switch(int p_tool);
void _update_edited_point_pos();
void _update_edited_point_name();
void _update_tool_erase();
void _erase_selected();
void _edit_point_pos(double);
void _edit_point_name(const String &p_name);
void _edit_point_index(double p_index);
void _set_selected_point(int p_index);
void _start_inline_edit(int p_point);
void _finish_inline_edit();
void _finish_inline_edit_with_text(const String &p_text);
void _cancel_inline_edit();
void _inline_editor_text_changed(const String &p_text);
void _open_editor();
void _index_edit_focus_entered();
void _index_edit_focus_exited();
void _index_focus_cooldown_timeout();
void _show_indices_with_cooldown();
void _auto_triangles_toggled();
StringName get_blend_position_path() const;
String _get_safe_name(const Ref<AnimationNodeBlendSpace2D> &p_blend_space, const String &p_name);
EditorFileDialog *open_file = nullptr;
Ref<AnimationNode> file_loaded;
@ -143,6 +168,7 @@ protected:
public:
static AnimationNodeBlendSpace2DEditor *get_singleton() { return singleton; }
void refresh_editor() { _update_space(); }
virtual bool can_edit(const Ref<AnimationNode> &p_node) override;
virtual void edit(const Ref<AnimationNode> &p_node) override;
AnimationNodeBlendSpace2DEditor();

View file

@ -0,0 +1,6 @@
GH-110369
--------------
Validate extension JSON: Error: Field 'classes/AnimationNodeBlendSpace2D/methods/add_blend_point/arguments': size changed value in new API, from 3 to 4.
Validate extension JSON: Error: Field 'classes/AnimationNodeBlendSpace1D/methods/add_blend_point/arguments': size changed value in new API, from 3 to 4.
Added "name" parameter, to functionally replace index reference for points. Compatibility methods registered.

View file

@ -0,0 +1,49 @@
/**************************************************************************/
/* animation_blend_space_1d.compat.inc */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef DISABLE_DEPRECATED
#include "animation_blend_space_1d.h"
#include "core/object/class_db.h"
void AnimationNodeBlendSpace1D::_add_blend_point_bind_compat_110369(const Ref<AnimationRootNode> &p_node, float p_position, int p_at_index) {
int n = p_at_index == -1 ? blend_points_used : p_at_index;
while (find_blend_point_by_name(itos(n)) != -1) {
n++;
}
add_blend_point(p_node, p_position, p_at_index, StringName(itos(n)));
}
void AnimationNodeBlendSpace1D::_bind_compatibility_methods() {
ClassDB::bind_compatibility_method(D_METHOD("add_blend_point", "node", "pos", "at_index"), &AnimationNodeBlendSpace1D::_add_blend_point_bind_compat_110369, DEFVAL(-1));
}
#endif // DISABLE_DEPRECATED

View file

@ -29,6 +29,7 @@
/**************************************************************************/
#include "animation_blend_space_1d.h"
#include "animation_blend_space_1d.compat.inc"
#include "animation_blend_tree.h"
@ -54,17 +55,11 @@ Variant AnimationNodeBlendSpace1D::get_parameter_default_value(const StringName
}
Ref<AnimationNode> AnimationNodeBlendSpace1D::get_child_by_name(const StringName &p_name) const {
return get_blend_point_node(p_name.operator String().to_int());
}
void AnimationNodeBlendSpace1D::_validate_property(PropertyInfo &p_property) const {
if (p_property.name.begins_with("blend_point_")) {
String left = p_property.name.get_slicec('/', 0);
int idx = left.get_slicec('_', 2).to_int();
if (idx >= blend_points_used) {
p_property.usage = PROPERTY_USAGE_NONE;
}
int point_index = find_blend_point_by_name(p_name);
if (point_index != -1) {
return get_blend_point_node(point_index);
}
return Ref<AnimationRootNode>();
}
void AnimationNodeBlendSpace1D::_tree_changed() {
@ -80,13 +75,17 @@ void AnimationNodeBlendSpace1D::_animation_node_removed(const ObjectID &p_oid, c
}
void AnimationNodeBlendSpace1D::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_blend_point", "node", "pos", "at_index"), &AnimationNodeBlendSpace1D::add_blend_point, DEFVAL(-1));
ClassDB::bind_method(D_METHOD("add_blend_point", "node", "pos", "at_index", "name"), &AnimationNodeBlendSpace1D::add_blend_point, DEFVAL(-1), DEFVAL(StringName()));
ClassDB::bind_method(D_METHOD("set_blend_point_position", "point", "pos"), &AnimationNodeBlendSpace1D::set_blend_point_position);
ClassDB::bind_method(D_METHOD("get_blend_point_position", "point"), &AnimationNodeBlendSpace1D::get_blend_point_position);
ClassDB::bind_method(D_METHOD("set_blend_point_node", "point", "node"), &AnimationNodeBlendSpace1D::set_blend_point_node);
ClassDB::bind_method(D_METHOD("get_blend_point_node", "point"), &AnimationNodeBlendSpace1D::get_blend_point_node);
ClassDB::bind_method(D_METHOD("set_blend_point_name", "point", "name"), &AnimationNodeBlendSpace1D::set_blend_point_name);
ClassDB::bind_method(D_METHOD("get_blend_point_name", "point"), &AnimationNodeBlendSpace1D::get_blend_point_name);
ClassDB::bind_method(D_METHOD("find_blend_point_by_name", "name"), &AnimationNodeBlendSpace1D::find_blend_point_by_name);
ClassDB::bind_method(D_METHOD("remove_blend_point", "point"), &AnimationNodeBlendSpace1D::remove_blend_point);
ClassDB::bind_method(D_METHOD("get_blend_point_count"), &AnimationNodeBlendSpace1D::get_blend_point_count);
ClassDB::bind_method(D_METHOD("reorder_blend_point", "from_index", "to_index"), &AnimationNodeBlendSpace1D::reorder_blend_point);
ClassDB::bind_method(D_METHOD("set_min_space", "min_space"), &AnimationNodeBlendSpace1D::set_min_space);
ClassDB::bind_method(D_METHOD("get_min_space"), &AnimationNodeBlendSpace1D::get_min_space);
@ -106,13 +105,6 @@ void AnimationNodeBlendSpace1D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_use_sync", "enable"), &AnimationNodeBlendSpace1D::set_use_sync);
ClassDB::bind_method(D_METHOD("is_using_sync"), &AnimationNodeBlendSpace1D::is_using_sync);
ClassDB::bind_method(D_METHOD("_add_blend_point", "index", "node"), &AnimationNodeBlendSpace1D::_add_blend_point);
for (int i = 0; i < MAX_BLEND_POINTS; i++) {
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "blend_point_" + itos(i) + "/node", PROPERTY_HINT_RESOURCE_TYPE, AnimationRootNode::get_class_static(), PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_add_blend_point", "get_blend_point_node", i);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "blend_point_" + itos(i) + "/pos", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_blend_point_position", "get_blend_point_position", i);
}
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "min_space", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_min_space", "get_min_space");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_space", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_max_space", "get_max_space");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "snap", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_snap", "get_snap");
@ -128,18 +120,25 @@ void AnimationNodeBlendSpace1D::_bind_methods() {
void AnimationNodeBlendSpace1D::get_child_nodes(List<ChildNode> *r_child_nodes) {
for (int i = 0; i < blend_points_used; i++) {
ChildNode cn;
cn.name = itos(i);
cn.name = blend_points[i].name;
cn.node = blend_points[i].node;
r_child_nodes->push_back(cn);
}
}
void AnimationNodeBlendSpace1D::add_blend_point(const Ref<AnimationRootNode> &p_node, float p_position, int p_at_index) {
void AnimationNodeBlendSpace1D::add_blend_point(const Ref<AnimationRootNode> &p_node, float p_position, int p_at_index, const StringName &p_name) {
ERR_FAIL_COND(blend_points_used >= MAX_BLEND_POINTS);
ERR_FAIL_COND(p_node.is_null());
ERR_FAIL_COND(p_at_index < -1 || p_at_index > blend_points_used);
#ifndef DISABLE_DEPRECATED
if (p_name == StringName()) {
_add_blend_point_bind_compat_110369(p_node, p_position, p_at_index);
WARN_PRINT_ED("AnimationNodeBlendSpace1D::add_blend_point: No name provided, using safe index as reference. In the future, empty names will be deprecated, so explicitly passing a name is recommended.");
return;
}
#else
ERR_FAIL_COND(p_name == StringName());
#endif
if (p_at_index == -1 || p_at_index == blend_points_used) {
p_at_index = blend_points_used;
} else {
@ -150,6 +149,7 @@ void AnimationNodeBlendSpace1D::add_blend_point(const Ref<AnimationRootNode> &p_
blend_points[p_at_index].node = p_node;
blend_points[p_at_index].position = p_position;
blend_points[p_at_index].name = p_name;
blend_points[p_at_index].node->connect("tree_changed", callable_mp(this, &AnimationNodeBlendSpace1D::_tree_changed), CONNECT_REFERENCE_COUNTED);
blend_points[p_at_index].node->connect("animation_node_renamed", callable_mp(this, &AnimationNodeBlendSpace1D::_animation_node_renamed), CONNECT_REFERENCE_COUNTED);
@ -193,6 +193,32 @@ Ref<AnimationRootNode> AnimationNodeBlendSpace1D::get_blend_point_node(int p_poi
return blend_points[p_point].node;
}
void AnimationNodeBlendSpace1D::set_blend_point_name(int p_point, const StringName &p_name) {
ERR_FAIL_INDEX(p_point, blend_points_used);
String new_name = p_name;
ERR_FAIL_COND(new_name.is_empty() || new_name.contains_char('.') || new_name.contains_char('/'));
String old_name = blend_points[p_point].name;
if (new_name != old_name) {
blend_points[p_point].name = p_name;
emit_signal(SNAME("animation_node_renamed"), get_instance_id(), old_name, new_name);
}
}
StringName AnimationNodeBlendSpace1D::get_blend_point_name(int p_point) const {
ERR_FAIL_INDEX_V(p_point, blend_points_used, StringName());
return blend_points[p_point].name;
}
int AnimationNodeBlendSpace1D::find_blend_point_by_name(const StringName &p_name) const {
for (int i = 0; i < blend_points_used; i++) {
if (blend_points[i].name == p_name) {
return i;
}
}
return -1;
}
void AnimationNodeBlendSpace1D::remove_blend_point(int p_point) {
ERR_FAIL_INDEX(p_point, blend_points_used);
@ -207,6 +233,8 @@ void AnimationNodeBlendSpace1D::remove_blend_point(int p_point) {
blend_points_used--;
blend_points[blend_points_used].name = StringName();
emit_signal(SNAME("animation_node_removed"), get_instance_id(), itos(p_point));
emit_signal(SNAME("tree_changed"));
}
@ -215,6 +243,22 @@ int AnimationNodeBlendSpace1D::get_blend_point_count() const {
return blend_points_used;
}
void AnimationNodeBlendSpace1D::reorder_blend_point(int p_from_index, int p_to_index) {
ERR_FAIL_INDEX(p_from_index, blend_points_used);
ERR_FAIL_INDEX(p_to_index, blend_points_used);
if (p_from_index == p_to_index) {
return;
}
BlendPoint temp = blend_points[p_from_index];
blend_points[p_from_index] = blend_points[p_to_index];
blend_points[p_to_index] = temp;
emit_signal(SNAME("tree_changed"));
}
void AnimationNodeBlendSpace1D::set_min_space(float p_min) {
min_space = p_min;
@ -271,11 +315,62 @@ bool AnimationNodeBlendSpace1D::is_using_sync() const {
return sync;
}
void AnimationNodeBlendSpace1D::_add_blend_point(int p_index, const Ref<AnimationRootNode> &p_node) {
if (p_index == blend_points_used) {
add_blend_point(p_node, 0);
} else {
set_blend_point_node(p_index, p_node);
bool AnimationNodeBlendSpace1D::_set(const StringName &p_name, const Variant &p_value) {
String prop = p_name;
if (prop.begins_with("blend_point_")) {
int idx = prop.get_slicec('_', 2).to_int();
String what = prop.get_slicec('/', 1);
if (what == "node") {
Ref<AnimationRootNode> node = p_value;
#ifndef DISABLE_DEPRECATED
if (idx == blend_points_used) {
add_blend_point(node, 0, -1, blend_points[idx].name.is_empty() ? StringName(itos(idx)) : blend_points[idx].name);
} else {
set_blend_point_node(idx, node);
}
#else
if (idx == blend_points_used) {
add_blend_point(node, 0, -1, blend_points[idx].name);
} else {
set_blend_point_node(idx, node);
}
#endif // DISABLE_DEPRECATED
} else if (what == "pos") {
set_blend_point_position(idx, p_value);
} else if (what == "name") {
set_blend_point_name(idx, p_value);
} else {
return false;
}
return true;
}
return false;
}
bool AnimationNodeBlendSpace1D::_get(const StringName &p_name, Variant &r_ret) const {
String prop = p_name;
if (prop.begins_with("blend_point_")) {
int idx = prop.get_slicec('_', 2).to_int();
String what = prop.get_slicec('/', 1);
if (what == "node") {
r_ret = get_blend_point_node(idx);
} else if (what == "pos") {
r_ret = get_blend_point_position(idx);
} else if (what == "name") {
r_ret = get_blend_point_name(idx);
} else {
return false;
}
return true;
}
return false;
}
void AnimationNodeBlendSpace1D::_get_property_list(List<PropertyInfo> *p_list) const {
for (int i = 0; i < blend_points_used; i++) {
p_list->push_back(PropertyInfo(Variant::OBJECT, "blend_point_" + itos(i) + "/node", PROPERTY_HINT_RESOURCE_TYPE, AnimationRootNode::get_class_static(), PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
p_list->push_back(PropertyInfo(Variant::FLOAT, "blend_point_" + itos(i) + "/pos", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
p_list->push_back(PropertyInfo(Variant::STRING, "blend_point_" + itos(i) + "/name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
}
}
@ -289,7 +384,7 @@ AnimationNode::NodeTimeInfo AnimationNodeBlendSpace1D::_process(const AnimationM
if (blend_points_used == 1) {
// only one point available, just play that animation
pi.weight = 1.0;
return blend_node(blend_points[0].node, blend_points[0].name, pi, FILTER_IGNORE, true, p_test_only);
return blend_node(blend_points[0].node, get_blend_point_name(0), pi, FILTER_IGNORE, true, p_test_only);
}
double blend_pos = get_parameter(blend_position);
@ -352,7 +447,7 @@ AnimationNode::NodeTimeInfo AnimationNodeBlendSpace1D::_process(const AnimationM
for (int i = 0; i < blend_points_used; i++) {
if (i == point_lower || i == point_higher) {
pi.weight = weights[i];
NodeTimeInfo t = blend_node(blend_points[i].node, blend_points[i].name, pi, FILTER_IGNORE, true, p_test_only);
NodeTimeInfo t = blend_node(blend_points[i].node, get_blend_point_name(i), pi, FILTER_IGNORE, true, p_test_only);
if (first || pi.weight > max_weight) {
max_weight = pi.weight;
mind = t;
@ -360,7 +455,7 @@ AnimationNode::NodeTimeInfo AnimationNodeBlendSpace1D::_process(const AnimationM
}
} else if (sync) {
pi.weight = 0;
blend_node(blend_points[i].node, blend_points[i].name, pi, FILTER_IGNORE, true, p_test_only);
blend_node(blend_points[i].node, get_blend_point_name(i), pi, FILTER_IGNORE, true, p_test_only);
}
}
} else {
@ -393,16 +488,16 @@ AnimationNode::NodeTimeInfo AnimationNodeBlendSpace1D::_process(const AnimationM
// See how much animation remains.
pi.seeked = false;
pi.weight = 0;
from = blend_node(blend_points[cur_closest].node, blend_points[cur_closest].name, pi, FILTER_IGNORE, true, true);
from = blend_node(blend_points[cur_closest].node, get_blend_point_name(cur_closest), pi, FILTER_IGNORE, true, true);
pi.time = from.position;
}
pi.seeked = true;
pi.weight = 1.0;
mind = blend_node(blend_points[new_closest].node, blend_points[new_closest].name, pi, FILTER_IGNORE, true, p_test_only);
mind = blend_node(blend_points[new_closest].node, get_blend_point_name(new_closest), pi, FILTER_IGNORE, true, p_test_only);
cur_closest = new_closest;
} else {
pi.weight = 1.0;
mind = blend_node(blend_points[cur_closest].node, blend_points[cur_closest].name, pi, FILTER_IGNORE, true, p_test_only);
mind = blend_node(blend_points[cur_closest].node, get_blend_point_name(cur_closest), pi, FILTER_IGNORE, true, p_test_only);
}
if (sync) {
@ -410,7 +505,7 @@ AnimationNode::NodeTimeInfo AnimationNodeBlendSpace1D::_process(const AnimationM
pi.weight = 0;
for (int i = 0; i < blend_points_used; i++) {
if (i != cur_closest) {
blend_node(blend_points[i].node, blend_points[i].name, pi, FILTER_IGNORE, true, p_test_only);
blend_node(blend_points[i].node, get_blend_point_name(i), pi, FILTER_IGNORE, true, p_test_only);
}
}
}
@ -425,9 +520,6 @@ String AnimationNodeBlendSpace1D::get_caption() const {
}
AnimationNodeBlendSpace1D::AnimationNodeBlendSpace1D() {
for (int i = 0; i < MAX_BLEND_POINTS; i++) {
blend_points[i].name = itos(i);
}
}
AnimationNodeBlendSpace1D::~AnimationNodeBlendSpace1D() {

View file

@ -63,7 +63,9 @@ protected:
String value_label = "value";
void _add_blend_point(int p_index, const Ref<AnimationRootNode> &p_node);
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
void _get_property_list(List<PropertyInfo> *p_list) const;
StringName blend_position = "blend_position";
StringName closest = "closest";
@ -72,28 +74,37 @@ protected:
bool sync = false;
void _validate_property(PropertyInfo &p_property) const;
static void _bind_methods();
virtual void _tree_changed() override;
virtual void _animation_node_renamed(const ObjectID &p_oid, const String &p_old_name, const String &p_new_name) override;
virtual void _animation_node_removed(const ObjectID &p_oid, const StringName &p_node) override;
#ifndef DISABLE_DEPRECATED
void _add_blend_point_bind_compat_110369(const Ref<AnimationRootNode> &p_node, float p_position, int p_at_index = -1);
static void _bind_compatibility_methods();
#endif
public:
virtual void get_parameter_list(List<PropertyInfo> *r_list) const override;
virtual Variant get_parameter_default_value(const StringName &p_parameter) const override;
virtual void get_child_nodes(List<ChildNode> *r_child_nodes) override;
void add_blend_point(const Ref<AnimationRootNode> &p_node, float p_position, int p_at_index = -1);
void add_blend_point(const Ref<AnimationRootNode> &p_node, float p_position, int p_at_index = -1, const StringName &p_name = "");
void set_blend_point_position(int p_point, float p_position);
void set_blend_point_node(int p_point, const Ref<AnimationRootNode> &p_node);
float get_blend_point_position(int p_point) const;
Ref<AnimationRootNode> get_blend_point_node(int p_point) const;
void set_blend_point_name(int p_point, const StringName &p_name);
StringName get_blend_point_name(int p_point) const;
int find_blend_point_by_name(const StringName &p_name) const;
void remove_blend_point(int p_point);
int get_blend_point_count() const;
void reorder_blend_point(int p_from_index, int p_to_index);
void set_min_space(float p_min);
float get_min_space() const;

View file

@ -0,0 +1,49 @@
/**************************************************************************/
/* animation_blend_space_2d.compat.inc */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef DISABLE_DEPRECATED
#include "animation_blend_space_2d.h"
#include "core/object/class_db.h"
void AnimationNodeBlendSpace2D::_add_blend_point_bind_compat_110369(const Ref<AnimationRootNode> &p_node, const Vector2 &p_position, int p_at_index) {
int n = p_at_index == -1 ? blend_points_used : p_at_index;
while (find_blend_point_by_name(itos(n)) != -1) {
n++;
}
add_blend_point(p_node, p_position, p_at_index, StringName(itos(n)));
}
void AnimationNodeBlendSpace2D::_bind_compatibility_methods() {
ClassDB::bind_compatibility_method(D_METHOD("add_blend_point", "node", "pos", "at_index"), &AnimationNodeBlendSpace2D::_add_blend_point_bind_compat_110369, DEFVAL(-1));
}
#endif // DISABLE_DEPRECATED

View file

@ -29,6 +29,7 @@
/**************************************************************************/
#include "animation_blend_space_2d.h"
#include "animation_blend_space_2d.compat.inc"
#include "animation_blend_tree.h"
@ -58,16 +59,25 @@ Variant AnimationNodeBlendSpace2D::get_parameter_default_value(const StringName
void AnimationNodeBlendSpace2D::get_child_nodes(List<ChildNode> *r_child_nodes) {
for (int i = 0; i < blend_points_used; i++) {
ChildNode cn;
cn.name = itos(i);
cn.name = blend_points[i].name;
cn.node = blend_points[i].node;
r_child_nodes->push_back(cn);
}
}
void AnimationNodeBlendSpace2D::add_blend_point(const Ref<AnimationRootNode> &p_node, const Vector2 &p_position, int p_at_index) {
void AnimationNodeBlendSpace2D::add_blend_point(const Ref<AnimationRootNode> &p_node, const Vector2 &p_position, int p_at_index, const StringName &p_name) {
ERR_FAIL_COND(blend_points_used >= MAX_BLEND_POINTS);
ERR_FAIL_COND(p_node.is_null());
ERR_FAIL_COND(p_at_index < -1 || p_at_index > blend_points_used);
#ifndef DISABLE_DEPRECATED
if (p_name == StringName()) {
_add_blend_point_bind_compat_110369(p_node, p_position, p_at_index);
WARN_PRINT_ED("AnimationNodeBlendSpace2D::add_blend_point: No name provided, using safe index as reference. In the future, empty names will be deprecated, so explicitly passing a name is recommended.");
return;
}
#else
ERR_FAIL_COND(p_name == StringName());
#endif
if (p_at_index == -1 || p_at_index == blend_points_used) {
p_at_index = blend_points_used;
@ -85,6 +95,7 @@ void AnimationNodeBlendSpace2D::add_blend_point(const Ref<AnimationRootNode> &p_
}
blend_points[p_at_index].node = p_node;
blend_points[p_at_index].position = p_position;
blend_points[p_at_index].name = p_name;
blend_points[p_at_index].node->connect("tree_changed", callable_mp(this, &AnimationNodeBlendSpace2D::_tree_changed), CONNECT_REFERENCE_COUNTED);
blend_points[p_at_index].node->connect("animation_node_renamed", callable_mp(this, &AnimationNodeBlendSpace2D::_animation_node_renamed), CONNECT_REFERENCE_COUNTED);
@ -129,6 +140,32 @@ Ref<AnimationRootNode> AnimationNodeBlendSpace2D::get_blend_point_node(int p_poi
return blend_points[p_point].node;
}
void AnimationNodeBlendSpace2D::set_blend_point_name(int p_point, const StringName &p_name) {
ERR_FAIL_INDEX(p_point, blend_points_used);
String new_name = p_name;
ERR_FAIL_COND(new_name.is_empty() || new_name.contains_char('.') || new_name.contains_char('/'));
String old_name = blend_points[p_point].name;
if (new_name != old_name) {
blend_points[p_point].name = p_name;
emit_signal(SNAME("animation_node_renamed"), get_instance_id(), old_name, p_name);
}
}
StringName AnimationNodeBlendSpace2D::get_blend_point_name(int p_point) const {
ERR_FAIL_INDEX_V(p_point, blend_points_used, StringName());
return blend_points[p_point].name;
}
int AnimationNodeBlendSpace2D::find_blend_point_by_name(const StringName &p_name) const {
for (int i = 0; i < blend_points_used; i++) {
if (blend_points[i].name == p_name) {
return i;
}
}
return -1;
}
void AnimationNodeBlendSpace2D::remove_blend_point(int p_point) {
ERR_FAIL_INDEX(p_point, blend_points_used);
@ -159,6 +196,8 @@ void AnimationNodeBlendSpace2D::remove_blend_point(int p_point) {
}
blend_points_used--;
blend_points[blend_points_used].name = StringName();
emit_signal(SNAME("animation_node_removed"), get_instance_id(), itos(p_point));
emit_signal(SNAME("tree_changed"));
}
@ -167,6 +206,38 @@ int AnimationNodeBlendSpace2D::get_blend_point_count() const {
return blend_points_used;
}
void AnimationNodeBlendSpace2D::reorder_blend_point(int p_from_index, int p_to_index) {
ERR_FAIL_INDEX(p_from_index, blend_points_used);
ERR_FAIL_INDEX(p_to_index, blend_points_used);
if (p_from_index == p_to_index) {
return;
}
// Update triangle indices for swap operation
for (int i = 0; i < triangles.size(); i++) {
for (int j = 0; j < 3; j++) {
int point_ref = triangles[i].points[j];
if (point_ref == p_from_index) {
triangles.write[i].points[j] = p_to_index;
} else if (point_ref == p_to_index) {
triangles.write[i].points[j] = p_from_index;
}
}
}
BlendPoint temp = blend_points[p_from_index];
blend_points[p_from_index] = blend_points[p_to_index];
blend_points[p_to_index] = temp;
_queue_auto_triangles();
emit_signal(SNAME("tree_changed"));
emit_signal(SNAME("triangles_updated"));
}
bool AnimationNodeBlendSpace2D::has_triangle(int p_x, int p_y, int p_z) const {
ERR_FAIL_INDEX_V(p_x, blend_points_used, false);
ERR_FAIL_INDEX_V(p_y, blend_points_used, false);
@ -299,14 +370,6 @@ String AnimationNodeBlendSpace2D::get_y_label() const {
return y_label;
}
void AnimationNodeBlendSpace2D::_add_blend_point(int p_index, const Ref<AnimationRootNode> &p_node) {
if (p_index == blend_points_used) {
add_blend_point(p_node, Vector2());
} else {
set_blend_point_node(p_index, p_node);
}
}
void AnimationNodeBlendSpace2D::_set_triangles(const Vector<int> &p_triangles) {
if (auto_triangles) {
return;
@ -332,6 +395,65 @@ Vector<int> AnimationNodeBlendSpace2D::_get_triangles() const {
return t;
}
bool AnimationNodeBlendSpace2D::_set(const StringName &p_name, const Variant &p_value) {
String prop = p_name;
if (prop.begins_with("blend_point_")) {
int idx = prop.get_slicec('_', 2).to_int();
String what = prop.get_slicec('/', 1);
if (what == "node") {
Ref<AnimationRootNode> node = p_value;
#ifndef DISABLE_DEPRECATED
if (idx == blend_points_used) {
add_blend_point(node, Vector2(), -1, blend_points[idx].name.is_empty() ? StringName(itos(idx)) : blend_points[idx].name);
} else {
set_blend_point_node(idx, node);
}
#else
if (idx == blend_points_used) {
add_blend_point(node, Vector2(), -1, blend_points[idx].name);
} else {
set_blend_point_node(idx, node);
}
#endif // DISABLE_DEPRECATED
} else if (what == "pos") {
set_blend_point_position(idx, p_value);
} else if (what == "name") {
set_blend_point_name(idx, p_value);
} else {
return false;
}
return true;
}
return false;
}
bool AnimationNodeBlendSpace2D::_get(const StringName &p_name, Variant &r_ret) const {
String prop = p_name;
if (prop.begins_with("blend_point_")) {
int idx = prop.get_slicec('_', 2).to_int();
String what = prop.get_slicec('/', 1);
if (what == "node") {
r_ret = get_blend_point_node(idx);
} else if (what == "pos") {
r_ret = get_blend_point_position(idx);
} else if (what == "name") {
r_ret = get_blend_point_name(idx);
} else {
return false;
}
return true;
}
return false;
}
void AnimationNodeBlendSpace2D::_get_property_list(List<PropertyInfo> *p_list) const {
for (int i = 0; i < blend_points_used; i++) {
p_list->push_back(PropertyInfo(Variant::OBJECT, "blend_point_" + itos(i) + "/node", PROPERTY_HINT_RESOURCE_TYPE, AnimationRootNode::get_class_static(), PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
p_list->push_back(PropertyInfo(Variant::VECTOR2, "blend_point_" + itos(i) + "/pos", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
p_list->push_back(PropertyInfo(Variant::STRING, "blend_point_" + itos(i) + "/name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
}
}
void AnimationNodeBlendSpace2D::_queue_auto_triangles() {
if (!auto_triangles || triangles_dirty) {
return;
@ -521,7 +643,7 @@ AnimationNode::NodeTimeInfo AnimationNodeBlendSpace2D::_process(const AnimationM
if (i == triangle_points[j]) {
//blend with the given weight
pi.weight = blend_weights[j];
NodeTimeInfo t = blend_node(blend_points[i].node, blend_points[i].name, pi, FILTER_IGNORE, true, p_test_only);
NodeTimeInfo t = blend_node(blend_points[i].node, get_blend_point_name(i), pi, FILTER_IGNORE, true, p_test_only);
if (first || pi.weight > max_weight) {
mind = t;
max_weight = pi.weight;
@ -534,7 +656,7 @@ AnimationNode::NodeTimeInfo AnimationNodeBlendSpace2D::_process(const AnimationM
if (sync && !found) {
pi.weight = 0;
blend_node(blend_points[i].node, blend_points[i].name, pi, FILTER_IGNORE, true, p_test_only);
blend_node(blend_points[i].node, get_blend_point_name(i), pi, FILTER_IGNORE, true, p_test_only);
}
}
} else {
@ -567,16 +689,16 @@ AnimationNode::NodeTimeInfo AnimationNodeBlendSpace2D::_process(const AnimationM
// See how much animation remains.
pi.seeked = false;
pi.weight = 0;
from = blend_node(blend_points[cur_closest].node, blend_points[cur_closest].name, pi, FILTER_IGNORE, true, true);
from = blend_node(blend_points[cur_closest].node, get_blend_point_name(cur_closest), pi, FILTER_IGNORE, true, true);
pi.time = from.position;
}
pi.seeked = true;
pi.weight = 1.0;
mind = blend_node(blend_points[new_closest].node, blend_points[new_closest].name, pi, FILTER_IGNORE, true, p_test_only);
mind = blend_node(blend_points[new_closest].node, get_blend_point_name(new_closest), pi, FILTER_IGNORE, true, p_test_only);
cur_closest = new_closest;
} else {
pi.weight = 1.0;
mind = blend_node(blend_points[cur_closest].node, blend_points[cur_closest].name, pi, FILTER_IGNORE, true, p_test_only);
mind = blend_node(blend_points[cur_closest].node, get_blend_point_name(cur_closest), pi, FILTER_IGNORE, true, p_test_only);
}
if (sync) {
@ -584,7 +706,7 @@ AnimationNode::NodeTimeInfo AnimationNodeBlendSpace2D::_process(const AnimationM
pi.weight = 0;
for (int i = 0; i < blend_points_used; i++) {
if (i != cur_closest) {
blend_node(blend_points[i].node, blend_points[i].name, pi, FILTER_IGNORE, true, p_test_only);
blend_node(blend_points[i].node, get_blend_point_name(i), pi, FILTER_IGNORE, true, p_test_only);
}
}
}
@ -601,12 +723,6 @@ String AnimationNodeBlendSpace2D::get_caption() const {
void AnimationNodeBlendSpace2D::_validate_property(PropertyInfo &p_property) const {
if (auto_triangles && p_property.name == "triangles") {
p_property.usage = PROPERTY_USAGE_NONE;
} else if (p_property.name.begins_with("blend_point_")) {
String left = p_property.name.get_slicec('/', 0);
int idx = left.get_slicec('_', 2).to_int();
if (idx >= blend_points_used) {
p_property.usage = PROPERTY_USAGE_NONE;
}
}
}
@ -624,7 +740,11 @@ bool AnimationNodeBlendSpace2D::get_auto_triangles() const {
}
Ref<AnimationNode> AnimationNodeBlendSpace2D::get_child_by_name(const StringName &p_name) const {
return get_blend_point_node(p_name.operator String().to_int());
int point_index = find_blend_point_by_name(p_name);
if (point_index != -1) {
return get_blend_point_node(point_index);
}
return Ref<AnimationRootNode>();
}
void AnimationNodeBlendSpace2D::set_blend_mode(BlendMode p_blend_mode) {
@ -656,13 +776,17 @@ void AnimationNodeBlendSpace2D::_animation_node_removed(const ObjectID &p_oid, c
}
void AnimationNodeBlendSpace2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_blend_point", "node", "pos", "at_index"), &AnimationNodeBlendSpace2D::add_blend_point, DEFVAL(-1));
ClassDB::bind_method(D_METHOD("add_blend_point", "node", "pos", "at_index", "name"), &AnimationNodeBlendSpace2D::add_blend_point, DEFVAL(-1), DEFVAL(StringName()));
ClassDB::bind_method(D_METHOD("set_blend_point_position", "point", "pos"), &AnimationNodeBlendSpace2D::set_blend_point_position);
ClassDB::bind_method(D_METHOD("get_blend_point_position", "point"), &AnimationNodeBlendSpace2D::get_blend_point_position);
ClassDB::bind_method(D_METHOD("set_blend_point_node", "point", "node"), &AnimationNodeBlendSpace2D::set_blend_point_node);
ClassDB::bind_method(D_METHOD("get_blend_point_node", "point"), &AnimationNodeBlendSpace2D::get_blend_point_node);
ClassDB::bind_method(D_METHOD("set_blend_point_name", "point", "name"), &AnimationNodeBlendSpace2D::set_blend_point_name);
ClassDB::bind_method(D_METHOD("get_blend_point_name", "point"), &AnimationNodeBlendSpace2D::get_blend_point_name);
ClassDB::bind_method(D_METHOD("find_blend_point_by_name", "name"), &AnimationNodeBlendSpace2D::find_blend_point_by_name);
ClassDB::bind_method(D_METHOD("remove_blend_point", "point"), &AnimationNodeBlendSpace2D::remove_blend_point);
ClassDB::bind_method(D_METHOD("get_blend_point_count"), &AnimationNodeBlendSpace2D::get_blend_point_count);
ClassDB::bind_method(D_METHOD("reorder_blend_point", "from_index", "to_index"), &AnimationNodeBlendSpace2D::reorder_blend_point);
ClassDB::bind_method(D_METHOD("add_triangle", "x", "y", "z", "at_index"), &AnimationNodeBlendSpace2D::add_triangle, DEFVAL(-1));
ClassDB::bind_method(D_METHOD("get_triangle_point", "triangle", "point"), &AnimationNodeBlendSpace2D::get_triangle_point);
@ -684,8 +808,6 @@ void AnimationNodeBlendSpace2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_y_label", "text"), &AnimationNodeBlendSpace2D::set_y_label);
ClassDB::bind_method(D_METHOD("get_y_label"), &AnimationNodeBlendSpace2D::get_y_label);
ClassDB::bind_method(D_METHOD("_add_blend_point", "index", "node"), &AnimationNodeBlendSpace2D::_add_blend_point);
ClassDB::bind_method(D_METHOD("_set_triangles", "triangles"), &AnimationNodeBlendSpace2D::_set_triangles);
ClassDB::bind_method(D_METHOD("_get_triangles"), &AnimationNodeBlendSpace2D::_get_triangles);
@ -699,12 +821,6 @@ void AnimationNodeBlendSpace2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_using_sync"), &AnimationNodeBlendSpace2D::is_using_sync);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_triangles", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_auto_triangles", "get_auto_triangles");
for (int i = 0; i < MAX_BLEND_POINTS; i++) {
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "blend_point_" + itos(i) + "/node", PROPERTY_HINT_RESOURCE_TYPE, AnimationRootNode::get_class_static(), PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_add_blend_point", "get_blend_point_node", i);
ADD_PROPERTYI(PropertyInfo(Variant::VECTOR2, "blend_point_" + itos(i) + "/pos", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_blend_point_position", "get_blend_point_position", i);
}
ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "triangles", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_triangles", "_get_triangles");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "min_space", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_min_space", "get_min_space");
@ -722,9 +838,6 @@ void AnimationNodeBlendSpace2D::_bind_methods() {
}
AnimationNodeBlendSpace2D::AnimationNodeBlendSpace2D() {
for (int i = 0; i < MAX_BLEND_POINTS; i++) {
blend_points[i].name = itos(i);
}
}
AnimationNodeBlendSpace2D::~AnimationNodeBlendSpace2D() {

View file

@ -71,7 +71,10 @@ protected:
String y_label = "y";
BlendMode blend_mode = BLEND_MODE_INTERPOLATED;
void _add_blend_point(int p_index, const Ref<AnimationRootNode> &p_node);
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
void _get_property_list(List<PropertyInfo> *p_list) const;
void _set_triangles(const Vector<int> &p_triangles);
Vector<int> _get_triangles() const;
@ -92,20 +95,30 @@ protected:
virtual void _animation_node_renamed(const ObjectID &p_oid, const String &p_old_name, const String &p_new_name) override;
virtual void _animation_node_removed(const ObjectID &p_oid, const StringName &p_node) override;
#ifndef DISABLE_DEPRECATED
void _add_blend_point_bind_compat_110369(const Ref<AnimationRootNode> &p_node, const Vector2 &p_position, int p_at_index = -1);
static void _bind_compatibility_methods();
#endif
public:
virtual void get_parameter_list(List<PropertyInfo> *r_list) const override;
virtual Variant get_parameter_default_value(const StringName &p_parameter) const override;
virtual void get_child_nodes(List<ChildNode> *r_child_nodes) override;
void add_blend_point(const Ref<AnimationRootNode> &p_node, const Vector2 &p_position, int p_at_index = -1);
void add_blend_point(const Ref<AnimationRootNode> &p_node, const Vector2 &p_position, int p_at_index = -1, const StringName &p_name = StringName());
void set_blend_point_position(int p_point, const Vector2 &p_position);
void set_blend_point_node(int p_point, const Ref<AnimationRootNode> &p_node);
void set_blend_point_name(int p_point, const StringName &p_name);
StringName get_blend_point_name(int p_point) const;
int find_blend_point_by_name(const StringName &p_name) const;
Vector2 get_blend_point_position(int p_point) const;
Ref<AnimationRootNode> get_blend_point_node(int p_point) const;
void remove_blend_point(int p_point);
int get_blend_point_count() const;
void reorder_blend_point(int p_from_index, int p_to_index);
bool has_triangle(int p_x, int p_y, int p_z) const;
void add_triangle(int p_x, int p_y, int p_z, int p_at_index = -1);
int get_triangle_point(int p_triangle, int p_point);