1
0
Fork 0
This commit is contained in:
Iceball457 2025-02-28 01:36:23 +01:00 committed by GitHub
commit 4cbef8fafe
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 168 additions and 61 deletions

View File

@ -165,6 +165,9 @@
<member name="allow_transition_to_self" type="bool" setter="set_allow_transition_to_self" getter="is_allow_transition_to_self" default="false"> <member name="allow_transition_to_self" type="bool" setter="set_allow_transition_to_self" getter="is_allow_transition_to_self" default="false">
If [code]true[/code], allows teleport to the self state with [method AnimationNodeStateMachinePlayback.travel]. When the reset option is enabled in [method AnimationNodeStateMachinePlayback.travel], the animation is restarted. If [code]false[/code], nothing happens on the teleportation to the self state. If [code]true[/code], allows teleport to the self state with [method AnimationNodeStateMachinePlayback.travel]. When the reset option is enabled in [method AnimationNodeStateMachinePlayback.travel], the animation is restarted. If [code]false[/code], nothing happens on the teleportation to the self state.
</member> </member>
<member name="default_transition" type="AnimationNodeStateMachineTransition" setter="set_default_transition" getter="get_default_transition">
Transition used whenever the state machine teleports.
</member>
<member name="reset_ends" type="bool" setter="set_reset_ends" getter="are_ends_reset" default="false"> <member name="reset_ends" type="bool" setter="set_reset_ends" getter="are_ends_reset" default="false">
If [code]true[/code], treat the cross-fade to the start and end nodes as a blend with the RESET animation. If [code]true[/code], treat the cross-fade to the start and end nodes as a blend with the RESET animation.
In most cases, when additional cross-fades are performed in the parent [AnimationNode] of the state machine, setting this property to [code]false[/code] and matching the cross-fade time of the parent [AnimationNode] and the state machine's start node and end node gives good results. In most cases, when additional cross-fades are performed in the parent [AnimationNode] of the state machine, setting this property to [code]false[/code] and matching the cross-fade time of the parent [AnimationNode] and the state machine's start node and end node gives good results.

View File

@ -82,11 +82,26 @@
<method name="travel"> <method name="travel">
<return type="void" /> <return type="void" />
<param index="0" name="to_node" type="StringName" /> <param index="0" name="to_node" type="StringName" />
<param index="1" name="reset_on_teleport" type="bool" default="true" />
<description> <description>
Transitions from the current state to another one, following the shortest path. Transitions from the current state to another one, following the shortest path.
If the path does not connect from the current state, the animation will play after the state teleports. If the path does not connect from the current state, the state machine's default transition will be used to jump directly to the destination state.
If [param reset_on_teleport] is [code]true[/code], the animation is played from the beginning when the travel cause a teleportation. </description>
</method>
<method name="queue_travel">
<return type="void" />
<param index="0" name="to_node" type="StringName" />
<description>
Appends a series of transitions from the last state in the current travel path to another one, following the shortest path.
If the path does not connect, the state machine's default transition will be used to jump to the destination state.
</description>
</method>
<method name="queue">
<return type="void" />
<param index="0" name="to_node" type="StringName" />
<description>
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.
</description> </description>
</method> </method>
</methods> </methods>

View File

