summaryrefslogtreecommitdiff
path: root/scene/animation/animation_node_state_machine.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'scene/animation/animation_node_state_machine.cpp')
-rw-r--r--scene/animation/animation_node_state_machine.cpp459
1 files changed, 246 insertions, 213 deletions
diff --git a/scene/animation/animation_node_state_machine.cpp b/scene/animation/animation_node_state_machine.cpp
index 2ee7f4fa43..2c79e5fe06 100644
--- a/scene/animation/animation_node_state_machine.cpp
+++ b/scene/animation/animation_node_state_machine.cpp
@@ -1,34 +1,35 @@
-/*************************************************************************/
-/* animation_node_state_machine.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
+/**************************************************************************/
+/* animation_node_state_machine.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
#include "animation_node_state_machine.h"
+#include "scene/main/window.h"
/////////////////////////////////////////////////
@@ -40,12 +41,12 @@ AnimationNodeStateMachineTransition::SwitchMode AnimationNodeStateMachineTransit
return switch_mode;
}
-void AnimationNodeStateMachineTransition::set_auto_advance(bool p_enable) {
- auto_advance = p_enable;
+void AnimationNodeStateMachineTransition::set_advance_mode(AdvanceMode p_mode) {
+ advance_mode = p_mode;
}
-bool AnimationNodeStateMachineTransition::has_auto_advance() const {
- return auto_advance;
+AnimationNodeStateMachineTransition::AdvanceMode AnimationNodeStateMachineTransition::get_advance_mode() const {
+ return advance_mode;
}
void AnimationNodeStateMachineTransition::set_advance_condition(const StringName &p_condition) {
@@ -88,31 +89,31 @@ String AnimationNodeStateMachineTransition::get_advance_expression() const {
return advance_expression;
}
-void AnimationNodeStateMachineTransition::set_advance_expression_base_node(const NodePath &p_expression_base_node) {
- advance_expression_base_node = p_expression_base_node;
-}
-
-NodePath AnimationNodeStateMachineTransition::get_advance_expression_base_node() const {
- return advance_expression_base_node;
-}
-
void AnimationNodeStateMachineTransition::set_xfade_time(float p_xfade) {
ERR_FAIL_COND(p_xfade < 0);
- xfade = p_xfade;
+ xfade_time = p_xfade;
emit_changed();
}
float AnimationNodeStateMachineTransition::get_xfade_time() const {
- return xfade;
+ return xfade_time;
+}
+
+void AnimationNodeStateMachineTransition::set_xfade_curve(const Ref<Curve> &p_curve) {
+ xfade_curve = p_curve;
}
-void AnimationNodeStateMachineTransition::set_disabled(bool p_disabled) {
- disabled = p_disabled;
+Ref<Curve> AnimationNodeStateMachineTransition::get_xfade_curve() const {
+ return xfade_curve;
+}
+
+void AnimationNodeStateMachineTransition::set_reset(bool p_reset) {
+ reset = p_reset;
emit_changed();
}
-bool AnimationNodeStateMachineTransition::is_disabled() const {
- return disabled;
+bool AnimationNodeStateMachineTransition::is_reset() const {
+ return reset;
}
void AnimationNodeStateMachineTransition::set_priority(int p_priority) {
@@ -128,8 +129,8 @@ void AnimationNodeStateMachineTransition::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_switch_mode", "mode"), &AnimationNodeStateMachineTransition::set_switch_mode);
ClassDB::bind_method(D_METHOD("get_switch_mode"), &AnimationNodeStateMachineTransition::get_switch_mode);
- ClassDB::bind_method(D_METHOD("set_auto_advance", "auto_advance"), &AnimationNodeStateMachineTransition::set_auto_advance);
- ClassDB::bind_method(D_METHOD("has_auto_advance"), &AnimationNodeStateMachineTransition::has_auto_advance);
+ ClassDB::bind_method(D_METHOD("set_advance_mode", "mode"), &AnimationNodeStateMachineTransition::set_advance_mode);
+ ClassDB::bind_method(D_METHOD("get_advance_mode"), &AnimationNodeStateMachineTransition::get_advance_mode);
ClassDB::bind_method(D_METHOD("set_advance_condition", "name"), &AnimationNodeStateMachineTransition::set_advance_condition);
ClassDB::bind_method(D_METHOD("get_advance_condition"), &AnimationNodeStateMachineTransition::get_advance_condition);
@@ -137,8 +138,11 @@ void AnimationNodeStateMachineTransition::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_xfade_time", "secs"), &AnimationNodeStateMachineTransition::set_xfade_time);
ClassDB::bind_method(D_METHOD("get_xfade_time"), &AnimationNodeStateMachineTransition::get_xfade_time);
- ClassDB::bind_method(D_METHOD("set_disabled", "disabled"), &AnimationNodeStateMachineTransition::set_disabled);
- ClassDB::bind_method(D_METHOD("is_disabled"), &AnimationNodeStateMachineTransition::is_disabled);
+ ClassDB::bind_method(D_METHOD("set_xfade_curve", "curve"), &AnimationNodeStateMachineTransition::set_xfade_curve);
+ ClassDB::bind_method(D_METHOD("get_xfade_curve"), &AnimationNodeStateMachineTransition::get_xfade_curve);
+
+ ClassDB::bind_method(D_METHOD("set_reset", "reset"), &AnimationNodeStateMachineTransition::set_reset);
+ ClassDB::bind_method(D_METHOD("is_reset"), &AnimationNodeStateMachineTransition::is_reset);
ClassDB::bind_method(D_METHOD("set_priority", "priority"), &AnimationNodeStateMachineTransition::set_priority);
ClassDB::bind_method(D_METHOD("get_priority"), &AnimationNodeStateMachineTransition::get_priority);
@@ -146,25 +150,27 @@ void AnimationNodeStateMachineTransition::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_advance_expression", "text"), &AnimationNodeStateMachineTransition::set_advance_expression);
ClassDB::bind_method(D_METHOD("get_advance_expression"), &AnimationNodeStateMachineTransition::get_advance_expression);
- ClassDB::bind_method(D_METHOD("set_advance_expression_base_node", "path"), &AnimationNodeStateMachineTransition::set_advance_expression_base_node);
- ClassDB::bind_method(D_METHOD("get_advance_expression_base_node"), &AnimationNodeStateMachineTransition::get_advance_expression_base_node);
-
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "xfade_time", PROPERTY_HINT_RANGE, "0,240,0.01,suffix:s"), "set_xfade_time", "get_xfade_time");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "xfade_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_xfade_curve", "get_xfade_curve");
+
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "reset"), "set_reset", "is_reset");
+
ADD_PROPERTY(PropertyInfo(Variant::INT, "priority", PROPERTY_HINT_RANGE, "0,32,1"), "set_priority", "get_priority");
ADD_GROUP("Switch", "");
ADD_PROPERTY(PropertyInfo(Variant::INT, "switch_mode", PROPERTY_HINT_ENUM, "Immediate,Sync,At End"), "set_switch_mode", "get_switch_mode");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_advance"), "set_auto_advance", "has_auto_advance");
ADD_GROUP("Advance", "advance_");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "advance_mode", PROPERTY_HINT_ENUM, "Disabled,Enabled,Auto"), "set_advance_mode", "get_advance_mode");
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "advance_condition"), "set_advance_condition", "get_advance_condition");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "advance_expression", PROPERTY_HINT_EXPRESSION, ""), "set_advance_expression", "get_advance_expression");
- ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "advance_expression_base_node"), "set_advance_expression_base_node", "get_advance_expression_base_node");
- ADD_GROUP("Disabling", "");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disabled"), "set_disabled", "is_disabled");
BIND_ENUM_CONSTANT(SWITCH_MODE_IMMEDIATE);
BIND_ENUM_CONSTANT(SWITCH_MODE_SYNC);
BIND_ENUM_CONSTANT(SWITCH_MODE_AT_END);
+ BIND_ENUM_CONSTANT(ADVANCE_MODE_DISABLED);
+ BIND_ENUM_CONSTANT(ADVANCE_MODE_ENABLED);
+ BIND_ENUM_CONSTANT(ADVANCE_MODE_AUTO);
+
ADD_SIGNAL(MethodInfo("advance_condition_changed"));
}
@@ -173,18 +179,27 @@ AnimationNodeStateMachineTransition::AnimationNodeStateMachineTransition() {
////////////////////////////////////////////////////////
-void AnimationNodeStateMachinePlayback::travel(const StringName &p_state) {
- start_request_travel = true;
- start_request = p_state;
+void AnimationNodeStateMachinePlayback::travel(const StringName &p_state, bool p_reset_on_teleport) {
+ travel_request = p_state;
+ reset_request_on_teleport = p_reset_on_teleport;
stop_request = false;
}
-void AnimationNodeStateMachinePlayback::start(const StringName &p_state) {
- start_request_travel = false;
+void AnimationNodeStateMachinePlayback::start(const StringName &p_state, bool p_reset) {
+ travel_request = StringName();
+ reset_request = p_reset;
+ _start(p_state);
+}
+
+void AnimationNodeStateMachinePlayback::_start(const StringName &p_state) {
start_request = p_state;
stop_request = false;
}
+void AnimationNodeStateMachinePlayback::next() {
+ next_request = true;
+}
+
void AnimationNodeStateMachinePlayback::stop() {
stop_request = true;
}
@@ -221,7 +236,7 @@ bool AnimationNodeStateMachinePlayback::_travel(AnimationNodeStateMachine *p_sta
path.clear(); //a new one will be needed
if (current == p_travel) {
- return true; //nothing to do
+ return false; // Will teleport oneself (restart).
}
Vector2 current_pos = p_state_machine->states[current].position;
@@ -233,7 +248,7 @@ bool AnimationNodeStateMachinePlayback::_travel(AnimationNodeStateMachine *p_sta
//build open list
for (int i = 0; i < p_state_machine->transitions.size(); i++) {
- if (p_state_machine->transitions[i].transition->is_disabled()) {
+ if (p_state_machine->transitions[i].transition->get_advance_mode() == AnimationNodeStateMachineTransition::ADVANCE_MODE_DISABLED) {
continue;
}
@@ -278,7 +293,7 @@ bool AnimationNodeStateMachinePlayback::_travel(AnimationNodeStateMachine *p_sta
StringName transition = p_state_machine->transitions[least_cost_transition->get()].local_to;
for (int i = 0; i < p_state_machine->transitions.size(); i++) {
- if (p_state_machine->transitions[i].transition->is_disabled()) {
+ if (p_state_machine->transitions[i].transition->get_advance_mode() == AnimationNodeStateMachineTransition::ADVANCE_MODE_DISABLED) {
continue;
}
@@ -331,18 +346,35 @@ bool AnimationNodeStateMachinePlayback::_travel(AnimationNodeStateMachine *p_sta
return true;
}
-double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_state_machine, double p_time, bool p_seek, bool p_seek_root) {
+double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_state_machine, double p_time, bool p_seek, bool p_is_external_seeking) {
+ double rem = _process(p_state_machine, p_time, p_seek, p_is_external_seeking);
+ start_request = StringName();
+ next_request = false;
+ stop_request = false;
+ reset_request_on_teleport = false;
+ return rem;
+}
+
+double AnimationNodeStateMachinePlayback::_process(AnimationNodeStateMachine *p_state_machine, double p_time, bool p_seek, bool p_is_external_seeking) {
+ if (p_time == -1) {
+ Ref<AnimationNodeStateMachine> anodesm = p_state_machine->states[current].node;
+ if (anodesm.is_valid()) {
+ p_state_machine->blend_node(current, p_state_machine->states[current].node, -1, p_seek, p_is_external_seeking, 0, AnimationNode::FILTER_IGNORE, true);
+ }
+ playing = false;
+ return 0;
+ }
+
//if not playing and it can restart, then restart
if (!playing && start_request == StringName()) {
if (!stop_request && p_state_machine->start_node) {
- start(p_state_machine->start_node);
+ _start(p_state_machine->start_node);
} else {
return 0;
}
}
if (playing && stop_request) {
- stop_request = false;
playing = false;
return 0;
}
@@ -350,53 +382,60 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s
bool play_start = false;
if (start_request != StringName()) {
- if (start_request_travel) {
- if (!playing) {
- if (!stop_request && p_state_machine->start_node) {
- // can restart, just postpone traveling
- path.clear();
- current = p_state_machine->start_node;
- playing = true;
- play_start = true;
- } else {
- // stopped, invalid state
- String node_name = start_request;
- start_request = StringName(); //clear start request
- ERR_FAIL_V_MSG(0, "Can't travel to '" + node_name + "' if state machine is not playing. Maybe you need to enable Autoplay on Load for one of the nodes in your state machine or call .start() first?");
- }
- } else {
- if (!_travel(p_state_machine, start_request)) {
- // can't travel, then teleport
- path.clear();
- current = start_request;
- }
- start_request = StringName(); //clear start request
- }
+ // teleport to start
+ if (p_state_machine->states.has(start_request)) {
+ path.clear();
+ current = start_request;
+ playing = true;
+ play_start = true;
} else {
- // teleport to start
- if (p_state_machine->states.has(start_request)) {
+ StringName node = start_request;
+ ERR_FAIL_V_MSG(0, "No such node: '" + node + "'");
+ }
+ } else if (travel_request != StringName()) {
+ if (!playing) {
+ if (!stop_request && p_state_machine->start_node) {
+ // can restart, just postpone traveling
path.clear();
- current = start_request;
+ current = p_state_machine->start_node;
playing = true;
play_start = true;
- start_request = StringName(); //clear start request
} else {
- StringName node = start_request;
- start_request = StringName(); //clear start request
- ERR_FAIL_V_MSG(0, "No such node: '" + node + "'");
+ // stopped, invalid state
+ String node_name = travel_request;
+ travel_request = StringName();
+ ERR_FAIL_V_MSG(0, "Can't travel to '" + node_name + "' if state machine is not playing. Maybe you need to enable Autoplay on Load for one of the nodes in your state machine or call .start() first?");
}
+ } else {
+ if (!_travel(p_state_machine, travel_request)) {
+ // can't travel, then teleport
+ if (p_state_machine->states.has(travel_request)) {
+ path.clear();
+ current = travel_request;
+ play_start = true;
+ reset_request = reset_request_on_teleport;
+ } else {
+ StringName node = travel_request;
+ travel_request = StringName();
+ ERR_FAIL_V_MSG(0, "No such node: '" + node + "'");
+ }
+ }
+ travel_request = StringName();
}
}
bool do_start = (p_seek && p_time == 0) || play_start || current == StringName();
if (do_start) {
- if (p_state_machine->start_node != StringName() && p_seek && p_time == 0) {
+ if (p_state_machine->start_node != StringName() && p_seek && p_time == 0 && current == StringName()) {
current = p_state_machine->start_node;
}
- len_current = p_state_machine->blend_node(current, p_state_machine->states[current].node, 0, true, p_seek_root, 1.0, AnimationNode::FILTER_IGNORE, false);
- pos_current = 0;
+ if (reset_request) {
+ len_current = p_state_machine->blend_node(current, p_state_machine->states[current].node, 0, true, p_is_external_seeking, 1.0, AnimationNode::FILTER_IGNORE, true);
+ pos_current = 0;
+ reset_request = false;
+ }
}
if (!p_state_machine->states.has(current)) {
@@ -414,16 +453,21 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s
fading_pos += p_time;
}
fade_blend = MIN(1.0, fading_pos / fading_time);
- if (fade_blend >= 1.0) {
- fading_from = StringName();
- }
}
}
- float rem = p_state_machine->blend_node(current, p_state_machine->states[current].node, p_time, p_seek, p_seek_root, fade_blend, AnimationNode::FILTER_IGNORE, false);
+ if (current_curve.is_valid()) {
+ fade_blend = current_curve->sample(fade_blend);
+ }
+
+ double rem = do_start ? len_current : p_state_machine->blend_node(current, p_state_machine->states[current].node, p_time, p_seek, p_is_external_seeking, Math::is_zero_approx(fade_blend) ? CMP_EPSILON : fade_blend, AnimationNode::FILTER_IGNORE, true); // Blend values must be more than CMP_EPSILON to process discrete keys in edge.
if (fading_from != StringName()) {
- p_state_machine->blend_node(fading_from, p_state_machine->states[fading_from].node, p_time, p_seek, p_seek_root, 1.0 - fade_blend, AnimationNode::FILTER_IGNORE, false);
+ double fade_blend_inv = 1.0 - fade_blend;
+ p_state_machine->blend_node(fading_from, p_state_machine->states[fading_from].node, p_time, p_seek, p_is_external_seeking, Math::is_zero_approx(fade_blend_inv) ? CMP_EPSILON : fade_blend_inv, AnimationNode::FILTER_IGNORE, true); // Blend values must be more than CMP_EPSILON to process discrete keys in edge.
+ if (fade_blend >= 1.0) {
+ fading_from = StringName();
+ }
}
//guess playback position
@@ -432,25 +476,27 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s
}
{ //advance and loop check
- float next_pos = len_current - rem;
+ double next_pos = len_current - rem;
end_loop = next_pos < pos_current;
pos_current = next_pos; //looped
}
//find next
StringName next;
- float next_xfade = 0.0;
+ double next_xfade = 0.0;
AnimationNodeStateMachineTransition::SwitchMode switch_mode = AnimationNodeStateMachineTransition::SWITCH_MODE_IMMEDIATE;
if (path.size()) {
for (int i = 0; i < p_state_machine->transitions.size(); i++) {
- if (p_state_machine->transitions[i].transition->is_disabled()) {
+ if (p_state_machine->transitions[i].transition->get_advance_mode() == AnimationNodeStateMachineTransition::ADVANCE_MODE_DISABLED) {
continue;
}
if (p_state_machine->transitions[i].local_from == current && p_state_machine->transitions[i].local_to == path[0]) {
next_xfade = p_state_machine->transitions[i].transition->get_xfade_time();
+ current_curve = p_state_machine->transitions[i].transition->get_xfade_curve();
switch_mode = p_state_machine->transitions[i].transition->get_switch_mode();
+ reset_request = p_state_machine->transitions[i].transition->is_reset();
next = path[0];
}
}
@@ -459,7 +505,7 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s
int auto_advance_to = -1;
for (int i = 0; i < p_state_machine->transitions.size(); i++) {
- if (p_state_machine->transitions[i].transition->is_disabled()) {
+ if (p_state_machine->transitions[i].transition->get_advance_mode() == AnimationNodeStateMachineTransition::ADVANCE_MODE_DISABLED) {
continue;
}
@@ -475,7 +521,7 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s
// handles start_node: if previous state machine is pointing to a node inside the current state machine, starts the current machine from start_node to prev_local_to
if (p_state_machine->start_node == current && p_state_machine->transitions[i].local_from == current) {
if (p_state_machine->prev_state_machine != nullptr) {
- Ref<AnimationNodeStateMachinePlayback> prev_playback = p_state_machine->prev_state_machine->get_parameter("playback");
+ Ref<AnimationNodeStateMachinePlayback> prev_playback = p_state_machine->prev_state_machine->get_parameter(p_state_machine->playback);
if (prev_playback.is_valid()) {
StringName prev_local_to = String(prev_playback->current_transition.next).replace_first(String(p_state_machine->state_machine_name) + "/", "");
@@ -504,8 +550,10 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s
tr.to = String(p_state_machine->transitions[auto_advance_to].to).replace_first("../", "");
tr.next = p_state_machine->transitions[auto_advance_to].to;
current_transition = tr;
+ current_curve = p_state_machine->transitions[auto_advance_to].transition->get_xfade_curve();
next_xfade = p_state_machine->transitions[auto_advance_to].transition->get_xfade_time();
switch_mode = p_state_machine->transitions[auto_advance_to].transition->get_switch_mode();
+ reset_request = p_state_machine->transitions[auto_advance_to].transition->is_reset();
}
}
@@ -513,7 +561,7 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s
AnimationNodeStateMachine *prev_state_machine = p_state_machine->prev_state_machine;
if (prev_state_machine != nullptr) {
- Ref<AnimationNodeStateMachinePlayback> prev_playback = prev_state_machine->get_parameter("playback");
+ Ref<AnimationNodeStateMachinePlayback> prev_playback = prev_state_machine->get_parameter(p_state_machine->playback);
if (prev_playback.is_valid()) {
if (next_xfade) {
@@ -526,7 +574,7 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s
int auto_advance_to = -1;
for (int i = 0; i < prev_state_machine->transitions.size(); i++) {
- if (prev_state_machine->transitions[i].transition->is_disabled()) {
+ if (prev_state_machine->transitions[i].transition->get_advance_mode() == AnimationNodeStateMachineTransition::ADVANCE_MODE_DISABLED) {
continue;
}
@@ -560,8 +608,7 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s
goto_next = fading_from == StringName();
}
- if (goto_next) { //end_loop should be used because fade time may be too small or zero and animation may have looped
-
+ if (next_request || goto_next) { //end_loop should be used because fade time may be too small or zero and animation may have looped
if (next_xfade) {
//time to fade, baby
fading_from = current;
@@ -575,14 +622,23 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s
if (path.size()) { //if it came from path, remove path
path.remove_at(0);
}
+
+ { // if the current node is a state machine, update the "playing" variable to false by passing -1 in p_time
+ Ref<AnimationNodeStateMachine> anodesm = p_state_machine->states[current].node;
+ if (anodesm.is_valid()) {
+ p_state_machine->blend_node(current, p_state_machine->states[current].node, -1, p_seek, p_is_external_seeking, 0, AnimationNode::FILTER_IGNORE, true);
+ }
+ }
+
current = next;
+
+ if (reset_request) {
+ len_current = p_state_machine->blend_node(current, p_state_machine->states[current].node, 0, true, p_is_external_seeking, CMP_EPSILON, AnimationNode::FILTER_IGNORE, true); // Process next node's first key in here.
+ }
if (switch_mode == AnimationNodeStateMachineTransition::SWITCH_MODE_SYNC) {
- len_current = p_state_machine->blend_node(current, p_state_machine->states[current].node, 0, true, p_seek_root, 0, AnimationNode::FILTER_IGNORE, false);
pos_current = MIN(pos_current, len_current);
- p_state_machine->blend_node(current, p_state_machine->states[current].node, pos_current, true, p_seek_root, 0, AnimationNode::FILTER_IGNORE, false);
-
+ p_state_machine->blend_node(current, p_state_machine->states[current].node, pos_current, true, p_is_external_seeking, 0, AnimationNode::FILTER_IGNORE, true);
} else {
- len_current = p_state_machine->blend_node(current, p_state_machine->states[current].node, 0, true, p_seek_root, 0, AnimationNode::FILTER_IGNORE, false);
pos_current = 0;
}
@@ -590,15 +646,16 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s
}
}
- // time left must always be 1 because the end node don't length to compute
- if (p_state_machine->end_node != current) {
- rem = 1;
+ if (current != p_state_machine->end_node) {
+ rem = 1; // the time remaining must always be 1 because there is no way to predict how long it takes for the entire state machine to complete
} else {
- Ref<AnimationNodeStateMachinePlayback> prev_playback = p_state_machine->prev_state_machine->get_parameter("playback");
+ if (p_state_machine->prev_state_machine != nullptr) {
+ Ref<AnimationNodeStateMachinePlayback> prev_playback = p_state_machine->prev_state_machine->get_parameter(p_state_machine->playback);
- if (prev_playback.is_valid()) {
- prev_playback->current_transition = current_transition;
- prev_playback->force_auto_advance = true;
+ if (prev_playback.is_valid()) {
+ prev_playback->current_transition = current_transition;
+ prev_playback->force_auto_advance = true;
+ }
}
}
@@ -606,45 +663,41 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s
}
bool AnimationNodeStateMachinePlayback::_check_advance_condition(const Ref<AnimationNodeStateMachine> state_machine, const Ref<AnimationNodeStateMachineTransition> transition) const {
- if (transition->has_auto_advance()) {
- return true;
+ if (transition->get_advance_mode() != AnimationNodeStateMachineTransition::ADVANCE_MODE_AUTO) {
+ return false;
}
StringName advance_condition_name = transition->get_advance_condition_name();
- if (advance_condition_name != StringName() && bool(state_machine->get_parameter(advance_condition_name))) {
- return true;
+ if (advance_condition_name != StringName() && !bool(state_machine->get_parameter(advance_condition_name))) {
+ return false;
}
if (transition->expression.is_valid()) {
AnimationTree *tree_base = state_machine->get_animation_tree();
ERR_FAIL_COND_V(tree_base == nullptr, false);
- NodePath advance_expression_base_node_path;
- if (!transition->advance_expression_base_node.is_empty()) {
- advance_expression_base_node_path = transition->advance_expression_base_node;
- } else {
- advance_expression_base_node_path = tree_base->get_advance_expression_base_node();
- }
-
+ NodePath advance_expression_base_node_path = tree_base->get_advance_expression_base_node();
Node *expression_base = tree_base->get_node_or_null(advance_expression_base_node_path);
+
if (expression_base) {
Ref<Expression> exp = transition->expression;
- bool ret = exp->execute(Array(), tree_base, false, Engine::get_singleton()->is_editor_hint()); // Avoids allowing the user to crash the system with an expression by only allowing const calls.
- if (!exp->has_execute_failed()) {
- if (ret) {
- return true;
- }
+ bool ret = exp->execute(Array(), expression_base, false, Engine::get_singleton()->is_editor_hint()); // Avoids allowing the user to crash the system with an expression by only allowing const calls.
+ if (exp->has_execute_failed() || !ret) {
+ return false;
}
+ } else {
+ WARN_PRINT_ONCE("Animation transition has a valid expression, but no expression base node was set on its AnimationTree.");
}
}
- return false;
+ return true;
}
void AnimationNodeStateMachinePlayback::_bind_methods() {
- ClassDB::bind_method(D_METHOD("travel", "to_node"), &AnimationNodeStateMachinePlayback::travel);
- ClassDB::bind_method(D_METHOD("start", "node"), &AnimationNodeStateMachinePlayback::start);
+ ClassDB::bind_method(D_METHOD("travel", "to_node", "reset_on_teleport"), &AnimationNodeStateMachinePlayback::travel, DEFVAL(true));
+ ClassDB::bind_method(D_METHOD("start", "node", "reset"), &AnimationNodeStateMachinePlayback::start, DEFVAL(true));
+ ClassDB::bind_method(D_METHOD("next"), &AnimationNodeStateMachinePlayback::next);
ClassDB::bind_method(D_METHOD("stop"), &AnimationNodeStateMachinePlayback::stop);
ClassDB::bind_method(D_METHOD("is_playing"), &AnimationNodeStateMachinePlayback::is_playing);
ClassDB::bind_method(D_METHOD("get_current_node"), &AnimationNodeStateMachinePlayback::get_current_node);
@@ -673,23 +726,6 @@ void AnimationNodeStateMachine::get_parameter_list(List<PropertyInfo> *r_list) c
for (const StringName &E : advance_conditions) {
r_list->push_back(PropertyInfo(Variant::BOOL, E));
}
-
- // for (const KeyValue<StringName, State> &E : states) {
- // if (E->node == ansm) {
- // for (int i = 0; i < E->node->transitions.size(); i++) {
- // StringName ac = E->node->transitions[i].transition->get_advance_condition_name();
- // if (ac != StringName() && advance_conditions.find(ac) == nullptr) {
- // advance_conditions.push_back(ac);
- // }
- // }
-
- // advance_conditions.sort_custom<StringName::AlphCompare>();
-
- // for (const StringName &E : advance_conditions) {
- // r_list->push_back(PropertyInfo(Variant::BOOL, E));
- // }
- // }
- // }
}
Variant AnimationNodeStateMachine::get_parameter_default_value(const StringName &p_parameter) const {
@@ -707,11 +743,11 @@ void AnimationNodeStateMachine::add_node(const StringName &p_name, Ref<Animation
ERR_FAIL_COND(p_node.is_null());
ERR_FAIL_COND(String(p_name).contains("/"));
- State state;
- state.node = p_node;
- state.position = p_position;
+ State state_new;
+ state_new.node = p_node;
+ state_new.position = p_position;
- states[p_name] = state;
+ states[p_name] = state_new;
Ref<AnimationNodeStateMachine> anodesm = p_node;
@@ -723,7 +759,7 @@ void AnimationNodeStateMachine::add_node(const StringName &p_name, Ref<Animation
emit_changed();
emit_signal(SNAME("tree_changed"));
- p_node->connect("tree_changed", callable_mp(this, &AnimationNodeStateMachine::_tree_changed), varray(), CONNECT_REFERENCE_COUNTED);
+ p_node->connect("tree_changed", callable_mp(this, &AnimationNodeStateMachine::_tree_changed), CONNECT_REFERENCE_COUNTED);
}
void AnimationNodeStateMachine::replace_node(const StringName &p_name, Ref<AnimationNode> p_node) {
@@ -743,7 +779,7 @@ void AnimationNodeStateMachine::replace_node(const StringName &p_name, Ref<Anima
emit_changed();
emit_signal(SNAME("tree_changed"));
- p_node->connect("tree_changed", callable_mp(this, &AnimationNodeStateMachine::_tree_changed), varray(), CONNECT_REFERENCE_COUNTED);
+ p_node->connect("tree_changed", callable_mp(this, &AnimationNodeStateMachine::_tree_changed), CONNECT_REFERENCE_COUNTED);
}
bool AnimationNodeStateMachine::can_edit_node(const StringName &p_name) const {
@@ -830,20 +866,13 @@ void AnimationNodeStateMachine::rename_node(const StringName &p_name, const Stri
anodesm->state_machine_name = p_new_name;
}
- for (int i = 0; i < transitions.size(); i++) {
- if (transitions[i].local_from == p_name) {
- _rename_transition(transitions[i].from, String(transitions[i].from).replace_first(p_name, p_new_name));
- }
-
- if (transitions[i].local_to == p_name) {
- _rename_transition(transitions[i].to, String(transitions[i].to).replace_first(p_name, p_new_name));
- }
- }
+ _rename_transitions(p_name, p_new_name);
- emit_signal("tree_changed");
+ emit_changed();
+ emit_signal(SNAME("tree_changed"));
}
-void AnimationNodeStateMachine::_rename_transition(const StringName &p_name, const StringName &p_new_name) {
+void AnimationNodeStateMachine::_rename_transitions(const StringName &p_name, const StringName &p_new_name) {
if (updating_transitions) {
return;
}
@@ -854,12 +883,16 @@ void AnimationNodeStateMachine::_rename_transition(const StringName &p_name, con
Vector<String> path = String(transitions[i].to).split("/");
if (path.size() > 1) {
if (path[0] == "..") {
- prev_state_machine->_rename_transition(String(state_machine_name) + "/" + p_name, String(state_machine_name) + "/" + p_new_name);
+ prev_state_machine->_rename_transitions(String(state_machine_name) + "/" + p_name, String(state_machine_name) + "/" + p_new_name);
} else {
- ((Ref<AnimationNodeStateMachine>)states[transitions[i].local_to].node)->_rename_transition("../" + p_name, "../" + p_new_name);
+ ((Ref<AnimationNodeStateMachine>)states[transitions[i].local_to].node)->_rename_transitions("../" + p_name, "../" + p_new_name);
}
}
+ if (transitions[i].local_from == p_name) {
+ transitions.write[i].local_from = p_new_name;
+ }
+
transitions.write[i].from = p_new_name;
}
@@ -867,26 +900,25 @@ void AnimationNodeStateMachine::_rename_transition(const StringName &p_name, con
Vector<String> path = String(transitions[i].from).split("/");
if (path.size() > 1) {
if (path[0] == "..") {
- prev_state_machine->_rename_transition(String(state_machine_name) + "/" + p_name, String(state_machine_name) + "/" + p_new_name);
+ prev_state_machine->_rename_transitions(String(state_machine_name) + "/" + p_name, String(state_machine_name) + "/" + p_new_name);
} else {
- ((Ref<AnimationNodeStateMachine>)states[transitions[i].local_from].node)->_rename_transition("../" + p_name, "../" + p_new_name);
+ ((Ref<AnimationNodeStateMachine>)states[transitions[i].local_from].node)->_rename_transitions("../" + p_name, "../" + p_new_name);
}
}
+ if (transitions[i].local_to == p_name) {
+ transitions.write[i].local_to = p_new_name;
+ }
+
transitions.write[i].to = p_new_name;
}
-
- updating_transitions = false;
}
+ updating_transitions = false;
}
void AnimationNodeStateMachine::get_node_list(List<StringName> *r_nodes) const {
List<StringName> nodes;
for (const KeyValue<StringName, State> &E : states) {
- if (E.key == end_node && prev_state_machine == nullptr) {
- continue;
- }
-
nodes.push_back(E.key);
}
nodes.sort_custom<StringName::AlphCompare>();
@@ -943,8 +975,8 @@ bool AnimationNodeStateMachine::_can_connect(const StringName &p_name, Vector<An
return true;
}
- String name = p_name;
- Vector<String> path = name.split("/");
+ String node_name = p_name;
+ Vector<String> path = node_name.split("/");
if (path.size() < 2) {
return false;
@@ -952,12 +984,12 @@ bool AnimationNodeStateMachine::_can_connect(const StringName &p_name, Vector<An
if (path[0] == "..") {
if (prev_state_machine != nullptr) {
- return prev_state_machine->_can_connect(name.replace_first("../", ""), p_parents);
+ return prev_state_machine->_can_connect(node_name.replace_first("../", ""), p_parents);
}
} else if (states.has(path[0])) {
Ref<AnimationNodeStateMachine> anodesm = states[path[0]].node;
if (anodesm.is_valid()) {
- return anodesm->_can_connect(name.replace_first(path[0] + "/", ""), p_parents);
+ return anodesm->_can_connect(node_name.replace_first(path[0] + "/", ""), p_parents);
}
}
@@ -1032,7 +1064,7 @@ void AnimationNodeStateMachine::add_transition(const StringName &p_from, const S
tr.local_to = local_to;
tr.transition = p_transition;
- tr.transition->connect("advance_condition_changed", callable_mp(this, &AnimationNodeStateMachine::_tree_changed), varray(), CONNECT_REFERENCE_COUNTED);
+ tr.transition->connect("advance_condition_changed", callable_mp(this, &AnimationNodeStateMachine::_tree_changed), CONNECT_REFERENCE_COUNTED);
transitions.push_back(tr);
@@ -1133,11 +1165,11 @@ Vector2 AnimationNodeStateMachine::get_graph_offset() const {
return graph_offset;
}
-double AnimationNodeStateMachine::process(double p_time, bool p_seek, bool p_seek_root) {
- Ref<AnimationNodeStateMachinePlayback> playback = get_parameter(this->playback);
- ERR_FAIL_COND_V(playback.is_null(), 0.0);
+double AnimationNodeStateMachine::process(double p_time, bool p_seek, bool p_is_external_seeking) {
+ Ref<AnimationNodeStateMachinePlayback> playback_new = get_parameter(playback);
+ ERR_FAIL_COND_V(playback_new.is_null(), 0.0);
- return playback->process(this, p_time, p_seek, p_seek_root);
+ return playback_new->process(this, p_time, p_seek, p_is_external_seeking);
}
String AnimationNodeStateMachine::get_caption() const {
@@ -1161,10 +1193,10 @@ Ref<AnimationNode> AnimationNodeStateMachine::get_child_by_name(const StringName
}
bool AnimationNodeStateMachine::_set(const StringName &p_name, const Variant &p_value) {
- String name = p_name;
- if (name.begins_with("states/")) {
- String node_name = name.get_slicec('/', 1);
- String what = name.get_slicec('/', 2);
+ String prop_name = p_name;
+ if (prop_name.begins_with("states/")) {
+ String node_name = prop_name.get_slicec('/', 1);
+ String what = prop_name.get_slicec('/', 2);
if (what == "node") {
Ref<AnimationNode> anode = p_value;
@@ -1180,7 +1212,7 @@ bool AnimationNodeStateMachine::_set(const StringName &p_name, const Variant &p_
}
return true;
}
- } else if (name == "transitions") {
+ } else if (prop_name == "transitions") {
Array trans = p_value;
ERR_FAIL_COND_V(trans.size() % 3 != 0, false);
@@ -1188,7 +1220,7 @@ bool AnimationNodeStateMachine::_set(const StringName &p_name, const Variant &p_
add_transition(trans[i], trans[i + 1], trans[i + 2]);
}
return true;
- } else if (name == "graph_offset") {
+ } else if (prop_name == "graph_offset") {
set_graph_offset(p_value);
return true;
}
@@ -1197,10 +1229,10 @@ bool AnimationNodeStateMachine::_set(const StringName &p_name, const Variant &p_
}
bool AnimationNodeStateMachine::_get(const StringName &p_name, Variant &r_ret) const {
- String name = p_name;
- if (name.begins_with("states/")) {
- String node_name = name.get_slicec('/', 1);
- String what = name.get_slicec('/', 2);
+ String prop_name = p_name;
+ if (prop_name.begins_with("states/")) {
+ String node_name = prop_name.get_slicec('/', 1);
+ String what = prop_name.get_slicec('/', 2);
if (what == "node") {
if (states.has(node_name) && can_edit_node(node_name)) {
@@ -1215,7 +1247,7 @@ bool AnimationNodeStateMachine::_get(const StringName &p_name, Variant &r_ret) c
return true;
}
}
- } else if (name == "transitions") {
+ } else if (prop_name == "transitions") {
Array trans;
for (int i = 0; i < transitions.size(); i++) {
String from = transitions[i].from;
@@ -1232,7 +1264,7 @@ bool AnimationNodeStateMachine::_get(const StringName &p_name, Variant &r_ret) c
r_ret = trans;
return true;
- } else if (name == "graph_offset") {
+ } else if (prop_name == "graph_offset") {
r_ret = get_graph_offset();
return true;
}
@@ -1247,9 +1279,9 @@ void AnimationNodeStateMachine::_get_property_list(List<PropertyInfo> *p_list) c
}
names.sort_custom<StringName::AlphCompare>();
- for (const StringName &name : names) {
- p_list->push_back(PropertyInfo(Variant::OBJECT, "states/" + name + "/node", PROPERTY_HINT_RESOURCE_TYPE, "AnimationNode", PROPERTY_USAGE_NO_EDITOR));
- p_list->push_back(PropertyInfo(Variant::VECTOR2, "states/" + name + "/position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
+ for (const StringName &prop_name : names) {
+ p_list->push_back(PropertyInfo(Variant::OBJECT, "states/" + prop_name + "/node", PROPERTY_HINT_RESOURCE_TYPE, "AnimationNode", PROPERTY_USAGE_NO_EDITOR));
+ p_list->push_back(PropertyInfo(Variant::VECTOR2, "states/" + prop_name + "/position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
}
p_list->push_back(PropertyInfo(Variant::ARRAY, "transitions", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
@@ -1293,6 +1325,7 @@ Vector2 AnimationNodeStateMachine::get_node_position(const StringName &p_name) c
}
void AnimationNodeStateMachine::_tree_changed() {
+ emit_changed();
emit_signal(SNAME("tree_changed"));
}