1
0
mirror of https://github.com/godotengine/godot.git synced 2025-11-04 12:00:25 +00:00

Merge pull request #75759 from TokageItLab/reimplement-grouped-statemachine

Rework for nested `AnimationNodeStateMachine`
This commit is contained in:
Rémi Verschelde
2023-04-24 16:44:21 +02:00
15 changed files with 1273 additions and 1158 deletions

View File

@@ -50,6 +50,7 @@
#include "scene/gui/tree.h"
#include "scene/main/viewport.h"
#include "scene/main/window.h"
#include "scene/scene_string_names.h"
bool AnimationNodeStateMachineEditor::can_edit(const Ref<AnimationNode> &p_node) {
Ref<AnimationNodeStateMachine> ansm = p_node;
@@ -67,7 +68,6 @@ void AnimationNodeStateMachineEditor::edit(const Ref<AnimationNode> &p_node) {
selected_transition_from = StringName();
selected_transition_to = StringName();
selected_transition_index = -1;
selected_multi_transition = TransitionLine();
selected_node = StringName();
selected_nodes.clear();
_update_mode();
@@ -78,14 +78,60 @@ void AnimationNodeStateMachineEditor::edit(const Ref<AnimationNode> &p_node) {
tool_connect->set_disabled(read_only);
}
String AnimationNodeStateMachineEditor::_get_root_playback_path(String &r_node_directory) {
AnimationTree *tree = AnimationTreeEditor::get_singleton()->get_animation_tree();
Vector<String> edited_path = AnimationTreeEditor::get_singleton()->get_edited_path();
String base_path;
Vector<String> node_directory_path;
bool is_playable_anodesm_found = false;
if (edited_path.size()) {
while (!is_playable_anodesm_found) {
base_path = String("/").join(edited_path);
Ref<AnimationNodeStateMachine> anodesm = !edited_path.size() ? tree->get_tree_root() : tree->get_tree_root()->find_node_by_path(base_path);
if (!anodesm.is_valid()) {
break;
} else {
if (anodesm->get_state_machine_type() != AnimationNodeStateMachine::STATE_MACHINE_TYPE_GROUPED) {
is_playable_anodesm_found = true;
} else {
int idx = edited_path.size() - 1;
node_directory_path.push_back(edited_path[idx]);
edited_path.remove_at(idx);
}
}
}
}
if (is_playable_anodesm_found) {
// Return Root/Nested state machine playback.
node_directory_path.reverse();
r_node_directory = String("/").join(node_directory_path);
if (node_directory_path.size()) {
r_node_directory += "/";
}
base_path = !edited_path.size() ? String(SceneStringNames::get_singleton()->parameters_base_path) + "playback" : String(SceneStringNames::get_singleton()->parameters_base_path) + base_path + "/playback";
} else {
// Hmmm, we have to return Grouped state machine playback...
// It will give the user the error that Root/Nested state machine should be retrieved, that would be kind :-)
r_node_directory = String();
base_path = AnimationTreeEditor::get_singleton()->get_base_path() + "playback";
}
return base_path;
}
void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEvent> &p_event) {
AnimationTree *tree = AnimationTreeEditor::get_singleton()->get_animation_tree();
if (!tree) {
return;
}
Ref<AnimationNodeStateMachinePlayback> playback = tree->get(AnimationTreeEditor::get_singleton()->get_base_path() + "playback");
if (playback.is_null()) {
String node_directory;
Ref<AnimationNodeStateMachinePlayback> playback = tree->get(_get_root_playback_path(node_directory));
if (!playback.is_valid()) {
return;
}
@@ -99,16 +145,6 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv
}
}
// Group selected nodes on a state machine
if (tool_select->is_pressed() && k.is_valid() && k->is_pressed() && k->is_ctrl_pressed() && !k->is_shift_pressed() && k->get_keycode() == Key::G && !k->is_echo()) {
_group_selected_nodes();
}
// Ungroup state machine
if (tool_select->is_pressed() && k.is_valid() && k->is_pressed() && k->is_ctrl_pressed() && k->is_shift_pressed() && k->get_keycode() == Key::G && !k->is_echo()) {
_ungroup_selected_nodes();
}
Ref<InputEventMouseButton> mb = p_event;
// Add new node
@@ -124,17 +160,16 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv
selected_transition_from = StringName();
selected_transition_to = StringName();
selected_transition_index = -1;
selected_multi_transition = TransitionLine();
selected_node = StringName();
for (int i = node_rects.size() - 1; i >= 0; i--) { //inverse to draw order
if (node_rects[i].play.has_point(mb->get_position())) { //edit name
if (play_mode->get_selected() == 1 || !playback->is_playing()) {
//start
playback->start(node_rects[i].node_name);
// Start
playback->start(node_directory + String(node_rects[i].node_name));
} else {
//travel
playback->travel(node_rects[i].node_name);
// Travel
playback->travel(node_directory + String(node_rects[i].node_name));
}
state_machine_draw->queue_redraw();
return;
@@ -213,26 +248,11 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv
selected_transition_index = closest;
Ref<AnimationNodeStateMachineTransition> tr = state_machine->get_transition(closest);
EditorNode::get_singleton()->push_item(tr.ptr(), "", true);
if (!transition_lines[closest].multi_transitions.is_empty()) {
selected_transition_index = -1;
selected_multi_transition = transition_lines[closest];
Ref<EditorAnimationMultiTransitionEdit> multi;
multi.instantiate();
multi->add_transition(selected_transition_from, selected_transition_to, tr);
for (int i = 0; i < transition_lines[closest].multi_transitions.size(); i++) {
int index = transition_lines[closest].multi_transitions[i].transition_index;
Ref<AnimationNodeStateMachineTransition> transition = state_machine->get_transition(index);
StringName from = transition_lines[closest].multi_transitions[i].from_node;
StringName to = transition_lines[closest].multi_transitions[i].to_node;
multi->add_transition(from, to, transition);
}
EditorNode::get_singleton()->push_item(multi.ptr(), "", true);
if (!state_machine->is_transition_across_group(closest)) {
EditorNode::get_singleton()->push_item(tr.ptr(), "", true);
} else {
EditorNode::get_singleton()->push_item(tr.ptr(), "", true);
EditorNode::get_singleton()->push_item(nullptr, "", true);
}
}
@@ -296,11 +316,7 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv
EditorNode::get_singleton()->show_warning(TTR("Transition exists!"));
connecting = false;
} else {
if (anodesm.is_valid() || end_node.is_valid()) {
_open_connect_menu(mb->get_position());
} else {
_add_transition();
}
_add_transition();
}
} else {
_open_menu(mb->get_position());
@@ -481,12 +497,6 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv
String from = String(transition_lines[closest].from_node);
String to = String(transition_lines[closest].to_node);
String tooltip = from + " -> " + to;
for (int i = 0; i < transition_lines[closest].multi_transitions.size(); i++) {
from = String(transition_lines[closest].multi_transitions[i].from_node);
to = String(transition_lines[closest].multi_transitions[i].to_node);
tooltip += "\n" + from + " -> " + to;
}
state_machine_draw->set_tooltip_text(tooltip);
} else {
state_machine_draw->set_tooltip_text("");
@@ -522,224 +532,6 @@ Control::CursorShape AnimationNodeStateMachineEditor::get_cursor_shape(const Poi
return cursor_shape;
}
void AnimationNodeStateMachineEditor::_group_selected_nodes() {
if (!selected_nodes.is_empty()) {
if (selected_nodes.size() == 1 && (*selected_nodes.begin() == state_machine->start_node || *selected_nodes.begin() == state_machine->end_node))
return;
Ref<AnimationNodeStateMachine> group_sm = memnew(AnimationNodeStateMachine);
Vector2 group_position;
Vector<NodeUR> nodes_ur;
Vector<TransitionUR> transitions_ur;
int base = 1;
String base_name = group_sm->get_caption();
String group_name = base_name;
while (state_machine->has_node(group_name) && !selected_nodes.has(group_name)) {
base++;
group_name = base_name + " " + itos(base);
}
updating = true;
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action("Group");
// Move selected nodes to the new state machine
for (const StringName &E : selected_nodes) {
if (!state_machine->can_edit_node(E)) {
continue;
}
Ref<AnimationNode> node = state_machine->get_node(E);
Vector2 node_position = state_machine->get_node_position(E);
group_position += node_position;
NodeUR new_node;
new_node.name = E;
new_node.node = node;
new_node.position = node_position;
nodes_ur.push_back(new_node);
}
// Add the transitions to the new state machine
for (int i = 0; i < state_machine->get_transition_count(); i++) {
String from = state_machine->get_transition_from(i);
String to = state_machine->get_transition_to(i);
String local_from = from.get_slicec('/', 0);
String local_to = to.get_slicec('/', 0);
String old_from = from;
String old_to = to;
bool from_selected = false;
bool to_selected = false;
if (selected_nodes.has(local_from) && local_from != state_machine->start_node) {
from_selected = true;
}
if (selected_nodes.has(local_to) && local_to != state_machine->end_node) {
to_selected = true;
}
if (!from_selected && !to_selected) {
continue;
}
Ref<AnimationNodeStateMachineTransition> tr = state_machine->get_transition(i);
if (!from_selected) {
from = "../" + old_from;
}
if (!to_selected) {
to = "../" + old_to;
}
TransitionUR new_tr;
new_tr.new_from = from;
new_tr.new_to = to;
new_tr.old_from = old_from;
new_tr.old_to = old_to;
new_tr.transition = tr;
transitions_ur.push_back(new_tr);
}
for (int i = 0; i < nodes_ur.size(); i++) {
undo_redo->add_do_method(state_machine.ptr(), "remove_node", nodes_ur[i].name);
undo_redo->add_undo_method(group_sm.ptr(), "remove_node", nodes_ur[i].name);
}
undo_redo->add_do_method(state_machine.ptr(), "add_node", group_name, group_sm, group_position / nodes_ur.size());
undo_redo->add_undo_method(state_machine.ptr(), "remove_node", group_name);
for (int i = 0; i < nodes_ur.size(); i++) {
undo_redo->add_do_method(group_sm.ptr(), "add_node", nodes_ur[i].name, nodes_ur[i].node, nodes_ur[i].position);
undo_redo->add_undo_method(state_machine.ptr(), "add_node", nodes_ur[i].name, nodes_ur[i].node, nodes_ur[i].position);
}
for (int i = 0; i < transitions_ur.size(); i++) {
undo_redo->add_do_method(group_sm.ptr(), "add_transition", transitions_ur[i].new_from, transitions_ur[i].new_to, transitions_ur[i].transition);
undo_redo->add_undo_method(state_machine.ptr(), "add_transition", transitions_ur[i].old_from, transitions_ur[i].old_to, transitions_ur[i].transition);
}
undo_redo->add_do_method(this, "_update_graph");
undo_redo->add_undo_method(this, "_update_graph");
undo_redo->commit_action();
updating = false;
selected_nodes.clear();
selected_nodes.insert(group_name);
state_machine_draw->queue_redraw();
accept_event();
_update_mode();
}
}
void AnimationNodeStateMachineEditor::_ungroup_selected_nodes() {
bool find = false;
HashSet<StringName> new_selected_nodes;
for (const StringName &E : selected_nodes) {
Ref<AnimationNodeStateMachine> group_sm = state_machine->get_node(E);
if (group_sm.is_valid()) {
find = true;
Vector2 group_position = state_machine->get_node_position(E);
StringName group_name = E;
List<AnimationNode::ChildNode> nodes;
group_sm->get_child_nodes(&nodes);
Vector<NodeUR> nodes_ur;
Vector<TransitionUR> transitions_ur;
updating = true;
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action("Ungroup");
// Move all child nodes to current state machine
for (int i = 0; i < nodes.size(); i++) {
if (!group_sm->can_edit_node(nodes[i].name)) {
continue;
}
Vector2 node_position = group_sm->get_node_position(nodes[i].name);
NodeUR new_node;
new_node.name = nodes[i].name;
new_node.position = node_position;
new_node.node = nodes[i].node;
nodes_ur.push_back(new_node);
}
for (int i = 0; i < group_sm->get_transition_count(); i++) {
String from = group_sm->get_transition_from(i);
String to = group_sm->get_transition_to(i);
Ref<AnimationNodeStateMachineTransition> tr = group_sm->get_transition(i);
TransitionUR new_tr;
new_tr.new_from = from.replace_first("../", "");
new_tr.new_to = to.replace_first("../", "");
new_tr.old_from = from;
new_tr.old_to = to;
new_tr.transition = tr;
transitions_ur.push_back(new_tr);
}
for (int i = 0; i < nodes_ur.size(); i++) {
undo_redo->add_do_method(group_sm.ptr(), "remove_node", nodes_ur[i].name);
undo_redo->add_undo_method(state_machine.ptr(), "remove_node", nodes_ur[i].name);
}
undo_redo->add_do_method(state_machine.ptr(), "remove_node", group_name);
undo_redo->add_undo_method(state_machine.ptr(), "add_node", group_name, group_sm, group_position);
for (int i = 0; i < nodes_ur.size(); i++) {
new_selected_nodes.insert(nodes_ur[i].name);
undo_redo->add_do_method(state_machine.ptr(), "add_node", nodes_ur[i].name, nodes_ur[i].node, nodes_ur[i].position);
undo_redo->add_undo_method(group_sm.ptr(), "add_node", nodes_ur[i].name, nodes_ur[i].node, nodes_ur[i].position);
}
for (int i = 0; i < transitions_ur.size(); i++) {
if (transitions_ur[i].old_from != state_machine->start_node && transitions_ur[i].old_to != state_machine->end_node) {
undo_redo->add_do_method(state_machine.ptr(), "add_transition", transitions_ur[i].new_from, transitions_ur[i].new_to, transitions_ur[i].transition);
}
undo_redo->add_undo_method(group_sm.ptr(), "add_transition", transitions_ur[i].old_from, transitions_ur[i].old_to, transitions_ur[i].transition);
}
for (int i = 0; i < state_machine->get_transition_count(); i++) {
String from = state_machine->get_transition_from(i);
String to = state_machine->get_transition_to(i);
Ref<AnimationNodeStateMachineTransition> tr = state_machine->get_transition(i);
if (from == group_name || to == group_name) {
undo_redo->add_undo_method(state_machine.ptr(), "add_transition", from, to, tr);
}
}
undo_redo->add_do_method(this, "_update_graph");
undo_redo->add_undo_method(this, "_update_graph");
undo_redo->commit_action();
updating = false;
}
}
if (find) {
selected_nodes = new_selected_nodes;
selected_node = StringName();
state_machine_draw->queue_redraw();
accept_event();
_update_mode();
}
}
void AnimationNodeStateMachineEditor::_open_menu(const Vector2 &p_position) {
AnimationTree *tree = AnimationTreeEditor::get_singleton()->get_animation_tree();
if (!tree) {
@@ -790,83 +582,8 @@ void AnimationNodeStateMachineEditor::_open_menu(const Vector2 &p_position) {
add_node_pos = p_position / EDSCALE + state_machine->get_graph_offset();
}
void AnimationNodeStateMachineEditor::_open_connect_menu(const Vector2 &p_position) {
ERR_FAIL_COND(connecting_to_node == StringName());
Ref<AnimationNode> node = state_machine->get_node(connecting_to_node);
Ref<AnimationNodeStateMachine> anodesm = node;
Ref<AnimationNodeEndState> end_node = node;
ERR_FAIL_COND(!anodesm.is_valid() && !end_node.is_valid());
connect_menu->clear();
state_machine_menu->clear();
end_menu->clear();
nodes_to_connect.clear();
for (int i = connect_menu->get_child_count() - 1; i >= 0; i--) {
Node *child = connect_menu->get_child(i);
if (child->is_class("PopupMenu")) {
connect_menu->remove_child(child);
}
}
connect_menu->reset_size();
state_machine_menu->reset_size();
end_menu->reset_size();
if (anodesm.is_valid()) {
_create_submenu(connect_menu, anodesm, connecting_to_node, connecting_to_node);
} else {
_create_submenu(connect_menu, state_machine, connecting_to_node, connecting_to_node, true);
}
connect_menu->add_submenu_item(TTR("To") + " Animation", connecting_to_node);
if (state_machine_menu->get_item_count() > 0 || !end_node.is_valid()) {
connect_menu->add_submenu_item(TTR("To") + " StateMachine", "state_machines");
connect_menu->add_child(state_machine_menu);
}
if (end_node.is_valid()) {
connect_menu->add_submenu_item(TTR("To") + " End", "end_nodes");
connect_menu->add_child(end_menu);
} else {
state_machine_menu->add_item(connecting_to_node, nodes_to_connect.size());
}
nodes_to_connect.push_back(connecting_to_node);
if (nodes_to_connect.size() == 1) {
_add_transition();
return;
}
connect_menu->set_position(state_machine_draw->get_screen_transform().xform(p_position));
connect_menu->popup();
}
bool AnimationNodeStateMachineEditor::_create_submenu(PopupMenu *p_menu, Ref<AnimationNodeStateMachine> p_nodesm, const StringName &p_name, const StringName &p_path, bool from_root, Vector<Ref<AnimationNodeStateMachine>> p_parents) {
bool AnimationNodeStateMachineEditor::_create_submenu(PopupMenu *p_menu, Ref<AnimationNodeStateMachine> p_nodesm, const StringName &p_name, const StringName &p_path) {
String prev_path;
Vector<Ref<AnimationNodeStateMachine>> parents = p_parents;
if (from_root && p_nodesm->get_prev_state_machine() == nullptr) {
return false;
}
if (from_root) {
AnimationNodeStateMachine *prev = p_nodesm->get_prev_state_machine();
while (prev != nullptr) {
parents.push_back(prev);
p_nodesm = Ref<AnimationNodeStateMachine>(prev);
prev_path += "../";
prev = prev->get_prev_state_machine();
}
end_menu->add_item("Root", nodes_to_connect.size());
nodes_to_connect.push_back(prev_path + state_machine->end_node);
prev_path.remove_at(prev_path.size() - 1);
}
List<StringName> nodes;
p_nodesm->get_node_list(&nodes);
@@ -881,12 +598,7 @@ bool AnimationNodeStateMachineEditor::_create_submenu(PopupMenu *p_menu, Ref<Ani
if (p_nodesm->can_edit_node(E)) {
Ref<AnimationNodeStateMachine> ansm = p_nodesm->get_node(E);
String path;
if (from_root) {
path = prev_path + "/" + E;
} else {
path = String(p_path) + "/" + E;
}
String path = String(p_path) + "/" + E;
if (ansm == state_machine) {
end_menu->add_item(E, nodes_to_connect.size());
@@ -895,25 +607,10 @@ bool AnimationNodeStateMachineEditor::_create_submenu(PopupMenu *p_menu, Ref<Ani
}
if (ansm.is_valid()) {
bool parent_found = false;
state_machine_menu->add_item(E, nodes_to_connect.size());
nodes_to_connect.push_back(path);
for (int i = 0; i < parents.size(); i++) {
if (parents[i] == ansm) {
path = path.replace_first("/../" + E, "");
parent_found = true;
break;
}
}
if (parent_found) {
end_menu->add_item(E, nodes_to_connect.size());
nodes_to_connect.push_back(path + "/" + state_machine->end_node);
} else {
state_machine_menu->add_item(E, nodes_to_connect.size());
nodes_to_connect.push_back(path);
}
if (_create_submenu(nodes_menu, ansm, E, path, false, parents)) {
if (_create_submenu(nodes_menu, ansm, E, path)) {
nodes_menu->add_submenu_item(E, E);
node_added = true;
}
@@ -939,7 +636,6 @@ void AnimationNodeStateMachineEditor::_delete_selected() {
while (item) {
if (!updating) {
updating = true;
selected_multi_transition = TransitionLine();
undo_redo->create_action("Transition(s) Removed");
}
@@ -959,18 +655,10 @@ void AnimationNodeStateMachineEditor::_delete_selected() {
}
void AnimationNodeStateMachineEditor::_delete_all() {
Vector<TransitionLine> multi_transitions = selected_multi_transition.multi_transitions;
selected_multi_transition = TransitionLine();
updating = true;
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action("Transition(s) Removed");
_erase_selected(true);
for (int i = 0; i < multi_transitions.size(); i++) {
selected_transition_from = multi_transitions[i].from_node;
selected_transition_to = multi_transitions[i].to_node;
_erase_selected(true);
}
undo_redo->commit_action();
updating = false;
@@ -1120,14 +808,19 @@ void AnimationNodeStateMachineEditor::_add_transition(const bool p_nested_action
selected_transition_to = connecting_to_node;
selected_transition_index = transition_lines.size();
EditorNode::get_singleton()->push_item(tr.ptr(), "", true);
if (!state_machine->is_transition_across_group(selected_transition_index)) {
EditorNode::get_singleton()->push_item(tr.ptr(), "", true);
} else {
EditorNode::get_singleton()->push_item(tr.ptr(), "", true);
EditorNode::get_singleton()->push_item(nullptr, "", true);
}
_update_mode();
}
connecting = false;
}
void AnimationNodeStateMachineEditor::_connection_draw(const Vector2 &p_from, const Vector2 &p_to, AnimationNodeStateMachineTransition::SwitchMode p_mode, bool p_enabled, bool p_selected, bool p_travel, float p_fade_ratio, bool p_auto_advance, bool p_multi_transitions) {
void AnimationNodeStateMachineEditor::_connection_draw(const Vector2 &p_from, const Vector2 &p_to, AnimationNodeStateMachineTransition::SwitchMode p_mode, bool p_enabled, bool p_selected, bool p_travel, float p_fade_ratio, bool p_auto_advance, bool p_is_across_group) {
Color linecolor = get_theme_color(SNAME("font_color"), SNAME("Label"));
Color icon_color(1, 1, 1);
Color accent = get_theme_color(SNAME("accent_color"), SNAME("Editor"));
@@ -1173,11 +866,7 @@ void AnimationNodeStateMachineEditor::_connection_draw(const Vector2 &p_from, co
xf.columns[2] = (p_from + p_to) * 0.5 - xf.columns[1] * icon->get_height() * 0.5 - xf.columns[0] * icon->get_height() * 0.5;
state_machine_draw->draw_set_transform_matrix(xf);
if (p_multi_transitions) {
state_machine_draw->draw_texture(icons[0], Vector2(-icon->get_width(), 0), icon_color);
state_machine_draw->draw_texture(icons[0], Vector2(), icon_color);
state_machine_draw->draw_texture(icons[0], Vector2(icon->get_width(), 0), icon_color);
} else {
if (!p_is_across_group) {
state_machine_draw->draw_texture(icon, Vector2(), icon_color);
}
state_machine_draw->draw_set_transform_matrix(Transform2D());
@@ -1344,17 +1033,14 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() {
for (int i = 0; i < state_machine->get_transition_count(); i++) {
TransitionLine tl;
tl.transition_index = i;
tl.from_node = state_machine->get_transition_from(i);
StringName local_from = String(tl.from_node).get_slicec('/', 0);
local_from = local_from == ".." ? state_machine->start_node : local_from;
Vector2 ofs_from = (dragging_selected && selected_nodes.has(local_from)) ? drag_ofs : Vector2();
tl.from = (state_machine->get_node_position(local_from) * EDSCALE) + ofs_from - state_machine->get_graph_offset() * EDSCALE;
Vector2 ofs_from = (dragging_selected && selected_nodes.has(tl.from_node)) ? drag_ofs : Vector2();
tl.from = (state_machine->get_node_position(tl.from_node) * EDSCALE) + ofs_from - state_machine->get_graph_offset() * EDSCALE;
tl.to_node = state_machine->get_transition_to(i);
StringName local_to = String(tl.to_node).get_slicec('/', 0);
local_to = local_to == ".." ? state_machine->end_node : local_to;
Vector2 ofs_to = (dragging_selected && selected_nodes.has(local_to)) ? drag_ofs : Vector2();
tl.to = (state_machine->get_node_position(local_to) * EDSCALE) + ofs_to - state_machine->get_graph_offset() * EDSCALE;
Vector2 ofs_to = (dragging_selected && selected_nodes.has(tl.to_node)) ? drag_ofs : Vector2();
tl.to = (state_machine->get_node_position(tl.to_node) * EDSCALE) + ofs_to - state_machine->get_graph_offset() * EDSCALE;
Ref<AnimationNodeStateMachineTransition> tr = state_machine->get_transition(i);
tl.disabled = bool(tr->get_advance_mode() == AnimationNodeStateMachineTransition::ADVANCE_MODE_DISABLED);
@@ -1366,35 +1052,36 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() {
tl.travel = false;
tl.fade_ratio = 0.0;
tl.hidden = false;
tl.is_across_group = state_machine->is_transition_across_group(i);
if (state_machine->has_local_transition(local_to, local_from)) { //offset if same exists
if (state_machine->has_transition(tl.to_node, tl.from_node)) { //offset if same exists
Vector2 offset = -(tl.from - tl.to).normalized().orthogonal() * tr_bidi_offset;
tl.from += offset;
tl.to += offset;
}
for (int j = 0; j < node_rects.size(); j++) {
if (node_rects[j].node_name == local_from) {
if (node_rects[j].node_name == tl.from_node) {
_clip_src_line_to_rect(tl.from, tl.to, node_rects[j].node);
}
if (node_rects[j].node_name == local_to) {
if (node_rects[j].node_name == tl.to_node) {
_clip_dst_line_to_rect(tl.from, tl.to, node_rects[j].node);
}
}
tl.selected = selected_transition_from == tl.from_node && selected_transition_to == tl.to_node;
if (blend_from == local_from && current == local_to) {
if (blend_from == tl.from_node && current == tl.to_node) {
tl.travel = true;
tl.fade_ratio = MIN(1.0, fading_pos / fading_time);
}
if (travel_path.size()) {
if (current == local_from && travel_path[0] == local_to) {
if (current == tl.from_node && travel_path[0] == tl.to_node) {
tl.travel = true;
} else {
for (int j = 0; j < travel_path.size() - 1; j++) {
if (travel_path[j] == local_from && travel_path[j + 1] == local_to) {
if (travel_path[j] == tl.from_node && travel_path[j + 1] == tl.to_node) {
tl.travel = true;
break;
}
@@ -1408,17 +1095,11 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() {
tl.auto_advance = true;
}
// check if already have this local transition
// check if already have this transition
for (int j = 0; j < transition_lines.size(); j++) {
StringName from = String(transition_lines[j].from_node).get_slicec('/', 0);
StringName to = String(transition_lines[j].to_node).get_slicec('/', 0);
from = from == ".." ? state_machine->start_node : from;
to = to == ".." ? state_machine->end_node : to;
if (from == local_from && to == local_to) {
if (transition_lines[j].from_node == tl.from_node && transition_lines[j].to_node == tl.to_node) {
tl.hidden = true;
transition_lines.write[j].disabled = transition_lines[j].disabled && tl.disabled;
transition_lines.write[j].multi_transitions.push_back(tl);
}
}
transition_lines.push_back(tl);
@@ -1427,7 +1108,7 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() {
for (int i = 0; i < transition_lines.size(); i++) {
TransitionLine tl = transition_lines[i];
if (!tl.hidden) {
_connection_draw(tl.from, tl.to, tl.mode, !tl.disabled, tl.selected, tl.travel, tl.fade_ratio, tl.auto_advance, !tl.multi_transitions.is_empty());
_connection_draw(tl.from, tl.to, tl.mode, !tl.disabled, tl.selected, tl.travel, tl.fade_ratio, tl.auto_advance, tl.is_across_group);
}
}
@@ -1481,6 +1162,7 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() {
state_machine_draw->draw_string(font, nr.name.position + Vector2(0, font->get_ascent(font_size)), name, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, font_color);
offset.x += strsize + sep;
nr.can_edit = needs_editor;
if (needs_editor) {
nr.edit.position = offset + Vector2(0, (h - edit->get_height()) / 2).floor();
nr.edit.size = edit->get_size();
@@ -1546,6 +1228,10 @@ void AnimationNodeStateMachineEditor::_state_machine_pos_draw_individual(String
const NodeRect &nr = node_rects[idx];
if (nr.can_edit) {
return; // It is not AnimationNodeAnimation.
}
Vector2 from;
from.x = nr.play.position.x;
from.y = (nr.play.position.y + nr.play.size.y + nr.node.position.y + nr.node.size.y) * 0.5;
@@ -1584,14 +1270,14 @@ void AnimationNodeStateMachineEditor::_state_machine_pos_draw_all() {
{
float len = MAX(0.0001, current_length);
float pos = CLAMP(current_play_pos, 0, len);
float c = pos / len;
float c = current_length == HUGE_LENGTH ? 1 : (pos / len);
_state_machine_pos_draw_individual(playback->get_current_node(), c);
}
{
float len = MAX(0.0001, fade_from_length);
float pos = CLAMP(fade_from_current_play_pos, 0, len);
float c = pos / len;
float c = fade_from_length == HUGE_LENGTH ? 1 : (pos / len);
_state_machine_pos_draw_individual(playback->get_fading_from_node(), c);
}
}
@@ -1630,8 +1316,6 @@ void AnimationNodeStateMachineEditor::_notification(int p_what) {
auto_advance->set_icon(get_theme_icon(SNAME("AutoPlay"), SNAME("EditorIcons")));
tool_erase->set_icon(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")));
tool_group->set_icon(get_theme_icon(SNAME("Group"), SNAME("EditorIcons")));
tool_ungroup->set_icon(get_theme_icon(SNAME("Ungroup"), SNAME("EditorIcons")));
play_mode->clear();
play_mode->add_icon_item(get_theme_icon(SNAME("PlayTravel"), SNAME("EditorIcons")), TTR("Travel"));
@@ -1655,10 +1339,6 @@ void AnimationNodeStateMachineEditor::_notification(int p_what) {
error = TTR("AnimationTree is inactive.\nActivate to enable playback, check node warnings if activation fails.");
} else if (tree->is_state_invalid()) {
error = tree->get_invalid_state_reason();
/*} else if (state_machine->get_parent().is_valid() && state_machine->get_parent()->is_class("AnimationNodeStateMachine")) {
if (state_machine->get_start_node() == StringName() || state_machine->get_end_node() == StringName()) {
error = TTR("Start and end nodes are needed for a sub-transition.");
}*/
} else if (playback.is_null()) {
error = vformat(TTR("No playback resource set at path: %s."), AnimationTreeEditor::get_singleton()->get_base_path() + "playback");
}
@@ -1780,8 +1460,12 @@ void AnimationNodeStateMachineEditor::_notification(int p_what) {
while (anodesm.is_valid()) {
current_node_playback = tree->get(AnimationTreeEditor::get_singleton()->get_base_path() + next + "/playback");
next += "/" + current_node_playback->get_current_node();
anodesm = anodesm->get_node(current_node_playback->get_current_node());
StringName cnode = current_node_playback->get_current_node();
next += "/" + cnode;
if (!anodesm->has_node(cnode)) {
break;
}
anodesm = anodesm->get_node(cnode);
}
// when current_node is a state machine, use playback of current_node to set play_pos
@@ -1883,10 +1567,7 @@ void AnimationNodeStateMachineEditor::_erase_selected(const bool p_nested_action
for (int j = 0; j < state_machine->get_transition_count(); j++) {
String from = state_machine->get_transition_from(j);
String to = state_machine->get_transition_to(j);
String local_from = from.get_slicec('/', 0);
String local_to = to.get_slicec('/', 0);
if (local_from == node_rects[i].node_name || local_to == node_rects[i].node_name) {
if (from == node_rects[i].node_name || to == node_rects[i].node_name) {
undo_redo->add_undo_method(state_machine.ptr(), "add_transition", from, to, state_machine->get_transition(j));
}
}
@@ -1903,30 +1584,6 @@ void AnimationNodeStateMachineEditor::_erase_selected(const bool p_nested_action
selected_nodes.clear();
}
if (!selected_multi_transition.multi_transitions.is_empty()) {
delete_tree->clear();
TreeItem *root = delete_tree->create_item();
TreeItem *item = delete_tree->create_item(root);
item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
item->set_text(0, String(selected_transition_from) + " -> " + selected_transition_to);
item->set_editable(0, true);
for (int i = 0; i < selected_multi_transition.multi_transitions.size(); i++) {
String from = selected_multi_transition.multi_transitions[i].from_node;
String to = selected_multi_transition.multi_transitions[i].to_node;
item = delete_tree->create_item(root);
item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
item->set_text(0, from + " -> " + to);
item->set_editable(0, true);
}
delete_window->popup_centered(Vector2(400, 200));
return;
}
if (selected_transition_to != StringName() && selected_transition_from != StringName() && state_machine->has_transition(selected_transition_from, selected_transition_to)) {
Ref<AnimationNodeStateMachineTransition> tr = state_machine->get_transition(state_machine->find_transition(selected_transition_from, selected_transition_to));
if (!p_nested_action) {
@@ -1945,7 +1602,6 @@ void AnimationNodeStateMachineEditor::_erase_selected(const bool p_nested_action
selected_transition_from = StringName();
selected_transition_to = StringName();
selected_transition_index = -1;
selected_multi_transition = TransitionLine();
}
state_machine_draw->queue_redraw();
@@ -1957,24 +1613,6 @@ void AnimationNodeStateMachineEditor::_update_mode() {
bool nothing_selected = selected_nodes.is_empty() && selected_transition_from == StringName() && selected_transition_to == StringName();
bool start_end_selected = selected_nodes.size() == 1 && (*selected_nodes.begin() == state_machine->start_node || *selected_nodes.begin() == state_machine->end_node);
tool_erase->set_disabled(nothing_selected || start_end_selected || read_only);
if (selected_nodes.is_empty() || start_end_selected || read_only) {
tool_group->set_disabled(true);
tool_group->set_visible(true);
tool_ungroup->set_visible(false);
} else {
Ref<AnimationNodeStateMachine> ansm = state_machine->get_node(*selected_nodes.begin());
if (selected_nodes.size() == 1 && ansm.is_valid()) {
tool_group->set_disabled(true);
tool_group->set_visible(false);
tool_ungroup->set_visible(true);
} else {
tool_group->set_disabled(false);
tool_group->set_visible(true);
tool_ungroup->set_visible(false);
}
}
} else {
selection_tools_hb->hide();
}
@@ -2037,20 +1675,6 @@ AnimationNodeStateMachineEditor::AnimationNodeStateMachineEditor() {
top_hb->add_child(selection_tools_hb);
selection_tools_hb->add_child(memnew(VSeparator));
tool_group = memnew(Button);
tool_group->set_flat(true);
tool_group->set_tooltip_text(TTR("Group Selected Node(s)") + " (Ctrl+G)");
tool_group->connect("pressed", callable_mp(this, &AnimationNodeStateMachineEditor::_group_selected_nodes));
tool_group->set_disabled(true);
selection_tools_hb->add_child(tool_group);
tool_ungroup = memnew(Button);
tool_ungroup->set_flat(true);
tool_ungroup->set_tooltip_text(TTR("Ungroup Selected Node") + " (Ctrl+Shift+G)");
tool_ungroup->connect("pressed", callable_mp(this, &AnimationNodeStateMachineEditor::_ungroup_selected_nodes));
tool_ungroup->set_visible(false);
selection_tools_hb->add_child(tool_ungroup);
tool_erase = memnew(Button);
tool_erase->set_flat(true);
tool_erase->set_tooltip_text(TTR("Remove selected node or transition."));