@ -257,7 +257,27 @@ void AnimationNodeStateMachinePlayback::_set_grouped(bool p_is_grouped) {
void AnimationNodeStateMachinePlayback::travel(const StringName &p_state, bool p_reset_on_teleport) { 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(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."); 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, p_reset_on_teleport);
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.");
// 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, true);
} }
void AnimationNodeStateMachinePlayback::start(const StringName &p_state, bool p_reset) { void AnimationNodeStateMachinePlayback::start(const StringName &p_state, bool p_reset) {
@ -276,10 +296,12 @@ void AnimationNodeStateMachinePlayback::stop() {
_stop_main(); _stop_main();
} }
void AnimationNodeStateMachinePlayback::_travel_main(const StringName &p_state, bool p_reset_on_teleport) { void AnimationNodeStateMachinePlayback::_travel_main(const StringName &p_state, bool p_reset_on_teleport, bool p_force_jump, bool p_retain_path) {
travel_request = p_state; travel_request = p_state;
reset_request_on_teleport = p_reset_on_teleport; reset_request_on_teleport = p_reset_on_teleport;
stop_request = false; stop_request = false;
jump_request = p_force_jump;
retain_path_request = p_retain_path;
} }
void AnimationNodeStateMachinePlayback::_start_main(const StringName &p_state, bool p_reset) { void AnimationNodeStateMachinePlayback::_start_main(const StringName &p_state, bool p_reset) {
@ -515,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<StringName> &r_path, bool p_test_only) { bool AnimationNodeStateMachinePlayback::_make_travel_path(AnimationTree *p_tree, AnimationNodeStateMachine *p_state_machine, bool p_is_allow_transition_to_self, Vector<StringName> &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(); travel_request = StringName();
if (!playing) { if (!playing) {
_start(p_state_machine); _start(p_state_machine);
} }
ERR_FAIL_COND_V(!p_state_machine->states.has(travel), false); ERR_FAIL_COND_V(!p_state_machine->states.has(travel_to), false);
ERR_FAIL_COND_V(!p_state_machine->states.has(current), 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; return !p_is_allow_transition_to_self;
} }
Vector<StringName> new_path; Vector<StringName> new_path;
Vector2 current_pos = p_state_machine->states[current].position; Vector2 current_pos = p_state_machine->states[travel_from].position;
Vector2 target_pos = p_state_machine->states[travel].position; Vector2 target_pos = p_state_machine->states[travel_to].position;
bool found_route = false; bool found_route = false;
HashMap<StringName, AStarCost> cost_map; HashMap<StringName, AStarCost> cost_map;
@ -545,16 +574,16 @@ bool AnimationNodeStateMachinePlayback::_make_travel_path(AnimationTree *p_tree,
continue; continue;
} }
if (p_state_machine->transitions[i].from == current) { if (p_state_machine->transitions[i].from == travel_from) {
open_list.push_back(i); open_list.push_back(i);
float cost = p_state_machine->states[p_state_machine->transitions[i].to].position.distance_to(current_pos); 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(); cost *= p_state_machine->transitions[i].transition->get_priority();
AStarCost ap; AStarCost ap;
ap.prev = current; ap.prev = travel_from;
ap.distance = cost; ap.distance = cost;
cost_map[p_state_machine->transitions[i].to] = ap; 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; found_route = true;
break; break;
} }
@ -567,7 +596,7 @@ bool AnimationNodeStateMachinePlayback::_make_travel_path(AnimationTree *p_tree,
break; // No path found. break; // No path found.
} }
// Find the last cost transition. // Find the least cost transition.
List<int>::Element *least_cost_transition = nullptr; List<int>::Element *least_cost_transition = nullptr;
float least_cost = 1e20; float least_cost = 1e20;
@ -612,7 +641,7 @@ bool AnimationNodeStateMachinePlayback::_make_travel_path(AnimationTree *p_tree,
open_list.push_back(i); 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; found_route = true;
break; break;
} }
@ -629,8 +658,8 @@ bool AnimationNodeStateMachinePlayback::_make_travel_path(AnimationTree *p_tree,
// Check child grouped state machine. // Check child grouped state machine.
if (found_route) { if (found_route) {
// Make path. // Make path.
StringName at = travel; StringName at = travel_to;
while (at != current) { while (at != travel_from) {
new_path.push_back(at); new_path.push_back(at);
at = cost_map[at].prev; at = cost_map[at].prev;
} }
@ -639,7 +668,7 @@ bool AnimationNodeStateMachinePlayback::_make_travel_path(AnimationTree *p_tree,
// Check internal paths of child grouped state machine. // Check internal paths of child grouped state machine.
// For example: // For example:
// [current - End] - [Start - End] - [Start - End] - [Start - target] // [current - End] - [Start - End] - [Start - End] - [Start - target]
String current_path = current; String current_path = travel_from;
int len = new_path.size() + 1; int len = new_path.size() + 1;
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
Ref<AnimationNodeStateMachine> anodesm = p_state_machine->find_node_by_path(current_path); Ref<AnimationNodeStateMachine> anodesm = p_state_machine->find_node_by_path(current_path);
@ -750,57 +779,86 @@ AnimationNode::NodeTimeInfo AnimationNodeStateMachinePlayback::_process(const St
} }
} }
AnimationMixer::PlaybackInfo pi = p_playback_info;
if (travel_request != StringName()) { if (travel_request != StringName()) {
// Fix path.
String travel_target = _validate_path(p_state_machine, travel_request); String travel_target = _validate_path(p_state_machine, travel_request);
Vector<String> travel_path = travel_target.split("/"); Vector<String> travel_path = travel_target.split("/");
travel_request = travel_path[0]; travel_request = travel_path[0];
StringName temp_travel_request = travel_request; // For the case that can't travel. StringName temp_travel_request = travel_request; // For the case that can't travel.
// Process children.
Vector<StringName> new_path; // Do not use transitions to move to and from special states:
bool can_travel = _make_travel_path(tree, p_state_machine, travel_path.size() <= 1 ? p_state_machine->is_allow_transition_to_self() : false, new_path, p_test_only); if (String(current).contains("/Start") || String(current).contains("/End") || String(temp_travel_request).contains("/Start") || String(temp_travel_request).contains("/End")) {
if (travel_path.size()) { teleport_request = true;
if (can_travel) {
can_travel = _travel_children(tree, p_state_machine, travel_target, p_state_machine->is_allow_transition_to_self(), travel_path[0] == current, p_test_only);
} else {
_start_children(tree, p_state_machine, travel_target, p_test_only);
}
} }
// Process to travel. // If we are already not planning to use the pathfinder, we can skip the expensive A* attempt.
if (can_travel) { if (!teleport_request && !jump_request) {
path = new_path; // Fix path.
} else { // Process children.
// Can't travel, then teleport. Vector<StringName> new_path;
if (p_state_machine->states.has(temp_travel_request)) { bool can_travel = _make_travel_path(tree, p_state_machine, travel_path.size() <= 1 ? p_state_machine->is_allow_transition_to_self() : false, new_path, p_test_only);
path.clear(); if (travel_path.size()) {
if (current != temp_travel_request || reset_request_on_teleport) { if (can_travel) {
can_travel = _travel_children(tree, p_state_machine, travel_target, p_state_machine->is_allow_transition_to_self(), travel_path[0] == current, p_test_only);
} else {
_start_children(tree, p_state_machine, travel_target, p_test_only);
}
}
// Process to travel.
if (can_travel) {
if (retain_path_request) {
retain_path_request = false;
path.append_array(new_path);
} else {
path = new_path;
}
} else {
// 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); _set_current(p_state_machine, temp_travel_request);
reset_request = reset_request_on_teleport; reset_request = reset_request_on_teleport;
teleport_request = true; teleport_request = true;
} }
}
}
if (jump_request) {
if (p_state_machine->states.has(temp_travel_request)) {
if (retain_path_request) {
retain_path_request = false;
} else {
path.clear();
}
if (p_state_machine->is_allow_transition_to_self() || current != temp_travel_request) {
path.push_back(temp_travel_request);
}
} else { } else {
ERR_FAIL_V_MSG(AnimationNode::NodeTimeInfo(), "No such node: '" + temp_travel_request + "'"); ERR_FAIL_V_MSG(AnimationNode::NodeTimeInfo(), "No such node: '" + temp_travel_request + "'");
} }
} }
}
AnimationMixer::PlaybackInfo pi = p_playback_info; if (teleport_request) {
teleport_request = false;
if (teleport_request) { // Clear fadeing on teleport.
teleport_request = false; fading_from = StringName();
// Clear fadeing on teleport. fadeing_from_nti = AnimationNode::NodeTimeInfo();
fading_from = StringName(); fading_pos = 0;
fadeing_from_nti = AnimationNode::NodeTimeInfo(); // Init current length.
fading_pos = 0; pi.time = 0;
// Init current length. pi.seeked = true;
pi.time = 0; pi.is_external_seeking = false;
pi.seeked = true; pi.weight = 0;
pi.is_external_seeking = false; current_nti = p_state_machine->blend_node(p_state_machine->states[current].node, current, pi, AnimationNode::FILTER_IGNORE, true, true);
pi.weight = 0; // Don't process first node if not necessary, instead process next node.
current_nti = p_state_machine->blend_node(p_state_machine->states[current].node, current, pi, AnimationNode::FILTER_IGNORE, true, true); _transition_to_next_recursive(tree, p_state_machine, p_delta, p_test_only);
// Don't process first node if not necessary, instead process next node. }
_transition_to_next_recursive(tree, p_state_machine, p_delta, p_test_only);
} }
// Check current node existence. // Check current node existence.
@ -1058,8 +1116,24 @@ AnimationNodeStateMachinePlayback::NextInfo AnimationNodeStateMachinePlayback::_
next.switch_mode = ref_transition->get_switch_mode(); next.switch_mode = ref_transition->get_switch_mode();
next.is_reset = ref_transition->is_reset(); next.is_reset = ref_transition->is_reset();
next.break_loop_at_end = ref_transition->is_loop_broken_at_end(); next.break_loop_at_end = ref_transition->is_loop_broken_at_end();
return next; // Once we have the information we need, we can actually just return that info
} }
} }
// There is no transition that ends at the next state in the state machine. We can use the default transition
next.node = path[0];
if (p_state_machine->default_transition.is_null()) {
next.xfade = 0;
next.switch_mode = AnimationNodeStateMachineTransition::SWITCH_MODE_IMMEDIATE;
next.is_reset = false;
next.break_loop_at_end = false;
} else {
next.xfade = p_state_machine->default_transition->get_xfade_time();
next.curve = p_state_machine->default_transition->get_xfade_curve();
next.switch_mode = p_state_machine->default_transition->get_switch_mode();
next.is_reset = p_state_machine->default_transition->is_reset();
next.break_loop_at_end = p_state_machine->default_transition->is_loop_broken_at_end();
}
return next; // Once we have the information we need, we can actually just return that info
} else { } else {
int auto_advance_to = -1; int auto_advance_to = -1;
float priority_best = 1e20; float priority_best = 1e20;
@ -1198,11 +1272,6 @@ void AnimationNodeStateMachinePlayback::_bind_methods() {
AnimationNodeStateMachinePlayback::AnimationNodeStateMachinePlayback() { AnimationNodeStateMachinePlayback::AnimationNodeStateMachinePlayback() {
set_local_to_scene(true); // Only one per instantiated scene. set_local_to_scene(true); // Only one per instantiated scene.
default_transition.instantiate();
default_transition->set_xfade_time(0);
default_transition->set_reset(true);
default_transition->set_advance_mode(AnimationNodeStateMachineTransition::ADVANCE_MODE_AUTO);
default_transition->set_switch_mode(AnimationNodeStateMachineTransition::SWITCH_MODE_IMMEDIATE);
} }
/////////////////////////////////////////////////////// ///////////////////////////////////////////////////////
@ -1320,6 +1389,14 @@ bool AnimationNodeStateMachine::are_ends_reset() const {
return reset_ends; return reset_ends;
} }
void AnimationNodeStateMachine::set_default_transition(Ref<AnimationNodeStateMachineTransition> p_transition) {
default_transition = p_transition;
}
Ref<AnimationNodeStateMachineTransition> AnimationNodeStateMachine::get_default_transition() {
return default_transition;
}
bool AnimationNodeStateMachine::can_edit_node(const StringName &p_name) const { bool AnimationNodeStateMachine::can_edit_node(const StringName &p_name) const {
if (states.has(p_name)) { if (states.has(p_name)) {
const AnimationNode *anode = states[p_name].node.ptr(); const AnimationNode *anode = states[p_name].node.ptr();
@ -1789,6 +1866,9 @@ void AnimationNodeStateMachine::get_argument_options(const StringName &p_functio
#endif #endif
void AnimationNodeStateMachine::_bind_methods() { void AnimationNodeStateMachine::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_default_transition", "transition"), &AnimationNodeStateMachine::set_default_transition);
ClassDB::bind_method(D_METHOD("get_default_transition"), &AnimationNodeStateMachine::get_default_transition);
ClassDB::bind_method(D_METHOD("add_node", "name", "node", "position"), &AnimationNodeStateMachine::add_node, DEFVAL(Vector2())); ClassDB::bind_method(D_METHOD("add_node", "name", "node", "position"), &AnimationNodeStateMachine::add_node, DEFVAL(Vector2()));
ClassDB::bind_method(D_METHOD("replace_node", "name", "node"), &AnimationNodeStateMachine::replace_node); ClassDB::bind_method(D_METHOD("replace_node", "name", "node"), &AnimationNodeStateMachine::replace_node);
ClassDB::bind_method(D_METHOD("get_node", "name"), &AnimationNodeStateMachine::get_node); ClassDB::bind_method(D_METHOD("get_node", "name"), &AnimationNodeStateMachine::get_node);
@ -1824,6 +1904,7 @@ void AnimationNodeStateMachine::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "state_machine_type", PROPERTY_HINT_ENUM, "Root,Nested,Grouped"), "set_state_machine_type", "get_state_machine_type"); ADD_PROPERTY(PropertyInfo(Variant::INT, "state_machine_type", PROPERTY_HINT_ENUM, "Root,Nested,Grouped"), "set_state_machine_type", "get_state_machine_type");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_transition_to_self"), "set_allow_transition_to_self", "is_allow_transition_to_self"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_transition_to_self"), "set_allow_transition_to_self", "is_allow_transition_to_self");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "reset_ends"), "set_reset_ends", "are_ends_reset"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "reset_ends"), "set_reset_ends", "are_ends_reset");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "default_transition", PROPERTY_HINT_RESOURCE_TYPE, "AnimationNodeStateMachineTransition"), "set_default_transition", "get_default_transition");
BIND_ENUM_CONSTANT(STATE_MACHINE_TYPE_ROOT); BIND_ENUM_CONSTANT(STATE_MACHINE_TYPE_ROOT);
BIND_ENUM_CONSTANT(STATE_MACHINE_TYPE_NESTED); BIND_ENUM_CONSTANT(STATE_MACHINE_TYPE_NESTED);

