diff --git a/doc/classes/AnimationNodeStateMachinePlayback.xml b/doc/classes/AnimationNodeStateMachinePlayback.xml index 7439a42026d..9a31dca5e55 100644 --- a/doc/classes/AnimationNodeStateMachinePlayback.xml +++ b/doc/classes/AnimationNodeStateMachinePlayback.xml @@ -100,6 +100,8 @@ Queues the requested state, using the state machine's default transition if a direct connection is not available. + + You can emulate traveling with a transition by calling [code]clear_path()[/code] beforehand. diff --git a/scene/animation/animation_node_state_machine.cpp b/scene/animation/animation_node_state_machine.cpp index 8b5aa9349b6..28727a8d7d3 100644 --- a/scene/animation/animation_node_state_machine.cpp +++ b/scene/animation/animation_node_state_machine.cpp @@ -254,22 +254,30 @@ void AnimationNodeStateMachinePlayback::_set_grouped(bool p_is_grouped) { is_grouped = p_is_grouped; } -void AnimationNodeStateMachinePlayback::travel(const StringName &p_state) { +void AnimationNodeStateMachinePlayback::travel(const StringName &p_state, bool p_reset_on_teleport) { ERR_FAIL_COND_EDMSG(is_grouped, "Grouped AnimationNodeStateMachinePlayback must be handled by parent AnimationNodeStateMachinePlayback. You need to retrieve the parent Root/Nested AnimationNodeStateMachine."); ERR_FAIL_COND_EDMSG(String(p_state).contains("/Start") || String(p_state).contains("/End"), "Grouped AnimationNodeStateMachinePlayback doesn't allow to play Start/End directly. Instead, play the prev or next state of group in the parent AnimationNodeStateMachine."); - _travel_main(p_state, false, false, true); + + reset_request_on_teleport = p_reset_on_teleport; + + _travel_main(p_state, p_reset_on_teleport, false, false); } void AnimationNodeStateMachinePlayback::queue_travel(const StringName &p_state) { ERR_FAIL_COND_EDMSG(is_grouped, "Grouped AnimationNodeStateMachinePlayback must be handled by parent AnimationNodeStateMachinePlayback. You need to retrieve the parent Root/Nested AnimationNodeStateMachine."); ERR_FAIL_COND_EDMSG(String(p_state).contains("/Start") || String(p_state).contains("/End"), "Grouped AnimationNodeStateMachinePlayback doesn't allow to play Start/End directly. Instead, play the prev or next state of group in the parent AnimationNodeStateMachine."); - _travel_main(p_state, false, false, false); + + // This might be able to be a parameter on both 'travel' + // It cannot be false in 'queue_travel' otherwise the teleport would delete the queue anyway... + request_jump_instead = true; + + _travel_main(p_state, false, false, true); } void AnimationNodeStateMachinePlayback::queue(const StringName &p_state) { ERR_FAIL_COND_EDMSG(is_grouped, "Grouped AnimationNodeStateMachinePlayback must be handled by parent AnimationNodeStateMachinePlayback. You need to retrieve the parent Root/Nested AnimationNodeStateMachine."); ERR_FAIL_COND_EDMSG(String(p_state).contains("/Start") || String(p_state).contains("/End"), "Grouped AnimationNodeStateMachinePlayback doesn't allow to play Start/End directly. Instead, play the prev or next state of group in the parent AnimationNodeStateMachine."); - _travel_main(p_state, false, true, false); + _travel_main(p_state, false, true, true); } void AnimationNodeStateMachinePlayback::start(const StringName &p_state, bool p_reset) { @@ -529,24 +537,31 @@ String AnimationNodeStateMachinePlayback::_validate_path(AnimationNodeStateMachi } bool AnimationNodeStateMachinePlayback::_make_travel_path(AnimationTree *p_tree, AnimationNodeStateMachine *p_state_machine, bool p_is_allow_transition_to_self, Vector &r_path, bool p_test_only) { - StringName travel = travel_request; + StringName travel_from; + if (retain_path_request) { + travel_from = r_path[r_path.size() - 1]; + } else { + travel_from = current; + } + + StringName travel_to = travel_request; travel_request = StringName(); if (!playing) { _start(p_state_machine); } - ERR_FAIL_COND_V(!p_state_machine->states.has(travel), false); - ERR_FAIL_COND_V(!p_state_machine->states.has(current), false); + ERR_FAIL_COND_V(!p_state_machine->states.has(travel_to), false); + ERR_FAIL_COND_V(!p_state_machine->states.has(travel_from), false); - if (current == travel) { + if (travel_from == travel_to) { return !p_is_allow_transition_to_self; } Vector new_path; - Vector2 current_pos = p_state_machine->states[current].position; - Vector2 target_pos = p_state_machine->states[travel].position; + Vector2 current_pos = p_state_machine->states[travel_from].position; + Vector2 target_pos = p_state_machine->states[travel_to].position; bool found_route = false; HashMap cost_map; @@ -559,16 +574,16 @@ bool AnimationNodeStateMachinePlayback::_make_travel_path(AnimationTree *p_tree, continue; } - if (p_state_machine->transitions[i].from == current) { + if (p_state_machine->transitions[i].from == travel_from) { open_list.push_back(i); float cost = p_state_machine->states[p_state_machine->transitions[i].to].position.distance_to(current_pos); cost *= p_state_machine->transitions[i].transition->get_priority(); AStarCost ap; - ap.prev = current; + ap.prev = travel_from; ap.distance = cost; cost_map[p_state_machine->transitions[i].to] = ap; - if (p_state_machine->transitions[i].to == travel) { // Prematurely found it! :D + if (p_state_machine->transitions[i].to == travel_to) { // Prematurely found it! :D found_route = true; break; } @@ -581,7 +596,7 @@ bool AnimationNodeStateMachinePlayback::_make_travel_path(AnimationTree *p_tree, break; // No path found. } - // Find the last cost transition. + // Find the least cost transition. List::Element *least_cost_transition = nullptr; float least_cost = 1e20; @@ -626,7 +641,7 @@ bool AnimationNodeStateMachinePlayback::_make_travel_path(AnimationTree *p_tree, open_list.push_back(i); - if (p_state_machine->transitions[i].to == travel) { + if (p_state_machine->transitions[i].to == travel_to) { found_route = true; break; } @@ -643,8 +658,8 @@ bool AnimationNodeStateMachinePlayback::_make_travel_path(AnimationTree *p_tree, // Check child grouped state machine. if (found_route) { // Make path. - StringName at = travel; - while (at != current) { + StringName at = travel_to; + while (at != travel_from) { new_path.push_back(at); at = cost_map[at].prev; } @@ -653,7 +668,7 @@ bool AnimationNodeStateMachinePlayback::_make_travel_path(AnimationTree *p_tree, // Check internal paths of child grouped state machine. // For example: // [current - End] - [Start - End] - [Start - End] - [Start - target] - String current_path = current; + String current_path = travel_from; int len = new_path.size() + 1; for (int i = 0; i < len; i++) { Ref anodesm = p_state_machine->find_node_by_path(current_path); @@ -800,8 +815,17 @@ AnimationNode::NodeTimeInfo AnimationNodeStateMachinePlayback::_process(const St path = new_path; } } else { - // Can't travel via explicit transitions, then travel directly (will use default_transition). - jump_request = true; + // Can't travel via explicit transitions, then travel directly... + if (request_jump_instead) { + // using 'default_transition'. + request_jump_instead = false; + jump_request = true; + } else { + // by teleporting immediately. + _set_current(p_state_machine, temp_travel_request); + reset_request = reset_request_on_teleport; + teleport_request = true; + } } } diff --git a/scene/animation/animation_node_state_machine.h b/scene/animation/animation_node_state_machine.h index de2cf79809b..8f218f82b57 100644 --- a/scene/animation/animation_node_state_machine.h +++ b/scene/animation/animation_node_state_machine.h @@ -287,6 +287,7 @@ class AnimationNodeStateMachinePlayback : public Resource { bool _reset_request_for_fading_from = false; bool next_request = false; bool stop_request = false; + bool request_jump_instead = false; bool teleport_request = false; bool jump_request = false; bool retain_path_request = false; @@ -330,7 +331,7 @@ protected: static void _bind_methods(); public: - void travel(const StringName &p_state); + void travel(const StringName &p_state, bool p_reset_on_teleport = true); void queue_travel(const StringName &p_state); void queue(const StringName &p_state); void start(const StringName &p_state, bool p_reset = true);