You've already forked godot
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:
@@ -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."));
|
||||
|
||||
Reference in New Issue
Block a user