View File

@ -136,6 +136,7 @@ private:
Ref<AnimationNodeStateMachineTransition> transition; Ref<AnimationNodeStateMachineTransition> transition;
}; };
Ref<AnimationNodeStateMachineTransition> default_transition;
Vector<Transition> transitions; Vector<Transition> transitions;
StringName playback = "playback"; StringName playback = "playback";
@ -206,6 +207,9 @@ public:
void set_reset_ends(bool p_enable); void set_reset_ends(bool p_enable);
bool are_ends_reset() const; bool are_ends_reset() const;
void set_default_transition(Ref<AnimationNodeStateMachineTransition> p_transition);
Ref<AnimationNodeStateMachineTransition> get_default_transition();
bool can_edit_node(const StringName &p_name) const; bool can_edit_node(const StringName &p_name) const;
void set_graph_offset(const Vector2 &p_offset); void set_graph_offset(const Vector2 &p_offset);
@ -259,7 +263,6 @@ class AnimationNodeStateMachinePlayback : public Resource {
bool is_reset = false; bool is_reset = false;
}; };
Ref<AnimationNodeStateMachineTransition> default_transition;
String base_path; String base_path;
AnimationNode::NodeTimeInfo current_nti; AnimationNode::NodeTimeInfo current_nti;
@ -284,11 +287,14 @@ class AnimationNodeStateMachinePlayback : public Resource {
bool _reset_request_for_fading_from = false; bool _reset_request_for_fading_from = false;
bool next_request = false; bool next_request = false;
bool stop_request = false; bool stop_request = false;
bool request_jump_instead = false;
bool teleport_request = false; bool teleport_request = false;
bool jump_request = false;
bool retain_path_request = false;
bool is_grouped = false; bool is_grouped = false;
void _travel_main(const StringName &p_state, bool p_reset_on_teleport = true); void _travel_main(const StringName &p_state, bool p_reset_on_teleport = true, bool p_force_jump = false, bool p_retain_path = false);
void _start_main(const StringName &p_state, bool p_reset = true); void _start_main(const StringName &p_state, bool p_reset = true);
void _next_main(); void _next_main();
void _stop_main(); void _stop_main();
@ -326,6 +332,8 @@ protected:
public: public:
void travel(const StringName &p_state, bool p_reset_on_teleport = true); 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); void start(const StringName &p_state, bool p_reset = true);
void next(); void next();
void stop(); void stop();