summaryrefslogtreecommitdiff
path: root/scene/animation
diff options
context:
space:
mode:
Diffstat (limited to 'scene/animation')
-rw-r--r--scene/animation/animation_blend_space_1d.cpp25
-rw-r--r--scene/animation/animation_blend_space_1d.h7
-rw-r--r--scene/animation/animation_blend_space_2d.cpp31
-rw-r--r--scene/animation/animation_blend_space_2d.h7
-rw-r--r--scene/animation/animation_blend_tree.cpp208
-rw-r--r--scene/animation/animation_blend_tree.h108
-rw-r--r--scene/animation/animation_node_state_machine.cpp659
-rw-r--r--scene/animation/animation_node_state_machine.h55
-rw-r--r--scene/animation/animation_player.cpp89
-rw-r--r--scene/animation/animation_player.h40
-rw-r--r--scene/animation/animation_tree.cpp210
-rw-r--r--scene/animation/animation_tree.h35
-rw-r--r--scene/animation/root_motion_view.cpp13
-rw-r--r--scene/animation/tween.cpp141
-rw-r--r--scene/animation/tween.h11
15 files changed, 1053 insertions, 586 deletions
diff --git a/scene/animation/animation_blend_space_1d.cpp b/scene/animation/animation_blend_space_1d.cpp
index 849316c568..d9a5adc883 100644
--- a/scene/animation/animation_blend_space_1d.cpp
+++ b/scene/animation/animation_blend_space_1d.cpp
@@ -78,6 +78,9 @@ void AnimationNodeBlendSpace1D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_value_label", "text"), &AnimationNodeBlendSpace1D::set_value_label);
ClassDB::bind_method(D_METHOD("get_value_label"), &AnimationNodeBlendSpace1D::get_value_label);
+ ClassDB::bind_method(D_METHOD("set_use_sync", "enable"), &AnimationNodeBlendSpace1D::set_use_sync);
+ ClassDB::bind_method(D_METHOD("is_using_sync"), &AnimationNodeBlendSpace1D::is_using_sync);
+
ClassDB::bind_method(D_METHOD("_add_blend_point", "index", "node"), &AnimationNodeBlendSpace1D::_add_blend_point);
for (int i = 0; i < MAX_BLEND_POINTS; i++) {
@@ -89,6 +92,7 @@ void AnimationNodeBlendSpace1D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_space", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_max_space", "get_max_space");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "snap", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_snap", "get_snap");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "value_label", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_value_label", "get_value_label");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sync", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_use_sync", "is_using_sync");
}
void AnimationNodeBlendSpace1D::get_child_nodes(List<ChildNode> *r_child_nodes) {
@@ -211,6 +215,14 @@ String AnimationNodeBlendSpace1D::get_value_label() const {
return value_label;
}
+void AnimationNodeBlendSpace1D::set_use_sync(bool p_sync) {
+ sync = p_sync;
+}
+
+bool AnimationNodeBlendSpace1D::is_using_sync() const {
+ return sync;
+}
+
void AnimationNodeBlendSpace1D::_add_blend_point(int p_index, const Ref<AnimationRootNode> &p_node) {
if (p_index == blend_points_used) {
add_blend_point(p_node, 0);
@@ -219,14 +231,14 @@ void AnimationNodeBlendSpace1D::_add_blend_point(int p_index, const Ref<Animatio
}
}
-double AnimationNodeBlendSpace1D::process(double p_time, bool p_seek) {
+double AnimationNodeBlendSpace1D::process(double p_time, bool p_seek, bool p_seek_root) {
if (blend_points_used == 0) {
return 0.0;
}
if (blend_points_used == 1) {
// only one point available, just play that animation
- return blend_node(blend_points[0].name, blend_points[0].node, p_time, p_seek, 1.0, FILTER_IGNORE, false);
+ return blend_node(blend_points[0].name, blend_points[0].node, p_time, p_seek, p_seek_root, 1.0, FILTER_IGNORE, true);
}
double blend_pos = get_parameter(blend_position);
@@ -295,9 +307,12 @@ double AnimationNodeBlendSpace1D::process(double p_time, bool p_seek) {
double max_time_remaining = 0.0;
for (int i = 0; i < blend_points_used; i++) {
- double remaining = blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, weights[i], FILTER_IGNORE, false);
-
- max_time_remaining = MAX(max_time_remaining, remaining);
+ if (i == point_lower || i == point_higher) {
+ double remaining = blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_seek_root, weights[i], FILTER_IGNORE, true);
+ max_time_remaining = MAX(max_time_remaining, remaining);
+ } else {
+ blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_seek_root, 0, FILTER_IGNORE, sync);
+ }
}
return max_time_remaining;
diff --git a/scene/animation/animation_blend_space_1d.h b/scene/animation/animation_blend_space_1d.h
index 7038cece06..346e8a3a2f 100644
--- a/scene/animation/animation_blend_space_1d.h
+++ b/scene/animation/animation_blend_space_1d.h
@@ -63,6 +63,8 @@ class AnimationNodeBlendSpace1D : public AnimationRootNode {
StringName blend_position = "blend_position";
protected:
+ bool sync = false;
+
virtual void _validate_property(PropertyInfo &property) const override;
static void _bind_methods();
@@ -93,7 +95,10 @@ public:
void set_value_label(const String &p_label);
String get_value_label() const;
- double process(double p_time, bool p_seek) override;
+ void set_use_sync(bool p_sync);
+ bool is_using_sync() const;
+
+ double process(double p_time, bool p_seek, bool p_seek_root) override;
String get_caption() const override;
Ref<AnimationNode> get_child_by_name(const StringName &p_name) override;
diff --git a/scene/animation/animation_blend_space_2d.cpp b/scene/animation/animation_blend_space_2d.cpp
index a3aa3f6cc8..0f77befd9d 100644
--- a/scene/animation/animation_blend_space_2d.cpp
+++ b/scene/animation/animation_blend_space_2d.cpp
@@ -432,7 +432,7 @@ void AnimationNodeBlendSpace2D::_blend_triangle(const Vector2 &p_pos, const Vect
r_weights[2] = w;
}
-double AnimationNodeBlendSpace2D::process(double p_time, bool p_seek) {
+double AnimationNodeBlendSpace2D::process(double p_time, bool p_seek, bool p_seek_root) {
_update_triangles();
Vector2 blend_pos = get_parameter(blend_position);
@@ -502,7 +502,7 @@ double AnimationNodeBlendSpace2D::process(double p_time, bool p_seek) {
for (int j = 0; j < 3; j++) {
if (i == triangle_points[j]) {
//blend with the given weight
- double t = blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, blend_weights[j], FILTER_IGNORE, false);
+ double t = blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_seek_root, blend_weights[j], FILTER_IGNORE, true);
if (first || t < mind) {
mind = t;
first = false;
@@ -513,8 +513,7 @@ double AnimationNodeBlendSpace2D::process(double p_time, bool p_seek) {
}
if (!found) {
- //ignore
- blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, 0, FILTER_IGNORE, false);
+ blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_seek_root, 0, FILTER_IGNORE, sync);
}
}
} else {
@@ -539,16 +538,22 @@ double AnimationNodeBlendSpace2D::process(double p_time, bool p_seek) {
na_n->set_backward(na_c->is_backward());
}
//see how much animation remains
- from = length_internal - blend_node(blend_points[closest].name, blend_points[closest].node, p_time, false, 0.0, FILTER_IGNORE, false);
+ from = length_internal - blend_node(blend_points[closest].name, blend_points[closest].node, p_time, false, p_seek_root, 0.0, FILTER_IGNORE, true);
}
- mind = blend_node(blend_points[new_closest].name, blend_points[new_closest].node, from, true, 1.0, FILTER_IGNORE, false);
+ mind = blend_node(blend_points[new_closest].name, blend_points[new_closest].node, from, true, p_seek_root, 1.0, FILTER_IGNORE, true);
length_internal = from + mind;
closest = new_closest;
} else {
- mind = blend_node(blend_points[closest].name, blend_points[closest].node, p_time, p_seek, 1.0, FILTER_IGNORE, false);
+ mind = blend_node(blend_points[closest].name, blend_points[closest].node, p_time, p_seek, p_seek_root, 1.0, FILTER_IGNORE, true);
+ }
+
+ for (int i = 0; i < blend_points_used; i++) {
+ if (i != closest) {
+ blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_seek_root, 0, FILTER_IGNORE, sync);
+ }
}
}
@@ -604,6 +609,14 @@ AnimationNodeBlendSpace2D::BlendMode AnimationNodeBlendSpace2D::get_blend_mode()
return blend_mode;
}
+void AnimationNodeBlendSpace2D::set_use_sync(bool p_sync) {
+ sync = p_sync;
+}
+
+bool AnimationNodeBlendSpace2D::is_using_sync() const {
+ return sync;
+}
+
void AnimationNodeBlendSpace2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_blend_point", "node", "pos", "at_index"), &AnimationNodeBlendSpace2D::add_blend_point, DEFVAL(-1));
ClassDB::bind_method(D_METHOD("set_blend_point_position", "point", "pos"), &AnimationNodeBlendSpace2D::set_blend_point_position);
@@ -644,6 +657,9 @@ void AnimationNodeBlendSpace2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_blend_mode", "mode"), &AnimationNodeBlendSpace2D::set_blend_mode);
ClassDB::bind_method(D_METHOD("get_blend_mode"), &AnimationNodeBlendSpace2D::get_blend_mode);
+ ClassDB::bind_method(D_METHOD("set_use_sync", "enable"), &AnimationNodeBlendSpace2D::set_use_sync);
+ ClassDB::bind_method(D_METHOD("is_using_sync"), &AnimationNodeBlendSpace2D::is_using_sync);
+
ClassDB::bind_method(D_METHOD("_update_triangles"), &AnimationNodeBlendSpace2D::_update_triangles);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_triangles", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_auto_triangles", "get_auto_triangles");
@@ -661,6 +677,7 @@ void AnimationNodeBlendSpace2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::STRING, "x_label", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_x_label", "get_x_label");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "y_label", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_y_label", "get_y_label");
ADD_PROPERTY(PropertyInfo(Variant::INT, "blend_mode", PROPERTY_HINT_ENUM, "Interpolated,Discrete,Carry", PROPERTY_USAGE_NO_EDITOR), "set_blend_mode", "get_blend_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sync", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_use_sync", "is_using_sync");
ADD_SIGNAL(MethodInfo("triangles_updated"));
BIND_ENUM_CONSTANT(BLEND_MODE_INTERPOLATED);
diff --git a/scene/animation/animation_blend_space_2d.h b/scene/animation/animation_blend_space_2d.h
index 1356656bf8..689b96e356 100644
--- a/scene/animation/animation_blend_space_2d.h
+++ b/scene/animation/animation_blend_space_2d.h
@@ -88,6 +88,8 @@ protected:
void _tree_changed();
protected:
+ bool sync = false;
+
virtual void _validate_property(PropertyInfo &property) const override;
static void _bind_methods();
@@ -126,7 +128,7 @@ public:
void set_y_label(const String &p_label);
String get_y_label() const;
- virtual double process(double p_time, bool p_seek) override;
+ virtual double process(double p_time, bool p_seek, bool p_seek_root) override;
virtual String get_caption() const override;
Vector2 get_closest_point(const Vector2 &p_point);
@@ -137,6 +139,9 @@ public:
void set_blend_mode(BlendMode p_blend_mode);
BlendMode get_blend_mode() const;
+ void set_use_sync(bool p_sync);
+ bool is_using_sync() const;
+
virtual Ref<AnimationNode> get_child_by_name(const StringName &p_name) override;
AnimationNodeBlendSpace2D();
diff --git a/scene/animation/animation_blend_tree.cpp b/scene/animation/animation_blend_tree.cpp
index 433f21f91f..d0aac931c0 100644
--- a/scene/animation/animation_blend_tree.cpp
+++ b/scene/animation/animation_blend_tree.cpp
@@ -64,7 +64,7 @@ void AnimationNodeAnimation::_validate_property(PropertyInfo &property) const {
}
}
-double AnimationNodeAnimation::process(double p_time, bool p_seek) {
+double AnimationNodeAnimation::process(double p_time, bool p_seek, bool p_seek_root) {
AnimationPlayer *ap = state->player;
ERR_FAIL_COND_V(!ap, 0);
@@ -101,14 +101,14 @@ double AnimationNodeAnimation::process(double p_time, bool p_seek) {
}
}
- if (anim->get_loop_mode() == Animation::LoopMode::LOOP_PINGPONG) {
- if (anim_size) {
+ if (anim->get_loop_mode() == Animation::LOOP_PINGPONG) {
+ if (!Math::is_zero_approx(anim_size)) {
if ((int)Math::floor(abs(time - prev_time) / anim_size) % 2 == 0) {
- if (prev_time > 0 && time <= 0) {
+ if (prev_time >= 0 && time < 0) {
backward = !backward;
pingponged = -1;
}
- if (prev_time < anim_size && time >= anim_size) {
+ if (prev_time <= anim_size && time > anim_size) {
backward = !backward;
pingponged = 1;
}
@@ -116,22 +116,24 @@ double AnimationNodeAnimation::process(double p_time, bool p_seek) {
time = Math::pingpong(time, anim_size);
}
} else {
- if (anim->get_loop_mode() == Animation::LoopMode::LOOP_LINEAR) {
- if (anim_size) {
+ if (anim->get_loop_mode() == Animation::LOOP_LINEAR) {
+ if (!Math::is_zero_approx(anim_size)) {
time = Math::fposmod(time, anim_size);
}
} else if (time < 0) {
+ step += time;
time = 0;
} else if (time > anim_size) {
+ step += anim_size - time;
time = anim_size;
}
backward = false;
}
if (play_mode == PLAY_MODE_FORWARD) {
- blend_animation(animation, time, step, p_seek, 1.0, pingponged);
+ blend_animation(animation, time, step, p_seek, p_seek_root, 1.0, pingponged);
} else {
- blend_animation(animation, anim_size - time, -step, p_seek, 1.0, pingponged);
+ blend_animation(animation, anim_size - time, -step, p_seek, p_seek_root, 1.0, pingponged);
}
set_parameter(this->time, time);
@@ -177,6 +179,26 @@ AnimationNodeAnimation::AnimationNodeAnimation() {
////////////////////////////////////////////////////////
+void AnimationNodeSync::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_use_sync", "enable"), &AnimationNodeSync::set_use_sync);
+ ClassDB::bind_method(D_METHOD("is_using_sync"), &AnimationNodeSync::is_using_sync);
+
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sync"), "set_use_sync", "is_using_sync");
+}
+
+void AnimationNodeSync::set_use_sync(bool p_sync) {
+ sync = p_sync;
+}
+
+bool AnimationNodeSync::is_using_sync() const {
+ return sync;
+}
+
+AnimationNodeSync::AnimationNodeSync() {
+}
+
+////////////////////////////////////////////////////////
+
void AnimationNodeOneShot::get_parameter_list(List<PropertyInfo> *r_list) const {
r_list->push_back(PropertyInfo(Variant::BOOL, active));
r_list->push_back(PropertyInfo(Variant::BOOL, prev_active, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE));
@@ -251,7 +273,7 @@ bool AnimationNodeOneShot::has_filter() const {
return true;
}
-double AnimationNodeOneShot::process(double p_time, bool p_seek) {
+double AnimationNodeOneShot::process(double p_time, bool p_seek, bool p_seek_root) {
bool active = get_parameter(this->active);
bool prev_active = get_parameter(this->prev_active);
double time = get_parameter(this->time);
@@ -274,7 +296,7 @@ double AnimationNodeOneShot::process(double p_time, bool p_seek) {
}
if (!active) {
- return blend_input(0, p_time, p_seek, 1.0, FILTER_IGNORE, !sync);
+ return blend_input(0, p_time, p_seek, p_seek_root, 1.0, FILTER_IGNORE, sync);
}
}
@@ -311,12 +333,12 @@ double AnimationNodeOneShot::process(double p_time, bool p_seek) {
double main_rem;
if (mix == MIX_MODE_ADD) {
- main_rem = blend_input(0, p_time, p_seek, 1.0, FILTER_IGNORE, !sync);
+ main_rem = blend_input(0, p_time, p_seek, p_seek_root, 1.0, FILTER_IGNORE, sync);
} else {
- main_rem = blend_input(0, p_time, p_seek, 1.0 - blend, FILTER_BLEND, !sync);
+ main_rem = blend_input(0, p_time, p_seek, p_seek_root, 1.0 - blend, FILTER_BLEND, sync);
}
- double os_rem = blend_input(1, os_seek ? time : p_time, os_seek, blend, FILTER_PASS, false);
+ double os_rem = blend_input(1, os_seek ? time : p_time, os_seek, p_seek_root, blend, FILTER_PASS, true);
if (do_start) {
remaining = os_rem;
@@ -341,14 +363,6 @@ double AnimationNodeOneShot::process(double p_time, bool p_seek) {
return MAX(main_rem, remaining);
}
-void AnimationNodeOneShot::set_use_sync(bool p_sync) {
- sync = p_sync;
-}
-
-bool AnimationNodeOneShot::is_using_sync() const {
- return sync;
-}
-
void AnimationNodeOneShot::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_fadein_time", "time"), &AnimationNodeOneShot::set_fadein_time);
ClassDB::bind_method(D_METHOD("get_fadein_time"), &AnimationNodeOneShot::get_fadein_time);
@@ -368,22 +382,16 @@ void AnimationNodeOneShot::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_mix_mode", "mode"), &AnimationNodeOneShot::set_mix_mode);
ClassDB::bind_method(D_METHOD("get_mix_mode"), &AnimationNodeOneShot::get_mix_mode);
- ClassDB::bind_method(D_METHOD("set_use_sync", "enable"), &AnimationNodeOneShot::set_use_sync);
- ClassDB::bind_method(D_METHOD("is_using_sync"), &AnimationNodeOneShot::is_using_sync);
-
ADD_PROPERTY(PropertyInfo(Variant::INT, "mix_mode", PROPERTY_HINT_ENUM, "Blend,Add"), "set_mix_mode", "get_mix_mode");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fadein_time", PROPERTY_HINT_RANGE, "0,60,0.01,or_greater"), "set_fadein_time", "get_fadein_time");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fadeout_time", PROPERTY_HINT_RANGE, "0,60,0.01,or_greater"), "set_fadeout_time", "get_fadeout_time");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fadein_time", PROPERTY_HINT_RANGE, "0,60,0.01,or_greater,suffix:s"), "set_fadein_time", "get_fadein_time");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fadeout_time", PROPERTY_HINT_RANGE, "0,60,0.01,or_greater,suffix:s"), "set_fadeout_time", "get_fadeout_time");
ADD_GROUP("Auto Restart", "autorestart_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "autorestart"), "set_autorestart", "has_autorestart");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "autorestart_delay", PROPERTY_HINT_RANGE, "0,60,0.01,or_greater"), "set_autorestart_delay", "get_autorestart_delay");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "autorestart_random_delay", PROPERTY_HINT_RANGE, "0,60,0.01,or_greater"), "set_autorestart_random_delay", "get_autorestart_random_delay");
-
- ADD_GROUP("", "");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sync"), "set_use_sync", "is_using_sync");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "autorestart_delay", PROPERTY_HINT_RANGE, "0,60,0.01,or_greater,suffix:s"), "set_autorestart_delay", "get_autorestart_delay");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "autorestart_random_delay", PROPERTY_HINT_RANGE, "0,60,0.01,or_greater,suffix:s"), "set_autorestart_random_delay", "get_autorestart_random_delay");
BIND_ENUM_CONSTANT(MIX_MODE_BLEND);
BIND_ENUM_CONSTANT(MIX_MODE_ADD);
@@ -408,31 +416,19 @@ String AnimationNodeAdd2::get_caption() const {
return "Add2";
}
-void AnimationNodeAdd2::set_use_sync(bool p_sync) {
- sync = p_sync;
-}
-
-bool AnimationNodeAdd2::is_using_sync() const {
- return sync;
-}
-
bool AnimationNodeAdd2::has_filter() const {
return true;
}
-double AnimationNodeAdd2::process(double p_time, bool p_seek) {
+double AnimationNodeAdd2::process(double p_time, bool p_seek, bool p_seek_root) {
double amount = get_parameter(add_amount);
- double rem0 = blend_input(0, p_time, p_seek, 1.0, FILTER_IGNORE, !sync);
- blend_input(1, p_time, p_seek, amount, FILTER_PASS, !sync);
+ double rem0 = blend_input(0, p_time, p_seek, p_seek_root, 1.0, FILTER_IGNORE, sync);
+ blend_input(1, p_time, p_seek, p_seek_root, amount, FILTER_PASS, sync);
return rem0;
}
void AnimationNodeAdd2::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_use_sync", "enable"), &AnimationNodeAdd2::set_use_sync);
- ClassDB::bind_method(D_METHOD("is_using_sync"), &AnimationNodeAdd2::is_using_sync);
-
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sync"), "set_use_sync", "is_using_sync");
}
AnimationNodeAdd2::AnimationNodeAdd2() {
@@ -454,32 +450,20 @@ String AnimationNodeAdd3::get_caption() const {
return "Add3";
}
-void AnimationNodeAdd3::set_use_sync(bool p_sync) {
- sync = p_sync;
-}
-
-bool AnimationNodeAdd3::is_using_sync() const {
- return sync;
-}
-
bool AnimationNodeAdd3::has_filter() const {
return true;
}
-double AnimationNodeAdd3::process(double p_time, bool p_seek) {
+double AnimationNodeAdd3::process(double p_time, bool p_seek, bool p_seek_root) {
double amount = get_parameter(add_amount);
- blend_input(0, p_time, p_seek, MAX(0, -amount), FILTER_PASS, !sync);
- double rem0 = blend_input(1, p_time, p_seek, 1.0, FILTER_IGNORE, !sync);
- blend_input(2, p_time, p_seek, MAX(0, amount), FILTER_PASS, !sync);
+ blend_input(0, p_time, p_seek, p_seek_root, MAX(0, -amount), FILTER_PASS, sync);
+ double rem0 = blend_input(1, p_time, p_seek, p_seek_root, 1.0, FILTER_IGNORE, sync);
+ blend_input(2, p_time, p_seek, p_seek_root, MAX(0, amount), FILTER_PASS, sync);
return rem0;
}
void AnimationNodeAdd3::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_use_sync", "enable"), &AnimationNodeAdd3::set_use_sync);
- ClassDB::bind_method(D_METHOD("is_using_sync"), &AnimationNodeAdd3::is_using_sync);
-
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sync"), "set_use_sync", "is_using_sync");
}
AnimationNodeAdd3::AnimationNodeAdd3() {
@@ -502,32 +486,20 @@ String AnimationNodeBlend2::get_caption() const {
return "Blend2";
}
-double AnimationNodeBlend2::process(double p_time, bool p_seek) {
+double AnimationNodeBlend2::process(double p_time, bool p_seek, bool p_seek_root) {
double amount = get_parameter(blend_amount);
- double rem0 = blend_input(0, p_time, p_seek, 1.0 - amount, FILTER_BLEND, !sync);
- double rem1 = blend_input(1, p_time, p_seek, amount, FILTER_PASS, !sync);
+ double rem0 = blend_input(0, p_time, p_seek, p_seek_root, 1.0 - amount, FILTER_BLEND, sync);
+ double rem1 = blend_input(1, p_time, p_seek, p_seek_root, amount, FILTER_PASS, sync);
return amount > 0.5 ? rem1 : rem0; //hacky but good enough
}
-void AnimationNodeBlend2::set_use_sync(bool p_sync) {
- sync = p_sync;
-}
-
-bool AnimationNodeBlend2::is_using_sync() const {
- return sync;
-}
-
bool AnimationNodeBlend2::has_filter() const {
return true;
}
void AnimationNodeBlend2::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_use_sync", "enable"), &AnimationNodeBlend2::set_use_sync);
- ClassDB::bind_method(D_METHOD("is_using_sync"), &AnimationNodeBlend2::is_using_sync);
-
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sync"), "set_use_sync", "is_using_sync");
}
AnimationNodeBlend2::AnimationNodeBlend2() {
@@ -549,36 +521,22 @@ String AnimationNodeBlend3::get_caption() const {
return "Blend3";
}
-void AnimationNodeBlend3::set_use_sync(bool p_sync) {
- sync = p_sync;
-}
-
-bool AnimationNodeBlend3::is_using_sync() const {
- return sync;
-}
-
-double AnimationNodeBlend3::process(double p_time, bool p_seek) {
+double AnimationNodeBlend3::process(double p_time, bool p_seek, bool p_seek_root) {
double amount = get_parameter(blend_amount);
- double rem0 = blend_input(0, p_time, p_seek, MAX(0, -amount), FILTER_IGNORE, !sync);
- double rem1 = blend_input(1, p_time, p_seek, 1.0 - ABS(amount), FILTER_IGNORE, !sync);
- double rem2 = blend_input(2, p_time, p_seek, MAX(0, amount), FILTER_IGNORE, !sync);
+ double rem0 = blend_input(0, p_time, p_seek, p_seek_root, MAX(0, -amount), FILTER_IGNORE, sync);
+ double rem1 = blend_input(1, p_time, p_seek, p_seek_root, 1.0 - ABS(amount), FILTER_IGNORE, sync);
+ double rem2 = blend_input(2, p_time, p_seek, p_seek_root, MAX(0, amount), FILTER_IGNORE, sync);
return amount > 0.5 ? rem2 : (amount < -0.5 ? rem0 : rem1); //hacky but good enough
}
void AnimationNodeBlend3::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_use_sync", "enable"), &AnimationNodeBlend3::set_use_sync);
- ClassDB::bind_method(D_METHOD("is_using_sync"), &AnimationNodeBlend3::is_using_sync);
-
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sync"), "set_use_sync", "is_using_sync");
}
AnimationNodeBlend3::AnimationNodeBlend3() {
- blend_amount = "blend_amount";
add_input("-blend");
add_input("in");
add_input("+blend");
- sync = false;
}
/////////////////////////////////
@@ -595,12 +553,12 @@ String AnimationNodeTimeScale::get_caption() const {
return "TimeScale";
}
-double AnimationNodeTimeScale::process(double p_time, bool p_seek) {
+double AnimationNodeTimeScale::process(double p_time, bool p_seek, bool p_seek_root) {
double scale = get_parameter(this->scale);
if (p_seek) {
- return blend_input(0, p_time, true, 1.0, FILTER_IGNORE, false);
+ return blend_input(0, p_time, true, p_seek_root, 1.0, FILTER_IGNORE, true);
} else {
- return blend_input(0, p_time * scale, false, 1.0, FILTER_IGNORE, false);
+ return blend_input(0, p_time * scale, false, p_seek_root, 1.0, FILTER_IGNORE, true);
}
}
@@ -625,16 +583,16 @@ String AnimationNodeTimeSeek::get_caption() const {
return "Seek";
}
-double AnimationNodeTimeSeek::process(double p_time, bool p_seek) {
+double AnimationNodeTimeSeek::process(double p_time, bool p_seek, bool p_seek_root) {
double seek_pos = get_parameter(this->seek_pos);
if (p_seek) {
- return blend_input(0, p_time, true, 1.0, FILTER_IGNORE, false);
+ return blend_input(0, p_time, true, p_seek_root, 1.0, FILTER_IGNORE, true);
} else if (seek_pos >= 0) {
- double ret = blend_input(0, seek_pos, true, 1.0, FILTER_IGNORE, false);
+ double ret = blend_input(0, seek_pos, true, true, 1.0, FILTER_IGNORE, true);
set_parameter(this->seek_pos, -1.0); //reset
return ret;
} else {
- return blend_input(0, p_time, false, 1.0, FILTER_IGNORE, false);
+ return blend_input(0, p_time, false, p_seek_root, 1.0, FILTER_IGNORE, true);
}
}
@@ -726,7 +684,15 @@ float AnimationNodeTransition::get_cross_fade_time() const {
return xfade;
}
-double AnimationNodeTransition::process(double p_time, bool p_seek) {
+void AnimationNodeTransition::set_from_start(bool p_from_start) {
+ from_start = p_from_start;
+}
+
+bool AnimationNodeTransition::is_from_start() const {
+ return from_start;
+}
+
+double AnimationNodeTransition::process(double p_time, bool p_seek, bool p_seek_root) {
int current = get_parameter(this->current);
int prev = get_parameter(this->prev);
int prev_current = get_parameter(this->prev_current);
@@ -752,9 +718,15 @@ double AnimationNodeTransition::process(double p_time, bool p_seek) {
double rem = 0.0;
+ for (int i = 0; i < enabled_inputs; i++) {
+ if (i != current && i != prev) {
+ blend_input(i, p_time, p_seek, p_seek_root, 0, FILTER_IGNORE, sync);
+ }
+ }
+
if (prev < 0) { // process current animation, check for transition
- rem = blend_input(current, p_time, p_seek, 1.0, FILTER_IGNORE, false);
+ rem = blend_input(current, p_time, p_seek, p_seek_root, 1.0, FILTER_IGNORE, true);
if (p_seek) {
time = p_time;
@@ -770,18 +742,18 @@ double AnimationNodeTransition::process(double p_time, bool p_seek) {
float blend = xfade == 0 ? 0 : (prev_xfading / xfade);
- if (!p_seek && switched) { //just switched, seek to start of current
+ if (from_start && !p_seek && switched) { //just switched, seek to start of current
- rem = blend_input(current, 0, true, 1.0 - blend, FILTER_IGNORE, false);
+ rem = blend_input(current, 0, true, p_seek_root, 1.0 - blend, FILTER_IGNORE, true);
} else {
- rem = blend_input(current, p_time, p_seek, 1.0 - blend, FILTER_IGNORE, false);
+ rem = blend_input(current, p_time, p_seek, p_seek_root, 1.0 - blend, FILTER_IGNORE, true);
}
- if (p_seek) { // don't seek prev animation
- blend_input(prev, 0, false, blend, FILTER_IGNORE, false);
+ if (p_seek) {
+ blend_input(prev, p_time, true, p_seek_root, blend, FILTER_IGNORE, true);
time = p_time;
} else {
- blend_input(prev, p_time, false, blend, FILTER_IGNORE, false);
+ blend_input(prev, p_time, false, p_seek_root, blend, FILTER_IGNORE, true);
time += p_time;
prev_xfading -= p_time;
if (prev_xfading < 0) {
@@ -823,8 +795,12 @@ void AnimationNodeTransition::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_cross_fade_time", "time"), &AnimationNodeTransition::set_cross_fade_time);
ClassDB::bind_method(D_METHOD("get_cross_fade_time"), &AnimationNodeTransition::get_cross_fade_time);
+ ClassDB::bind_method(D_METHOD("set_from_start", "from_start"), &AnimationNodeTransition::set_from_start);
+ ClassDB::bind_method(D_METHOD("is_from_start"), &AnimationNodeTransition::is_from_start);
+
ADD_PROPERTY(PropertyInfo(Variant::INT, "input_count", PROPERTY_HINT_RANGE, "0,64,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_enabled_inputs", "get_enabled_inputs");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "xfade_time", PROPERTY_HINT_RANGE, "0,120,0.01"), "set_cross_fade_time", "get_cross_fade_time");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "xfade_time", PROPERTY_HINT_RANGE, "0,120,0.01,suffix:s"), "set_cross_fade_time", "get_cross_fade_time");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "from_start"), "set_from_start", "is_from_start");
for (int i = 0; i < MAX_INPUTS; i++) {
ADD_PROPERTYI(PropertyInfo(Variant::STRING, "input_" + itos(i) + "/name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL), "set_input_caption", "get_input_caption", i);
@@ -844,8 +820,8 @@ String AnimationNodeOutput::get_caption() const {
return "Output";
}
-double AnimationNodeOutput::process(double p_time, bool p_seek) {
- return blend_input(0, p_time, p_seek, 1.0);
+double AnimationNodeOutput::process(double p_time, bool p_seek, bool p_seek_root) {
+ return blend_input(0, p_time, p_seek, p_seek_root, 1.0, FILTER_IGNORE, true);
}
AnimationNodeOutput::AnimationNodeOutput() {
@@ -1057,9 +1033,9 @@ String AnimationNodeBlendTree::get_caption() const {
return "BlendTree";
}
-double AnimationNodeBlendTree::process(double p_time, bool p_seek) {
+double AnimationNodeBlendTree::process(double p_time, bool p_seek, bool p_seek_root) {
Ref<AnimationNodeOutput> output = nodes[SceneStringNames::get_singleton()->output].node;
- return _blend_node("output", nodes[SceneStringNames::get_singleton()->output].connections, this, output, p_time, p_seek, 1.0);
+ return _blend_node("output", nodes[SceneStringNames::get_singleton()->output].connections, this, output, p_time, p_seek, p_seek_root, 1.0, FILTER_IGNORE, true);
}
void AnimationNodeBlendTree::get_node_list(List<StringName> *r_list) {
diff --git a/scene/animation/animation_blend_tree.h b/scene/animation/animation_blend_tree.h
index 2acacd7396..d35ff04f30 100644
--- a/scene/animation/animation_blend_tree.h
+++ b/scene/animation/animation_blend_tree.h
@@ -53,7 +53,7 @@ public:
static Vector<String> (*get_editable_animation_list)();
virtual String get_caption() const override;
- virtual double process(double p_time, bool p_seek) override;
+ virtual double process(double p_time, bool p_seek, bool p_seek_root) override;
void set_animation(const StringName &p_name);
StringName get_animation() const;
@@ -77,8 +77,23 @@ private:
VARIANT_ENUM_CAST(AnimationNodeAnimation::PlayMode)
-class AnimationNodeOneShot : public AnimationNode {
- GDCLASS(AnimationNodeOneShot, AnimationNode);
+class AnimationNodeSync : public AnimationNode {
+ GDCLASS(AnimationNodeSync, AnimationNode);
+
+protected:
+ bool sync = false;
+
+ static void _bind_methods();
+
+public:
+ void set_use_sync(bool p_sync);
+ bool is_using_sync() const;
+
+ AnimationNodeSync();
+};
+
+class AnimationNodeOneShot : public AnimationNodeSync {
+ GDCLASS(AnimationNodeOneShot, AnimationNodeSync);
public:
enum MixMode {
@@ -87,22 +102,20 @@ public:
};
private:
- float fade_in = 0.1;
- float fade_out = 0.1;
+ float fade_in = 0.0;
+ float fade_out = 0.0;
bool autorestart = false;
float autorestart_delay = 1.0;
float autorestart_random_delay = 0.0;
MixMode mix = MIX_MODE_BLEND;
- bool sync = false;
-
/* bool active;
bool do_start;
float time;
float remaining;*/
- StringName active = "active";
+ StringName active = PNAME("active");
StringName prev_active = "prev_active";
StringName time = "time";
StringName remaining = "remaining";
@@ -134,22 +147,18 @@ public:
void set_mix_mode(MixMode p_mix);
MixMode get_mix_mode() const;
- void set_use_sync(bool p_sync);
- bool is_using_sync() const;
-
virtual bool has_filter() const override;
- virtual double process(double p_time, bool p_seek) override;
+ virtual double process(double p_time, bool p_seek, bool p_seek_root) override;
AnimationNodeOneShot();
};
VARIANT_ENUM_CAST(AnimationNodeOneShot::MixMode)
-class AnimationNodeAdd2 : public AnimationNode {
- GDCLASS(AnimationNodeAdd2, AnimationNode);
+class AnimationNodeAdd2 : public AnimationNodeSync {
+ GDCLASS(AnimationNodeAdd2, AnimationNodeSync);
- StringName add_amount = "add_amount";
- bool sync = false;
+ StringName add_amount = PNAME("add_amount");
protected:
static void _bind_methods();
@@ -160,20 +169,16 @@ public:
virtual String get_caption() const override;
- void set_use_sync(bool p_sync);
- bool is_using_sync() const;
-
virtual bool has_filter() const override;
- virtual double process(double p_time, bool p_seek) override;
+ virtual double process(double p_time, bool p_seek, bool p_seek_root) override;
AnimationNodeAdd2();
};
-class AnimationNodeAdd3 : public AnimationNode {
- GDCLASS(AnimationNodeAdd3, AnimationNode);
+class AnimationNodeAdd3 : public AnimationNodeSync {
+ GDCLASS(AnimationNodeAdd3, AnimationNodeSync);
- StringName add_amount = "add_amount";
- bool sync = false;
+ StringName add_amount = PNAME("add_amount");
protected:
static void _bind_methods();
@@ -184,20 +189,16 @@ public:
virtual String get_caption() const override;
- void set_use_sync(bool p_sync);
- bool is_using_sync() const;
-
virtual bool has_filter() const override;
- virtual double process(double p_time, bool p_seek) override;
+ virtual double process(double p_time, bool p_seek, bool p_seek_root) override;
AnimationNodeAdd3();
};
-class AnimationNodeBlend2 : public AnimationNode {
- GDCLASS(AnimationNodeBlend2, AnimationNode);
+class AnimationNodeBlend2 : public AnimationNodeSync {
+ GDCLASS(AnimationNodeBlend2, AnimationNodeSync);
- StringName blend_amount = "blend_amount";
- bool sync = false;
+ StringName blend_amount = PNAME("blend_amount");
protected:
static void _bind_methods();
@@ -207,20 +208,16 @@ public:
virtual Variant get_parameter_default_value(const StringName &p_parameter) const override;
virtual String get_caption() const override;
- virtual double process(double p_time, bool p_seek) override;
-
- void set_use_sync(bool p_sync);
- bool is_using_sync() const;
+ virtual double process(double p_time, bool p_seek, bool p_seek_root) override;
virtual bool has_filter() const override;
AnimationNodeBlend2();
};
-class AnimationNodeBlend3 : public AnimationNode {
- GDCLASS(AnimationNodeBlend3, AnimationNode);
+class AnimationNodeBlend3 : public AnimationNodeSync {
+ GDCLASS(AnimationNodeBlend3, AnimationNodeSync);
- StringName blend_amount;
- bool sync;
+ StringName blend_amount = PNAME("blend_amount");
protected:
static void _bind_methods();
@@ -231,17 +228,14 @@ public:
virtual String get_caption() const override;
- void set_use_sync(bool p_sync);
- bool is_using_sync() const;
-
- double process(double p_time, bool p_seek) override;
+ double process(double p_time, bool p_seek, bool p_seek_root) override;
AnimationNodeBlend3();
};
class AnimationNodeTimeScale : public AnimationNode {
GDCLASS(AnimationNodeTimeScale, AnimationNode);
- StringName scale = "scale";
+ StringName scale = PNAME("scale");
protected:
static void _bind_methods();
@@ -252,7 +246,7 @@ public:
virtual String get_caption() const override;
- double process(double p_time, bool p_seek) override;
+ double process(double p_time, bool p_seek, bool p_seek_root) override;
AnimationNodeTimeScale();
};
@@ -260,7 +254,7 @@ public:
class AnimationNodeTimeSeek : public AnimationNode {
GDCLASS(AnimationNodeTimeSeek, AnimationNode);
- StringName seek_pos = "seek_position";
+ StringName seek_pos = PNAME("seek_position");
protected:
static void _bind_methods();
@@ -271,13 +265,13 @@ public:
virtual String get_caption() const override;
- double process(double p_time, bool p_seek) override;
+ double process(double p_time, bool p_seek, bool p_seek_root) override;
AnimationNodeTimeSeek();
};
-class AnimationNodeTransition : public AnimationNode {
- GDCLASS(AnimationNodeTransition, AnimationNode);
+class AnimationNodeTransition : public AnimationNodeSync {
+ GDCLASS(AnimationNodeTransition, AnimationNodeSync);
enum {
MAX_INPUTS = 32
@@ -300,10 +294,11 @@ class AnimationNodeTransition : public AnimationNode {
StringName prev_xfading = "prev_xfading";
StringName prev = "prev";
StringName time = "time";
- StringName current = "current";
+ StringName current = PNAME("current");
StringName prev_current = "prev_current";
float xfade = 0.0;
+ bool from_start = true;
void _update_inputs();
@@ -329,7 +324,10 @@ public:
void set_cross_fade_time(float p_fade);
float get_cross_fade_time() const;
- double process(double p_time, bool p_seek) override;
+ void set_from_start(bool p_from_start);
+ bool is_from_start() const;
+
+ double process(double p_time, bool p_seek, bool p_seek_root) override;
AnimationNodeTransition();
};
@@ -339,7 +337,7 @@ class AnimationNodeOutput : public AnimationNode {
public:
virtual String get_caption() const override;
- virtual double process(double p_time, bool p_seek) override;
+ virtual double process(double p_time, bool p_seek, bool p_seek_root) override;
AnimationNodeOutput();
};
@@ -354,7 +352,7 @@ class AnimationNodeBlendTree : public AnimationRootNode {
Vector<StringName> connections;
};
- Map<StringName, Node> nodes;
+ HashMap<StringName, Node> nodes;
Vector2 graph_offset;
@@ -408,7 +406,7 @@ public:
void get_node_connections(List<NodeConnection> *r_connections) const;
virtual String get_caption() const override;
- virtual double process(double p_time, bool p_seek) override;
+ virtual double process(double p_time, bool p_seek, bool p_seek_root) override;
void get_node_list(List<StringName> *r_list);
diff --git a/scene/animation/animation_node_state_machine.cpp b/scene/animation/animation_node_state_machine.cpp
index 5ea7f4b7d9..fe08e849a1 100644
--- a/scene/animation/animation_node_state_machine.cpp
+++ b/scene/animation/animation_node_state_machine.cpp
@@ -68,6 +68,34 @@ StringName AnimationNodeStateMachineTransition::get_advance_condition_name() con
return advance_condition_name;
}
+void AnimationNodeStateMachineTransition::set_advance_expression(const String &p_expression) {
+ advance_expression = p_expression;
+
+ String advance_expression_stripped = advance_expression.strip_edges();
+ if (advance_expression_stripped == String()) {
+ expression.unref();
+ return;
+ }
+
+ if (expression.is_null()) {
+ expression.instantiate();
+ }
+
+ expression->parse(advance_expression_stripped);
+}
+
+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;
@@ -115,11 +143,22 @@ void AnimationNodeStateMachineTransition::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_priority", "priority"), &AnimationNodeStateMachineTransition::set_priority);
ClassDB::bind_method(D_METHOD("get_priority"), &AnimationNodeStateMachineTransition::get_priority);
+ 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::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::STRING_NAME, "advance_condition"), "set_advance_condition", "get_advance_condition");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "xfade_time", PROPERTY_HINT_RANGE, "0,240,0.01"), "set_xfade_time", "get_xfade_time");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "priority", PROPERTY_HINT_RANGE, "0,32,1"), "set_priority", "get_priority");
+ 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);
@@ -185,27 +224,29 @@ bool AnimationNodeStateMachinePlayback::_travel(AnimationNodeStateMachine *p_sta
return true; //nothing to do
}
- loops_current = 0; // reset loops, so fade does not happen immediately
-
Vector2 current_pos = p_state_machine->states[current].position;
Vector2 target_pos = p_state_machine->states[p_travel].position;
- Map<StringName, AStarCost> cost_map;
+ HashMap<StringName, AStarCost> cost_map;
List<int> open_list;
//build open list
for (int i = 0; i < p_state_machine->transitions.size(); i++) {
- if (p_state_machine->transitions[i].from == current) {
+ if (p_state_machine->transitions[i].transition->is_disabled()) {
+ continue;
+ }
+
+ if (p_state_machine->transitions[i].local_from == current) {
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].local_to].position.distance_to(current_pos);
cost *= p_state_machine->transitions[i].transition->get_priority();
AStarCost ap;
ap.prev = current;
ap.distance = cost;
- cost_map[p_state_machine->transitions[i].to] = ap;
+ cost_map[p_state_machine->transitions[i].local_to] = ap;
- if (p_state_machine->transitions[i].to == p_travel) { //prematurely found it! :D
+ if (p_state_machine->transitions[i].local_to == p_travel) { //prematurely found it! :D
path.push_back(p_travel);
return true;
}
@@ -224,8 +265,8 @@ bool AnimationNodeStateMachinePlayback::_travel(AnimationNodeStateMachine *p_sta
float least_cost = 1e20;
for (List<int>::Element *E = open_list.front(); E; E = E->next()) {
- float cost = cost_map[p_state_machine->transitions[E->get()].to].distance;
- cost += p_state_machine->states[p_state_machine->transitions[E->get()].to].position.distance_to(target_pos);
+ float cost = cost_map[p_state_machine->transitions[E->get()].local_to].distance;
+ cost += p_state_machine->states[p_state_machine->transitions[E->get()].local_to].position.distance_to(target_pos);
if (cost < least_cost) {
least_cost_transition = E;
@@ -233,34 +274,38 @@ bool AnimationNodeStateMachinePlayback::_travel(AnimationNodeStateMachine *p_sta
}
}
- StringName transition_prev = p_state_machine->transitions[least_cost_transition->get()].from;
- StringName transition = p_state_machine->transitions[least_cost_transition->get()].to;
+ StringName transition_prev = p_state_machine->transitions[least_cost_transition->get()].local_from;
+ 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].from != transition || p_state_machine->transitions[i].to == transition_prev) {
+ if (p_state_machine->transitions[i].transition->is_disabled()) {
+ continue;
+ }
+
+ if (p_state_machine->transitions[i].local_from != transition || p_state_machine->transitions[i].local_to == transition_prev) {
continue; //not interested on those
}
- float distance = p_state_machine->states[p_state_machine->transitions[i].from].position.distance_to(p_state_machine->states[p_state_machine->transitions[i].to].position);
+ float distance = p_state_machine->states[p_state_machine->transitions[i].local_from].position.distance_to(p_state_machine->states[p_state_machine->transitions[i].local_to].position);
distance *= p_state_machine->transitions[i].transition->get_priority();
- distance += cost_map[p_state_machine->transitions[i].from].distance;
+ distance += cost_map[p_state_machine->transitions[i].local_from].distance;
- if (cost_map.has(p_state_machine->transitions[i].to)) {
+ if (cost_map.has(p_state_machine->transitions[i].local_to)) {
//oh this was visited already, can we win the cost?
- if (distance < cost_map[p_state_machine->transitions[i].to].distance) {
- cost_map[p_state_machine->transitions[i].to].distance = distance;
- cost_map[p_state_machine->transitions[i].to].prev = p_state_machine->transitions[i].from;
+ if (distance < cost_map[p_state_machine->transitions[i].local_to].distance) {
+ cost_map[p_state_machine->transitions[i].local_to].distance = distance;
+ cost_map[p_state_machine->transitions[i].local_to].prev = p_state_machine->transitions[i].local_from;
}
} else {
//add to open list
AStarCost ac;
- ac.prev = p_state_machine->transitions[i].from;
+ ac.prev = p_state_machine->transitions[i].local_from;
ac.distance = distance;
- cost_map[p_state_machine->transitions[i].to] = ac;
+ cost_map[p_state_machine->transitions[i].local_to] = ac;
open_list.push_back(i);
- if (p_state_machine->transitions[i].to == p_travel) {
+ if (p_state_machine->transitions[i].local_to == p_travel) {
found_route = true;
break;
}
@@ -286,7 +331,7 @@ bool AnimationNodeStateMachinePlayback::_travel(AnimationNodeStateMachine *p_sta
return true;
}
-double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_state_machine, double p_time, bool p_seek) {
+double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_state_machine, double p_time, bool p_seek, bool p_seek_root) {
//if not playing and it can restart, then restart
if (!playing && start_request == StringName()) {
if (!stop_request && p_state_machine->start_node) {
@@ -350,9 +395,8 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s
current = p_state_machine->start_node;
}
- len_current = p_state_machine->blend_node(current, p_state_machine->states[current].node, 0, true, 1.0, AnimationNode::FILTER_IGNORE, false);
+ len_current = p_state_machine->blend_node(current, p_state_machine->states[current].node, 0, true, p_seek_root, 1.0, AnimationNode::FILTER_IGNORE, true);
pos_current = 0;
- loops_current = 0;
}
if (!p_state_machine->states.has(current)) {
@@ -376,10 +420,10 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s
}
}
- float rem = p_state_machine->blend_node(current, p_state_machine->states[current].node, p_time, p_seek, fade_blend, AnimationNode::FILTER_IGNORE, false);
+ 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, true);
if (fading_from != StringName()) {
- p_state_machine->blend_node(fading_from, p_state_machine->states[fading_from].node, p_time, p_seek, 1.0 - fade_blend, AnimationNode::FILTER_IGNORE, false);
+ 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, true);
}
//guess playback position
@@ -388,12 +432,8 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s
}
{ //advance and loop check
-
float next_pos = len_current - rem;
-
- if (next_pos < pos_current) {
- loops_current++;
- }
+ end_loop = next_pos < pos_current;
pos_current = next_pos; //looped
}
@@ -404,7 +444,11 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s
if (path.size()) {
for (int i = 0; i < p_state_machine->transitions.size(); i++) {
- if (p_state_machine->transitions[i].from == current && p_state_machine->transitions[i].to == path[0]) {
+ if (p_state_machine->transitions[i].transition->is_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();
switch_mode = p_state_machine->transitions[i].transition->get_switch_mode();
next = path[0];
@@ -413,17 +457,39 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s
} else {
float priority_best = 1e20;
int auto_advance_to = -1;
+
for (int i = 0; i < p_state_machine->transitions.size(); i++) {
- bool auto_advance = false;
- if (p_state_machine->transitions[i].transition->has_auto_advance()) {
- auto_advance = true;
+ if (p_state_machine->transitions[i].transition->is_disabled()) {
+ continue;
}
- StringName advance_condition_name = p_state_machine->transitions[i].transition->get_advance_condition_name();
- if (advance_condition_name != StringName() && bool(p_state_machine->get_parameter(advance_condition_name))) {
- auto_advance = true;
+
+ // handles end_node: when end_node is reached in a sub state machine, find and activate the current_transition
+ if (force_auto_advance) {
+ if (p_state_machine->transitions[i].from == current_transition.from && p_state_machine->transitions[i].to == current_transition.to) {
+ auto_advance_to = i;
+ force_auto_advance = false;
+ break;
+ }
}
- if (p_state_machine->transitions[i].from == current && auto_advance) {
+ // 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");
+
+ if (prev_playback.is_valid()) {
+ StringName prev_local_to = String(prev_playback->current_transition.next).replace_first(String(p_state_machine->state_machine_name) + "/", "");
+
+ if (p_state_machine->transitions[i].to == prev_local_to) {
+ auto_advance_to = i;
+ prev_playback->current_transition.next = StringName();
+ break;
+ }
+ }
+ }
+ }
+
+ if (p_state_machine->transitions[i].from == current && _check_advance_condition(p_state_machine, p_state_machine->transitions[i].transition)) {
if (p_state_machine->transitions[i].transition->get_priority() <= priority_best) {
priority_best = p_state_machine->transitions[i].transition->get_priority();
auto_advance_to = i;
@@ -432,26 +498,69 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s
}
if (auto_advance_to != -1) {
- next = p_state_machine->transitions[auto_advance_to].to;
+ next = p_state_machine->transitions[auto_advance_to].local_to;
+ Transition tr;
+ tr.from = String(p_state_machine->state_machine_name) + "/" + String(p_state_machine->transitions[auto_advance_to].from);
+ 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;
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();
}
}
+ if (next == p_state_machine->end_node) {
+ 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");
+
+ if (prev_playback.is_valid()) {
+ if (next_xfade) {
+ prev_playback->current_transition = current_transition;
+ prev_playback->force_auto_advance = true;
+
+ return rem;
+ }
+ float priority_best = 1e20;
+ 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()) {
+ continue;
+ }
+
+ if (current_transition.next == prev_state_machine->end_node && _check_advance_condition(prev_state_machine, prev_state_machine->transitions[i].transition)) {
+ if (prev_state_machine->transitions[i].transition->get_priority() <= priority_best) {
+ priority_best = prev_state_machine->transitions[i].transition->get_priority();
+ auto_advance_to = i;
+ }
+ }
+ }
+
+ if (auto_advance_to != -1) {
+ if (prev_state_machine->transitions[auto_advance_to].transition->get_xfade_time()) {
+ return rem;
+ }
+ }
+ }
+ }
+ }
+
//if next, see when to transition
if (next != StringName()) {
bool goto_next = false;
if (switch_mode == AnimationNodeStateMachineTransition::SWITCH_MODE_AT_END) {
- goto_next = next_xfade >= (len_current - pos_current) || loops_current > 0;
- if (loops_current > 0) {
+ goto_next = next_xfade >= (len_current - pos_current) || end_loop;
+ if (end_loop) {
next_xfade = 0;
}
} else {
goto_next = fading_from == StringName();
}
- if (goto_next) { //loops should be used because fade time may be too small or zero and animation may have looped
+ if (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
@@ -468,28 +577,71 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s
}
current = next;
if (switch_mode == AnimationNodeStateMachineTransition::SWITCH_MODE_SYNC) {
- len_current = p_state_machine->blend_node(current, p_state_machine->states[current].node, 0, true, 0, AnimationNode::FILTER_IGNORE, false);
+ len_current = p_state_machine->blend_node(current, p_state_machine->states[current].node, 0, true, p_seek_root, 0, AnimationNode::FILTER_IGNORE, true);
pos_current = MIN(pos_current, len_current);
- p_state_machine->blend_node(current, p_state_machine->states[current].node, pos_current, true, 0, AnimationNode::FILTER_IGNORE, false);
+ p_state_machine->blend_node(current, p_state_machine->states[current].node, pos_current, true, p_seek_root, 0, AnimationNode::FILTER_IGNORE, true);
} else {
- len_current = p_state_machine->blend_node(current, p_state_machine->states[current].node, 0, true, 0, AnimationNode::FILTER_IGNORE, false);
+ len_current = p_state_machine->blend_node(current, p_state_machine->states[current].node, 0, true, p_seek_root, 0, AnimationNode::FILTER_IGNORE, true);
pos_current = 0;
}
rem = len_current; //so it does not show 0 on transition
- loops_current = 0;
}
}
- //compute time left for transitions by using the end node
- if (p_state_machine->end_node != StringName() && p_state_machine->end_node != current) {
- rem = p_state_machine->blend_node(p_state_machine->end_node, p_state_machine->states[p_state_machine->end_node].node, 0, true, 0, AnimationNode::FILTER_IGNORE, false);
+ // time left must always be 1 because the end node don't length to compute
+ if (p_state_machine->end_node != current) {
+ rem = 1;
+ } else {
+ Ref<AnimationNodeStateMachinePlayback> prev_playback = p_state_machine->prev_state_machine->get_parameter("playback");
+
+ if (prev_playback.is_valid()) {
+ prev_playback->current_transition = current_transition;
+ prev_playback->force_auto_advance = true;
+ }
}
return rem;
}
+bool AnimationNodeStateMachinePlayback::_check_advance_condition(const Ref<AnimationNodeStateMachine> state_machine, const Ref<AnimationNodeStateMachineTransition> transition) const {
+ if (transition->has_auto_advance()) {
+ return true;
+ }
+
+ 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 (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();
+ }
+
+ 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;
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
void AnimationNodeStateMachinePlayback::_bind_methods() {
ClassDB::bind_method(D_METHOD("travel", "to_node"), &AnimationNodeStateMachinePlayback::travel);
ClassDB::bind_method(D_METHOD("start", "node"), &AnimationNodeStateMachinePlayback::start);
@@ -521,6 +673,23 @@ 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 {
@@ -544,6 +713,13 @@ void AnimationNodeStateMachine::add_node(const StringName &p_name, Ref<Animation
states[p_name] = state;
+ Ref<AnimationNodeStateMachine> anodesm = p_node;
+
+ if (anodesm.is_valid()) {
+ anodesm->state_machine_name = p_name;
+ anodesm->prev_state_machine = this;
+ }
+
emit_changed();
emit_signal(SNAME("tree_changed"));
@@ -570,6 +746,14 @@ void AnimationNodeStateMachine::replace_node(const StringName &p_name, Ref<Anima
p_node->connect("tree_changed", callable_mp(this, &AnimationNodeStateMachine::_tree_changed), varray(), CONNECT_REFERENCE_COUNTED);
}
+bool AnimationNodeStateMachine::can_edit_node(const StringName &p_name) const {
+ if (states.has(p_name)) {
+ return !(states[p_name].node->is_class("AnimationNodeStartState") || states[p_name].node->is_class("AnimationNodeEndState"));
+ }
+
+ return true;
+}
+
Ref<AnimationNode> AnimationNodeStateMachine::get_node(const StringName &p_name) const {
ERR_FAIL_COND_V(!states.has(p_name), Ref<AnimationNode>());
@@ -610,36 +794,24 @@ bool AnimationNodeStateMachine::has_node(const StringName &p_name) const {
void AnimationNodeStateMachine::remove_node(const StringName &p_name) {
ERR_FAIL_COND(!states.has(p_name));
- {
- Ref<AnimationNode> node = states[p_name].node;
-
- ERR_FAIL_COND(node.is_null());
-
- node->disconnect("tree_changed", callable_mp(this, &AnimationNodeStateMachine::_tree_changed));
+ if (!can_edit_node(p_name)) {
+ return;
}
- states.erase(p_name);
- //path.erase(p_name);
-
for (int i = 0; i < transitions.size(); i++) {
- if (transitions[i].from == p_name || transitions[i].to == p_name) {
- transitions.write[i].transition->disconnect("advance_condition_changed", callable_mp(this, &AnimationNodeStateMachine::_tree_changed));
- transitions.remove_at(i);
+ if (transitions[i].local_from == p_name || transitions[i].local_to == p_name) {
+ remove_transition_by_index(i);
i--;
}
}
- if (start_node == p_name) {
- start_node = StringName();
- }
-
- if (end_node == p_name) {
- end_node = StringName();
+ {
+ Ref<AnimationNode> node = states[p_name].node;
+ ERR_FAIL_COND(node.is_null());
+ node->disconnect("tree_changed", callable_mp(this, &AnimationNodeStateMachine::_tree_changed));
}
- /*if (playing && current == p_name) {
- stop();
- }*/
+ states.erase(p_name);
emit_changed();
emit_signal(SNAME("tree_changed"));
@@ -648,39 +820,73 @@ void AnimationNodeStateMachine::remove_node(const StringName &p_name) {
void AnimationNodeStateMachine::rename_node(const StringName &p_name, const StringName &p_new_name) {
ERR_FAIL_COND(!states.has(p_name));
ERR_FAIL_COND(states.has(p_new_name));
+ ERR_FAIL_COND(!can_edit_node(p_name));
states[p_new_name] = states[p_name];
states.erase(p_name);
+ Ref<AnimationNodeStateMachine> anodesm = states[p_new_name].node;
+ if (anodesm.is_valid()) {
+ anodesm->state_machine_name = p_new_name;
+ }
+
for (int i = 0; i < transitions.size(); i++) {
- if (transitions[i].from == p_name) {
- transitions.write[i].from = p_new_name;
+ 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].to == p_name) {
- transitions.write[i].to = 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));
}
}
- if (start_node == p_name) {
- start_node = p_new_name;
- }
+ emit_signal("tree_changed");
+}
- if (end_node == p_name) {
- end_node = p_new_name;
+void AnimationNodeStateMachine::_rename_transition(const StringName &p_name, const StringName &p_new_name) {
+ if (updating_transitions) {
+ return;
}
- /*if (playing && current == p_name) {
- current = p_new_name;
- }*/
+ updating_transitions = true;
+ for (int i = 0; i < transitions.size(); i++) {
+ if (transitions[i].from == p_name) {
+ 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);
+ } else {
+ ((Ref<AnimationNodeStateMachine>)states[transitions[i].local_to].node)->_rename_transition("../" + p_name, "../" + p_new_name);
+ }
+ }
- //path.clear(); //clear path
- emit_signal(SNAME("tree_changed"));
+ transitions.write[i].from = p_new_name;
+ }
+
+ if (transitions[i].to == p_name) {
+ 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);
+ } else {
+ ((Ref<AnimationNodeStateMachine>)states[transitions[i].local_from].node)->_rename_transition("../" + p_name, "../" + p_new_name);
+ }
+ }
+
+ transitions.write[i].to = p_new_name;
+ }
+
+ 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>();
@@ -690,9 +896,16 @@ void AnimationNodeStateMachine::get_node_list(List<StringName> *r_nodes) const {
}
}
+AnimationNodeStateMachine *AnimationNodeStateMachine::get_prev_state_machine() const {
+ return prev_state_machine;
+}
+
bool AnimationNodeStateMachine::has_transition(const StringName &p_from, const StringName &p_to) const {
+ StringName from = _get_shortest_path(p_from);
+ StringName to = _get_shortest_path(p_to);
+
for (int i = 0; i < transitions.size(); i++) {
- if (transitions[i].from == p_from && transitions[i].to == p_to) {
+ if (transitions[i].from == from && transitions[i].to == to) {
return true;
}
}
@@ -700,32 +913,148 @@ bool AnimationNodeStateMachine::has_transition(const StringName &p_from, const S
}
int AnimationNodeStateMachine::find_transition(const StringName &p_from, const StringName &p_to) const {
+ StringName from = _get_shortest_path(p_from);
+ StringName to = _get_shortest_path(p_to);
+
for (int i = 0; i < transitions.size(); i++) {
- if (transitions[i].from == p_from && transitions[i].to == p_to) {
+ if (transitions[i].from == from && transitions[i].to == to) {
return i;
}
}
return -1;
}
+bool AnimationNodeStateMachine::_can_connect(const StringName &p_name, Vector<AnimationNodeStateMachine *> p_parents) {
+ if (p_parents.is_empty()) {
+ AnimationNodeStateMachine *prev = this;
+ while (prev != nullptr) {
+ p_parents.push_back(prev);
+ prev = prev->prev_state_machine;
+ }
+ }
+
+ if (states.has(p_name)) {
+ Ref<AnimationNodeStateMachine> anodesm = states[p_name].node;
+
+ if (anodesm.is_valid() && p_parents.find(anodesm.ptr()) != -1) {
+ return false;
+ }
+
+ return true;
+ }
+
+ String name = p_name;
+ Vector<String> path = name.split("/");
+
+ if (path.size() < 2) {
+ return false;
+ }
+
+ if (path[0] == "..") {
+ if (prev_state_machine != nullptr) {
+ return prev_state_machine->_can_connect(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 false;
+}
+
+StringName AnimationNodeStateMachine::_get_shortest_path(const StringName &p_path) const {
+ // If p_path is something like StateMachine/../StateMachine2/State1,
+ // the result will be StateMachine2/State1. This avoid duplicate
+ // transitions when using add_transition. eg, this two calls is the same:
+ //
+ // add_transition("State1", "StateMachine/../State2", tr)
+ // add_transition("State1", "State2", tr)
+ //
+ // but the second call must be invalid because the transition already exists
+
+ Vector<String> path = String(p_path).split("/");
+ Vector<String> new_path;
+
+ for (int i = 0; i < path.size(); i++) {
+ if (i > 0 && path[i] == ".." && new_path[i - 1] != "..") {
+ new_path.remove_at(i - 1);
+ } else {
+ new_path.push_back(path[i]);
+ }
+ }
+
+ String result;
+ for (int i = 0; i < new_path.size(); i++) {
+ result += new_path[i] + "/";
+ }
+ result.remove_at(result.length() - 1);
+
+ return result;
+}
+
void AnimationNodeStateMachine::add_transition(const StringName &p_from, const StringName &p_to, const Ref<AnimationNodeStateMachineTransition> &p_transition) {
- ERR_FAIL_COND(p_from == p_to);
- ERR_FAIL_COND(!states.has(p_from));
- ERR_FAIL_COND(!states.has(p_to));
+ if (updating_transitions) {
+ return;
+ }
+
+ StringName from = _get_shortest_path(p_from);
+ StringName to = _get_shortest_path(p_to);
+ Vector<String> path_from = String(from).split("/");
+ Vector<String> path_to = String(to).split("/");
+
+ ERR_FAIL_COND(from == end_node || to == start_node);
+ ERR_FAIL_COND(from == to);
+ ERR_FAIL_COND(!_can_connect(from));
+ ERR_FAIL_COND(!_can_connect(to));
ERR_FAIL_COND(p_transition.is_null());
for (int i = 0; i < transitions.size(); i++) {
- ERR_FAIL_COND(transitions[i].from == p_from && transitions[i].to == p_to);
+ ERR_FAIL_COND(transitions[i].from == from && transitions[i].to == to);
+ }
+
+ if (path_from.size() > 1 || path_to.size() > 1) {
+ ERR_FAIL_COND(path_from[0] == path_to[0]);
}
+ updating_transitions = true;
+
+ StringName local_from = String(from).get_slicec('/', 0);
+ StringName local_to = String(to).get_slicec('/', 0);
+ local_from = local_from == ".." ? "Start" : local_from;
+ local_to = local_to == ".." ? "End" : local_to;
+
Transition tr;
- tr.from = p_from;
- tr.to = p_to;
+ tr.from = from;
+ tr.to = to;
+ tr.local_from = local_from;
+ 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);
transitions.push_back(tr);
+
+ // do recursive
+ if (path_from.size() > 1) {
+ StringName local_path = String(from).replace_first(path_from[0] + "/", "");
+ if (path_from[0] == "..") {
+ prev_state_machine->add_transition(local_path, String(state_machine_name) + "/" + to, p_transition);
+ } else {
+ ((Ref<AnimationNodeStateMachine>)states[path_from[0]].node)->add_transition(local_path, "../" + to, p_transition);
+ }
+ }
+ if (path_to.size() > 1) {
+ StringName local_path = String(to).replace_first(path_to[0] + "/", "");
+ if (path_to[0] == "..") {
+ prev_state_machine->add_transition(String(state_machine_name) + "/" + from, local_path, p_transition);
+ } else {
+ ((Ref<AnimationNodeStateMachine>)states[path_to[0]].node)->add_transition("../" + from, local_path, p_transition);
+ }
+ }
+
+ updating_transitions = false;
}
Ref<AnimationNodeStateMachineTransition> AnimationNodeStateMachine::get_transition(int p_transition) const {
@@ -748,44 +1077,52 @@ int AnimationNodeStateMachine::get_transition_count() const {
}
void AnimationNodeStateMachine::remove_transition(const StringName &p_from, const StringName &p_to) {
+ StringName from = _get_shortest_path(p_from);
+ StringName to = _get_shortest_path(p_to);
+
for (int i = 0; i < transitions.size(); i++) {
- if (transitions[i].from == p_from && transitions[i].to == p_to) {
- transitions.write[i].transition->disconnect("advance_condition_changed", callable_mp(this, &AnimationNodeStateMachine::_tree_changed));
- transitions.remove_at(i);
+ if (transitions[i].from == from && transitions[i].to == to) {
+ remove_transition_by_index(i);
return;
}
}
-
- /*if (playing) {
- path.clear();
- }*/
}
-void AnimationNodeStateMachine::remove_transition_by_index(int p_transition) {
+void AnimationNodeStateMachine::remove_transition_by_index(const int p_transition) {
ERR_FAIL_INDEX(p_transition, transitions.size());
+ Transition tr = transitions[p_transition];
transitions.write[p_transition].transition->disconnect("advance_condition_changed", callable_mp(this, &AnimationNodeStateMachine::_tree_changed));
transitions.remove_at(p_transition);
- /*if (playing) {
- path.clear();
- }*/
-}
-void AnimationNodeStateMachine::set_start_node(const StringName &p_node) {
- ERR_FAIL_COND(p_node != StringName() && !states.has(p_node));
- start_node = p_node;
-}
+ Vector<String> path_from = String(tr.from).split("/");
+ Vector<String> path_to = String(tr.to).split("/");
-String AnimationNodeStateMachine::get_start_node() const {
- return start_node;
-}
+ List<Vector<String>> paths;
+ paths.push_back(path_from);
+ paths.push_back(path_to);
+
+ for (List<Vector<String>>::Element *E = paths.front(); E; E = E->next()) {
+ if (E->get()[0].size() > 1) {
+ if (E->get()[0] == "..") {
+ prev_state_machine->_remove_transition(tr.transition);
+ } else if (states.has(E->get()[0])) {
+ Ref<AnimationNodeStateMachine> anodesm = states[E->get()[0]].node;
-void AnimationNodeStateMachine::set_end_node(const StringName &p_node) {
- ERR_FAIL_COND(p_node != StringName() && !states.has(p_node));
- end_node = p_node;
+ if (anodesm.is_valid()) {
+ anodesm->_remove_transition(tr.transition);
+ }
+ }
+ }
+ }
}
-String AnimationNodeStateMachine::get_end_node() const {
- return end_node;
+void AnimationNodeStateMachine::_remove_transition(const Ref<AnimationNodeStateMachineTransition> p_transition) {
+ for (int i = 0; i < transitions.size(); i++) {
+ if (transitions[i].transition == p_transition) {
+ remove_transition_by_index(i);
+ return;
+ }
+ }
}
void AnimationNodeStateMachine::set_graph_offset(const Vector2 &p_offset) {
@@ -796,17 +1133,29 @@ Vector2 AnimationNodeStateMachine::get_graph_offset() const {
return graph_offset;
}
-double AnimationNodeStateMachine::process(double p_time, bool p_seek) {
+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);
- return playback->process(this, p_time, p_seek);
+ return playback->process(this, p_time, p_seek, p_seek_root);
}
String AnimationNodeStateMachine::get_caption() const {
return "StateMachine";
}
+bool AnimationNodeStateMachine::has_local_transition(const StringName &p_from, const StringName &p_to) const {
+ StringName from = _get_shortest_path(p_from);
+ StringName to = _get_shortest_path(p_to);
+
+ for (int i = 0; i < transitions.size(); i++) {
+ if (transitions[i].local_from == from && transitions[i].local_to == to) {
+ return true;
+ }
+ }
+ return false;
+}
+
Ref<AnimationNode> AnimationNodeStateMachine::get_child_by_name(const StringName &p_name) {
return get_node(p_name);
}
@@ -839,12 +1188,6 @@ 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 == "start_node") {
- set_start_node(p_value);
- return true;
- } else if (name == "end_node") {
- set_end_node(p_value);
- return true;
} else if (name == "graph_offset") {
set_graph_offset(p_value);
return true;
@@ -860,7 +1203,7 @@ bool AnimationNodeStateMachine::_get(const StringName &p_name, Variant &r_ret) c
String what = name.get_slicec('/', 2);
if (what == "node") {
- if (states.has(node_name)) {
+ if (states.has(node_name) && can_edit_node(node_name)) {
r_ret = states[node_name].node;
return true;
}
@@ -874,22 +1217,21 @@ bool AnimationNodeStateMachine::_get(const StringName &p_name, Variant &r_ret) c
}
} else if (name == "transitions") {
Array trans;
- trans.resize(transitions.size() * 3);
-
for (int i = 0; i < transitions.size(); i++) {
- trans[i * 3 + 0] = transitions[i].from;
- trans[i * 3 + 1] = transitions[i].to;
- trans[i * 3 + 2] = transitions[i].transition;
+ String from = transitions[i].from;
+ String to = transitions[i].to;
+
+ if (from.get_slicec('/', 0) == ".." || to.get_slicec('/', 0) == "..") {
+ continue;
+ }
+
+ trans.push_back(from);
+ trans.push_back(to);
+ trans.push_back(transitions[i].transition);
}
r_ret = trans;
return true;
- } else if (name == "start_node") {
- r_ret = get_start_node();
- return true;
- } else if (name == "end_node") {
- r_ret = get_end_node();
- return true;
} else if (name == "graph_offset") {
r_ret = get_graph_offset();
return true;
@@ -911,8 +1253,6 @@ void AnimationNodeStateMachine::_get_property_list(List<PropertyInfo> *p_list) c
}
p_list->push_back(PropertyInfo(Variant::ARRAY, "transitions", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
- p_list->push_back(PropertyInfo(Variant::STRING_NAME, "start_node", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
- p_list->push_back(PropertyInfo(Variant::STRING_NAME, "end_node", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
p_list->push_back(PropertyInfo(Variant::VECTOR2, "graph_offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
}
@@ -920,10 +1260,24 @@ void AnimationNodeStateMachine::reset_state() {
states.clear();
transitions.clear();
playback = "playback";
- start_node = StringName();
- end_node = StringName();
+ start_node = "Start";
+ end_node = "End";
graph_offset = Vector2();
+ Ref<AnimationNodeStartState> s;
+ s.instantiate();
+ State start;
+ start.node = s;
+ start.position = Vector2(200, 100);
+ states[start_node] = start;
+
+ Ref<AnimationNodeEndState> e;
+ e.instantiate();
+ State end;
+ end.node = e;
+ end.position = Vector2(900, 100);
+ states[end_node] = end;
+
emit_changed();
emit_signal(SNAME("tree_changed"));
}
@@ -963,15 +1317,22 @@ void AnimationNodeStateMachine::_bind_methods() {
ClassDB::bind_method(D_METHOD("remove_transition_by_index", "idx"), &AnimationNodeStateMachine::remove_transition_by_index);
ClassDB::bind_method(D_METHOD("remove_transition", "from", "to"), &AnimationNodeStateMachine::remove_transition);
- ClassDB::bind_method(D_METHOD("set_start_node", "name"), &AnimationNodeStateMachine::set_start_node);
- ClassDB::bind_method(D_METHOD("get_start_node"), &AnimationNodeStateMachine::get_start_node);
-
- ClassDB::bind_method(D_METHOD("set_end_node", "name"), &AnimationNodeStateMachine::set_end_node);
- ClassDB::bind_method(D_METHOD("get_end_node"), &AnimationNodeStateMachine::get_end_node);
-
ClassDB::bind_method(D_METHOD("set_graph_offset", "offset"), &AnimationNodeStateMachine::set_graph_offset);
ClassDB::bind_method(D_METHOD("get_graph_offset"), &AnimationNodeStateMachine::get_graph_offset);
}
AnimationNodeStateMachine::AnimationNodeStateMachine() {
+ Ref<AnimationNodeStartState> s;
+ s.instantiate();
+ State start;
+ start.node = s;
+ start.position = Vector2(200, 100);
+ states[start_node] = start;
+
+ Ref<AnimationNodeEndState> e;
+ e.instantiate();
+ State end;
+ end.node = e;
+ end.position = Vector2(900, 100);
+ states[end_node] = end;
}
diff --git a/scene/animation/animation_node_state_machine.h b/scene/animation/animation_node_state_machine.h
index 3bae0fcffa..d4e58ca3d3 100644
--- a/scene/animation/animation_node_state_machine.h
+++ b/scene/animation/animation_node_state_machine.h
@@ -31,6 +31,7 @@
#ifndef ANIMATION_NODE_STATE_MACHINE_H
#define ANIMATION_NODE_STATE_MACHINE_H
+#include "core/math/expression.h"
#include "scene/animation/animation_tree.h"
class AnimationNodeStateMachineTransition : public Resource {
@@ -51,6 +52,11 @@ private:
float xfade = 0.0;
bool disabled = false;
int priority = 1;
+ String advance_expression;
+ NodePath advance_expression_base_node;
+
+ friend class AnimationNodeStateMachinePlayback;
+ Ref<Expression> expression;
protected:
static void _bind_methods();
@@ -67,6 +73,12 @@ public:
StringName get_advance_condition_name() const;
+ void set_advance_expression(const String &p_expression);
+ String get_advance_expression() const;
+
+ void set_advance_expression_base_node(const NodePath &p_expression_base_node);
+ NodePath get_advance_expression_base_node() const;
+
void set_xfade_time(float p_xfade);
float get_xfade_time() const;
@@ -93,13 +105,19 @@ class AnimationNodeStateMachinePlayback : public Resource {
StringName prev;
};
- float len_total = 0.0;
+ struct Transition {
+ StringName from;
+ StringName to;
+ StringName next;
+ };
float len_current = 0.0;
float pos_current = 0.0;
- int loops_current = 0;
+ bool end_loop = false;
StringName current;
+ Transition current_transition;
+ bool force_auto_advance = false;
StringName fading_from;
float fading_time = 0.0;
@@ -114,7 +132,9 @@ class AnimationNodeStateMachinePlayback : public Resource {
bool _travel(AnimationNodeStateMachine *p_state_machine, const StringName &p_travel);
- double process(AnimationNodeStateMachine *p_state_machine, double p_time, bool p_seek);
+ double process(AnimationNodeStateMachine *p_state_machine, double p_time, bool p_seek, bool p_seek_root);
+
+ bool _check_advance_condition(const Ref<AnimationNodeStateMachine> p_state_machine, const Ref<AnimationNodeStateMachineTransition> p_transition) const;
protected:
static void _bind_methods();
@@ -144,24 +164,30 @@ private:
Vector2 position;
};
- Map<StringName, State> states;
+ HashMap<StringName, State> states;
struct Transition {
StringName from;
StringName to;
+ StringName local_from;
+ StringName local_to;
Ref<AnimationNodeStateMachineTransition> transition;
};
Vector<Transition> transitions;
StringName playback = "playback";
-
- StringName start_node;
- StringName end_node;
+ StringName state_machine_name;
+ AnimationNodeStateMachine *prev_state_machine = nullptr;
+ bool updating_transitions = false;
Vector2 graph_offset;
void _tree_changed();
+ void _remove_transition(const Ref<AnimationNodeStateMachineTransition> p_transition);
+ void _rename_transition(const StringName &p_name, const StringName &p_new_name);
+ bool _can_connect(const StringName &p_name, Vector<AnimationNodeStateMachine *> p_parents = Vector<AnimationNodeStateMachine *>());
+ StringName _get_shortest_path(const StringName &p_path) const;
protected:
static void _bind_methods();
@@ -169,10 +195,14 @@ protected:
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
void _get_property_list(List<PropertyInfo> *p_list) const;
+ bool _check_advance_condition(const Ref<AnimationNodeStateMachine> p_state_machine, const Ref<AnimationNodeStateMachineTransition> p_transition) const;
virtual void reset_state() override;
public:
+ StringName start_node = "Start";
+ StringName end_node = "End";
+
virtual void get_parameter_list(List<PropertyInfo> *r_list) const override;
virtual Variant get_parameter_default_value(const StringName &p_parameter) const override;
@@ -191,25 +221,24 @@ public:
virtual void get_child_nodes(List<ChildNode> *r_child_nodes) override;
bool has_transition(const StringName &p_from, const StringName &p_to) const;
+ bool has_local_transition(const StringName &p_from, const StringName &p_to) const;
int find_transition(const StringName &p_from, const StringName &p_to) const;
void add_transition(const StringName &p_from, const StringName &p_to, const Ref<AnimationNodeStateMachineTransition> &p_transition);
Ref<AnimationNodeStateMachineTransition> get_transition(int p_transition) const;
StringName get_transition_from(int p_transition) const;
StringName get_transition_to(int p_transition) const;
int get_transition_count() const;
- void remove_transition_by_index(int p_transition);
+ void remove_transition_by_index(const int p_transition);
void remove_transition(const StringName &p_from, const StringName &p_to);
- void set_start_node(const StringName &p_node);
- String get_start_node() const;
+ bool can_edit_node(const StringName &p_name) const;
- void set_end_node(const StringName &p_node);
- String get_end_node() const;
+ AnimationNodeStateMachine *get_prev_state_machine() const;
void set_graph_offset(const Vector2 &p_offset);
Vector2 get_graph_offset() const;
- virtual double process(double p_time, bool p_seek) override;
+ virtual double process(double p_time, bool p_seek, bool p_seek_root) override;
virtual String get_caption() const override;
virtual Ref<AnimationNode> get_child_by_name(const StringName &p_name) override;
diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp
index 6949e3681c..2e87dbf9da 100644
--- a/scene/animation/animation_player.cpp
+++ b/scene/animation/animation_player.cpp
@@ -163,7 +163,7 @@ bool AnimationPlayer::_get(const StringName &p_name, Variant &r_ret) const {
for (int i = 0; i < keys.size(); i++) {
array.push_back(keys[i].from);
array.push_back(keys[i].to);
- array.push_back(blend_times[keys[i]]);
+ array.push_back(blend_times.get(keys[i]));
}
r_ret = array;
@@ -284,7 +284,12 @@ void AnimationPlayer::_ensure_node_caches(AnimationData *p_anim, Node *p_root_ov
for (int i = 0; i < a->get_track_count(); i++) {
p_anim->node_cache.write[i] = nullptr;
- RES resource;
+
+ if (!a->track_is_enabled(i)) {
+ continue;
+ }
+
+ Ref<Resource> resource;
Vector<StringName> leftover_path;
Node *child = parent->get_node_and_resource(a->track_get_path(i), resource, leftover_path);
ERR_CONTINUE_MSG(!child, "On Animation: '" + p_anim->name + "', couldn't resolve track: '" + String(a->track_get_path(i)) + "'."); // couldn't find the child node
@@ -588,10 +593,10 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
//StringName property=a->track_get_path(i).get_property();
- Map<StringName, TrackNodeCache::PropertyAnim>::Element *E = nc->property_anim.find(a->track_get_path(i).get_concatenated_subnames());
+ HashMap<StringName, TrackNodeCache::PropertyAnim>::Iterator E = nc->property_anim.find(a->track_get_path(i).get_concatenated_subnames());
ERR_CONTINUE(!E); //should it continue, or create a new one?
- TrackNodeCache::PropertyAnim *pa = &E->get();
+ TrackNodeCache::PropertyAnim *pa = &E->value;
Animation::UpdateMode update_mode = a->value_track_get_update_mode(i);
@@ -738,10 +743,10 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
continue;
}
- Map<StringName, TrackNodeCache::BezierAnim>::Element *E = nc->bezier_anim.find(a->track_get_path(i).get_concatenated_subnames());
+ HashMap<StringName, TrackNodeCache::BezierAnim>::Iterator E = nc->bezier_anim.find(a->track_get_path(i).get_concatenated_subnames());
ERR_CONTINUE(!E); //should it continue, or create a new one?
- TrackNodeCache::BezierAnim *ba = &E->get();
+ TrackNodeCache::BezierAnim *ba = &E->value;
real_t bezier = a->bezier_track_interpolate(i, p_time);
if (ba->accum_pass != accum_pass) {
@@ -832,7 +837,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
nc->audio_start = p_time;
}
} else if (nc->audio_playing) {
- bool loop = a->get_loop_mode() != Animation::LoopMode::LOOP_NONE;
+ bool loop = a->get_loop_mode() != Animation::LOOP_NONE;
bool stop = false;
@@ -883,15 +888,15 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
double at_anim_pos = 0.0;
switch (anim->get_loop_mode()) {
- case Animation::LoopMode::LOOP_NONE: {
+ case Animation::LOOP_NONE: {
at_anim_pos = MIN((double)anim->get_length(), p_time - pos); //seek to end
} break;
- case Animation::LoopMode::LOOP_LINEAR: {
+ case Animation::LOOP_LINEAR: {
at_anim_pos = Math::fposmod(p_time - pos, (double)anim->get_length()); //seek to loop
} break;
- case Animation::LoopMode::LOOP_PINGPONG: {
+ case Animation::LOOP_PINGPONG: {
at_anim_pos = Math::pingpong(p_time - pos, (double)anim->get_length());
} break;
@@ -944,7 +949,7 @@ void AnimationPlayer::_animation_process_data(PlaybackData &cd, double p_delta,
int pingponged = 0;
switch (cd.from->animation->get_loop_mode()) {
- case Animation::LoopMode::LOOP_NONE: {
+ case Animation::LOOP_NONE: {
if (next_pos < 0) {
next_pos = 0;
} else if (next_pos > len) {
@@ -969,7 +974,7 @@ void AnimationPlayer::_animation_process_data(PlaybackData &cd, double p_delta,
}
} break;
- case Animation::LoopMode::LOOP_LINEAR: {
+ case Animation::LOOP_LINEAR: {
double looped_next_pos = Math::fposmod(next_pos, (double)len);
if (looped_next_pos == 0 && next_pos != 0) {
// Loop multiples of the length to it, rather than 0
@@ -980,7 +985,7 @@ void AnimationPlayer::_animation_process_data(PlaybackData &cd, double p_delta,
}
} break;
- case Animation::LoopMode::LOOP_PINGPONG: {
+ case Animation::LOOP_PINGPONG: {
if ((int)Math::floor(abs(next_pos - cd.pos) / len) % 2 == 0) {
if (next_pos < 0 && cd.pos >= 0) {
cd.speed_scale *= -1.0;
@@ -1240,8 +1245,6 @@ void AnimationPlayer::_animation_set_cache_update() {
void AnimationPlayer::_animation_added(const StringName &p_name, const StringName &p_library) {
_animation_set_cache_update();
-
- update_configuration_warnings();
}
void AnimationPlayer::_animation_removed(const StringName &p_name, const StringName &p_library) {
@@ -1265,14 +1268,12 @@ void AnimationPlayer::_animation_removed(const StringName &p_name, const StringN
blend_times.erase(to_erase.front()->get());
to_erase.pop_front();
}
-
- update_configuration_warnings();
}
void AnimationPlayer::_rename_animation(const StringName &p_from_name, const StringName &p_to_name) {
// Rename autoplay or blends if needed.
List<BlendKey> to_erase;
- Map<BlendKey, float> to_insert;
+ HashMap<BlendKey, float, BlendKey> to_insert;
for (const KeyValue<BlendKey, float> &E : blend_times) {
BlendKey bk = E.key;
BlendKey new_bk = bk;
@@ -1298,8 +1299,8 @@ void AnimationPlayer::_rename_animation(const StringName &p_from_name, const Str
}
while (to_insert.size()) {
- blend_times[to_insert.front()->key()] = to_insert.front()->get();
- to_insert.erase(to_insert.front());
+ blend_times[to_insert.begin()->key] = to_insert.begin()->value;
+ to_insert.remove(to_insert.begin());
}
if (autoplay == p_from_name) {
@@ -1317,7 +1318,6 @@ void AnimationPlayer::_animation_renamed(const StringName &p_name, const StringN
_animation_set_cache_update();
_rename_animation(from_name, to_name);
- update_configuration_warnings();
}
Error AnimationPlayer::add_animation_library(const StringName &p_name, const Ref<AnimationLibrary> &p_animation_library) {
@@ -1353,7 +1353,6 @@ Error AnimationPlayer::add_animation_library(const StringName &p_name, const Ref
notify_property_list_changed();
- update_configuration_warnings();
return OK;
}
@@ -1383,7 +1382,6 @@ void AnimationPlayer::remove_animation_library(const StringName &p_name) {
_animation_set_cache_update();
notify_property_list_changed();
- update_configuration_warnings();
}
void AnimationPlayer::_ref_anim(const Ref<Animation> &p_anim) {
@@ -1469,25 +1467,12 @@ void AnimationPlayer::get_animation_library_list(List<StringName> *p_libraries)
}
}
-TypedArray<String> AnimationPlayer::get_configuration_warnings() const {
- TypedArray<String> warnings = Node::get_configuration_warnings();
-
- for (uint32_t i = 0; i < animation_libraries.size(); i++) {
- for (const KeyValue<StringName, Ref<Animation>> &K : animation_libraries[i].library->animations) {
- if (animation_set.has(K.key) && animation_set[K.key].animation_library != animation_libraries[i].name) {
- warnings.push_back(vformat(RTR("Animation '%s' in library '%s' is unused because another animation with the same name exists in library '%s'."), K.key, animation_libraries[i].name, animation_set[K.key].animation_library));
- }
- }
- }
- return warnings;
-}
-
bool AnimationPlayer::has_animation(const StringName &p_name) const {
return animation_set.has(p_name);
}
Ref<Animation> AnimationPlayer::get_animation(const StringName &p_name) const {
- ERR_FAIL_COND_V(!animation_set.has(p_name), Ref<Animation>());
+ ERR_FAIL_COND_V_MSG(!animation_set.has(p_name), Ref<Animation>(), vformat("Animation not found: %s.", p_name));
const AnimationData &data = animation_set[p_name];
@@ -1509,8 +1494,8 @@ void AnimationPlayer::get_animation_list(List<StringName> *p_animations) const {
}
void AnimationPlayer::set_blend_time(const StringName &p_animation1, const StringName &p_animation2, float p_time) {
- ERR_FAIL_COND(!animation_set.has(p_animation1));
- ERR_FAIL_COND(!animation_set.has(p_animation2));
+ ERR_FAIL_COND_MSG(!animation_set.has(p_animation1), vformat("Animation not found: %s.", p_animation1));
+ ERR_FAIL_COND_MSG(!animation_set.has(p_animation2), vformat("Animation not found: %s.", p_animation2));
ERR_FAIL_COND_MSG(p_time < 0, "Blend time cannot be smaller than 0.");
BlendKey bk;
@@ -1567,7 +1552,7 @@ void AnimationPlayer::play(const StringName &p_name, float p_custom_blend, float
name = playback.assigned;
}
- ERR_FAIL_COND_MSG(!animation_set.has(name), "Animation not found: " + name + ".");
+ ERR_FAIL_COND_MSG(!animation_set.has(name), vformat("Animation not found: %s.", name));
Playback &c = playback;
@@ -1670,7 +1655,7 @@ void AnimationPlayer::set_assigned_animation(const String &p_anim) {
if (is_playing()) {
play(p_anim);
} else {
- ERR_FAIL_COND(!animation_set.has(p_anim));
+ ERR_FAIL_COND_MSG(!animation_set.has(p_anim), vformat("Animation not found: %s.", p_anim));
playback.current.pos = 0;
playback.current.from = &animation_set[p_anim];
playback.assigned = p_anim;
@@ -1713,7 +1698,7 @@ float AnimationPlayer::get_playing_speed() const {
void AnimationPlayer::seek(double p_time, bool p_update) {
if (!playback.current.from) {
if (playback.assigned) {
- ERR_FAIL_COND(!animation_set.has(playback.assigned));
+ ERR_FAIL_COND_MSG(!animation_set.has(playback.assigned), vformat("Animation not found: %s.", playback.assigned));
playback.current.from = &animation_set[playback.assigned];
}
ERR_FAIL_COND(!playback.current.from);
@@ -1729,7 +1714,7 @@ void AnimationPlayer::seek(double p_time, bool p_update) {
void AnimationPlayer::seek_delta(double p_time, float p_delta) {
if (!playback.current.from) {
if (playback.assigned) {
- ERR_FAIL_COND(!animation_set.has(playback.assigned));
+ ERR_FAIL_COND_MSG(!animation_set.has(playback.assigned), vformat("Animation not found: %s.", playback.assigned));
playback.current.from = &animation_set[playback.assigned];
}
ERR_FAIL_COND(!playback.current.from);
@@ -1766,12 +1751,12 @@ void AnimationPlayer::_animation_changed() {
}
void AnimationPlayer::_stop_playing_caches() {
- for (Set<TrackNodeCache *>::Element *E = playing_caches.front(); E; E = E->next()) {
- if (E->get()->node && E->get()->audio_playing) {
- E->get()->node->call(SNAME("stop"));
+ for (TrackNodeCache *E : playing_caches) {
+ if (E->node && E->audio_playing) {
+ E->node->call(SNAME("stop"));
}
- if (E->get()->node && E->get()->animation_playing) {
- AnimationPlayer *player = Object::cast_to<AnimationPlayer>(E->get()->node);
+ if (E->node && E->animation_playing) {
+ AnimationPlayer *player = Object::cast_to<AnimationPlayer>(E->node);
if (!player) {
continue;
}
@@ -1899,7 +1884,7 @@ void AnimationPlayer::_set_process(bool p_process, bool p_force) {
}
void AnimationPlayer::animation_set_next(const StringName &p_animation, const StringName &p_next) {
- ERR_FAIL_COND(!animation_set.has(p_animation));
+ ERR_FAIL_COND_MSG(!animation_set.has(p_animation), vformat("Animation not found: %s.", p_animation));
animation_set[p_animation].next = p_next;
}
@@ -1929,7 +1914,7 @@ NodePath AnimationPlayer::get_root() const {
void AnimationPlayer::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const {
String pf = p_function;
- if (p_idx == 0 && (p_function == "play" || p_function == "play_backwards" || p_function == "remove_animation" || p_function == "has_animation" || p_function == "queue")) {
+ if (p_idx == 0 && (p_function == "play" || p_function == "play_backwards" || p_function == "has_animation" || p_function == "queue")) {
List<StringName> al;
get_animation_list(&al);
for (const StringName &name : al) {
@@ -2011,7 +1996,7 @@ Ref<AnimatedValuesBackup> AnimationPlayer::apply_reset(bool p_user_initiated) {
Ref<AnimationLibrary> al;
al.instantiate();
al->add_animation(SceneStringNames::get_singleton()->RESET, reset_anim);
- aux_player->add_animation_library("default", al);
+ aux_player->add_animation_library("", al);
aux_player->set_assigned_animation(SceneStringNames::get_singleton()->RESET);
// Forcing the use of the original root because the scene where original player belongs may be not the active one
Node *root = get_node(get_root());
@@ -2115,7 +2100,7 @@ void AnimationPlayer::_bind_methods() {
ADD_GROUP("Playback Options", "playback_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "playback_process_mode", PROPERTY_HINT_ENUM, "Physics,Idle,Manual"), "set_process_callback", "get_process_callback");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "playback_default_blend_time", PROPERTY_HINT_RANGE, "0,4096,0.01"), "set_default_blend_time", "get_default_blend_time");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "playback_default_blend_time", PROPERTY_HINT_RANGE, "0,4096,0.01,suffix:s"), "set_default_blend_time", "get_default_blend_time");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "playback_active", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_active", "is_active");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "playback_speed", PROPERTY_HINT_RANGE, "-64,64,0.01"), "set_speed_scale", "get_speed_scale");
ADD_PROPERTY(PropertyInfo(Variant::INT, "method_call_mode", PROPERTY_HINT_ENUM, "Deferred,Immediate"), "set_method_call_mode", "get_method_call_mode");
diff --git a/scene/animation/animation_player.h b/scene/animation/animation_player.h
index 1d450175ad..d3eb37a345 100644
--- a/scene/animation/animation_player.h
+++ b/scene/animation/animation_player.h
@@ -94,7 +94,7 @@ private:
struct TrackNodeCache {
NodePath path;
uint32_t id = 0;
- RES resource;
+ Ref<Resource> resource;
Node *node = nullptr;
Node2D *node_2d = nullptr;
#ifndef _3D_DISABLED
@@ -132,7 +132,7 @@ private:
Variant capture;
};
- Map<StringName, PropertyAnim> property_anim;
+ HashMap<StringName, PropertyAnim> property_anim;
struct BezierAnim {
Vector<StringName> bezier_property;
@@ -142,7 +142,7 @@ private:
uint64_t accum_pass = 0;
};
- Map<StringName, BezierAnim> bezier_anim;
+ HashMap<StringName, BezierAnim> bezier_anim;
uint32_t last_setup_pass = 0;
TrackNodeCache() {}
@@ -153,6 +153,16 @@ private:
int bone_idx = -1;
int blend_shape_idx = -1;
+ static uint32_t hash(const TrackNodeCacheKey &p_key) {
+ uint32_t h = hash_one_uint64(p_key.id);
+ h = hash_murmur3_one_32(p_key.bone_idx, h);
+ return hash_fmix32(hash_murmur3_one_32(p_key.blend_shape_idx, h));
+ }
+
+ inline bool operator==(const TrackNodeCacheKey &p_right) const {
+ return id == p_right.id && bone_idx == p_right.bone_idx && blend_shape_idx == p_right.blend_shape_idx;
+ }
+
inline bool operator<(const TrackNodeCacheKey &p_right) const {
if (id == p_right.id) {
if (blend_shape_idx == p_right.blend_shape_idx) {
@@ -166,7 +176,7 @@ private:
}
};
- Map<TrackNodeCacheKey, TrackNodeCache> node_cache_map;
+ HashMap<TrackNodeCacheKey, TrackNodeCache, TrackNodeCacheKey> node_cache_map;
TrackNodeCache *cache_update[NODE_CACHE_UPDATE_MAX];
int cache_update_size = 0;
@@ -174,7 +184,7 @@ private:
int cache_update_prop_size = 0;
TrackNodeCache::BezierAnim *cache_update_bezier[NODE_CACHE_UPDATE_MAX];
int cache_update_bezier_size = 0;
- Set<TrackNodeCache *> playing_caches;
+ HashSet<TrackNodeCache *> playing_caches;
uint64_t accum_pass = 1;
float speed_scale = 1.0;
@@ -189,7 +199,7 @@ private:
uint64_t last_update = 0;
};
- Map<StringName, AnimationData> animation_set;
+ HashMap<StringName, AnimationData> animation_set;
struct AnimationLibraryData {
StringName name;
@@ -202,10 +212,22 @@ private:
struct BlendKey {
StringName from;
StringName to;
- bool operator<(const BlendKey &bk) const { return from == bk.from ? String(to) < String(bk.to) : String(from) < String(bk.from); }
+ static uint32_t hash(const BlendKey &p_key) {
+ return hash_one_uint64((uint64_t(p_key.from.hash()) << 32) | uint32_t(p_key.to.hash()));
+ }
+ bool operator==(const BlendKey &bk) const {
+ return from == bk.from && to == bk.to;
+ }
+ bool operator<(const BlendKey &bk) const {
+ if (from == bk.from) {
+ return to < bk.to;
+ } else {
+ return from < bk.from;
+ }
+ }
};
- Map<BlendKey, float> blend_times;
+ HashMap<BlendKey, float, BlendKey> blend_times;
struct PlaybackData {
AnimationData *from = nullptr;
@@ -366,8 +388,6 @@ public:
bool can_apply_reset() const;
#endif
- TypedArray<String> get_configuration_warnings() const override;
-
AnimationPlayer();
~AnimationPlayer();
};
diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp
index 572e767f44..4b80f571ae 100644
--- a/scene/animation/animation_tree.cpp
+++ b/scene/animation/animation_tree.cpp
@@ -88,7 +88,7 @@ void AnimationNode::get_child_nodes(List<ChildNode> *r_child_nodes) {
}
}
-void AnimationNode::blend_animation(const StringName &p_animation, double p_time, double p_delta, bool p_seeked, real_t p_blend, int p_pingponged) {
+void AnimationNode::blend_animation(const StringName &p_animation, double p_time, double p_delta, bool p_seeked, bool p_seek_root, real_t p_blend, int p_pingponged) {
ERR_FAIL_COND(!state);
ERR_FAIL_COND(!state->player->has_animation(p_animation));
@@ -115,17 +115,18 @@ void AnimationNode::blend_animation(const StringName &p_animation, double p_time
anim_state.animation = animation;
anim_state.seeked = p_seeked;
anim_state.pingponged = p_pingponged;
+ anim_state.seek_root = p_seek_root;
state->animation_states.push_back(anim_state);
}
-double AnimationNode::_pre_process(const StringName &p_base_path, AnimationNode *p_parent, State *p_state, double p_time, bool p_seek, const Vector<StringName> &p_connections) {
+double AnimationNode::_pre_process(const StringName &p_base_path, AnimationNode *p_parent, State *p_state, double p_time, bool p_seek, bool p_seek_root, const Vector<StringName> &p_connections) {
base_path = p_base_path;
parent = p_parent;
connections = p_connections;
state = p_state;
- double t = process(p_time, p_seek);
+ double t = process(p_time, p_seek, p_seek_root);
state = nullptr;
parent = nullptr;
@@ -135,6 +136,11 @@ double AnimationNode::_pre_process(const StringName &p_base_path, AnimationNode
return t;
}
+AnimationTree *AnimationNode::get_animation_tree() const {
+ ERR_FAIL_COND_V(!state, nullptr);
+ return state->tree;
+}
+
void AnimationNode::make_invalid(const String &p_reason) {
ERR_FAIL_COND(!state);
state->valid = false;
@@ -144,7 +150,7 @@ void AnimationNode::make_invalid(const String &p_reason) {
state->invalid_reasons += String::utf8("• ") + p_reason;
}
-double AnimationNode::blend_input(int p_input, double p_time, bool p_seek, real_t p_blend, FilterAction p_filter, bool p_optimize) {
+double AnimationNode::blend_input(int p_input, double p_time, bool p_seek, bool p_seek_root, real_t p_blend, FilterAction p_filter, bool p_sync) {
ERR_FAIL_INDEX_V(p_input, inputs.size(), 0);
ERR_FAIL_COND_V(!state, 0);
@@ -163,7 +169,7 @@ double AnimationNode::blend_input(int p_input, double p_time, bool p_seek, real_
//inputs.write[p_input].last_pass = state->last_pass;
real_t activity = 0.0;
- double ret = _blend_node(node_name, blend_tree->get_node_connection_array(node_name), nullptr, node, p_time, p_seek, p_blend, p_filter, p_optimize, &activity);
+ double ret = _blend_node(node_name, blend_tree->get_node_connection_array(node_name), nullptr, node, p_time, p_seek, p_seek_root, p_blend, p_filter, p_sync, &activity);
Vector<AnimationTree::Activity> *activity_ptr = state->tree->input_activity_map.getptr(base_path);
@@ -174,11 +180,11 @@ double AnimationNode::blend_input(int p_input, double p_time, bool p_seek, real_
return ret;
}
-double AnimationNode::blend_node(const StringName &p_sub_path, Ref<AnimationNode> p_node, double p_time, bool p_seek, real_t p_blend, FilterAction p_filter, bool p_optimize) {
- return _blend_node(p_sub_path, Vector<StringName>(), this, p_node, p_time, p_seek, p_blend, p_filter, p_optimize);
+double AnimationNode::blend_node(const StringName &p_sub_path, Ref<AnimationNode> p_node, double p_time, bool p_seek, bool p_seek_root, real_t p_blend, FilterAction p_filter, bool p_sync) {
+ return _blend_node(p_sub_path, Vector<StringName>(), this, p_node, p_time, p_seek, p_seek_root, p_blend, p_filter, p_sync);
}
-double AnimationNode::_blend_node(const StringName &p_subpath, const Vector<StringName> &p_connections, AnimationNode *p_new_parent, Ref<AnimationNode> p_node, double p_time, bool p_seek, real_t p_blend, FilterAction p_filter, bool p_optimize, real_t *r_max) {
+double AnimationNode::_blend_node(const StringName &p_subpath, const Vector<StringName> &p_connections, AnimationNode *p_new_parent, Ref<AnimationNode> p_node, double p_time, bool p_seek, bool p_seek_root, real_t p_blend, FilterAction p_filter, bool p_sync, real_t *r_max) {
ERR_FAIL_COND_V(!p_node.is_valid(), 0);
ERR_FAIL_COND_V(!state, 0);
@@ -198,12 +204,11 @@ double AnimationNode::_blend_node(const StringName &p_subpath, const Vector<Stri
blendw[i] = 0.0; //all to zero by default
}
- const NodePath *K = nullptr;
- while ((K = filter.next(K))) {
- if (!state->track_map.has(*K)) {
+ for (const KeyValue<NodePath, bool> &E : filter) {
+ if (!state->track_map.has(E.key)) {
continue;
}
- int idx = state->track_map[*K];
+ int idx = state->track_map[E.key];
blendw[idx] = 1.0; //filtered goes to one
}
@@ -276,7 +281,7 @@ double AnimationNode::_blend_node(const StringName &p_subpath, const Vector<Stri
String new_path;
AnimationNode *new_parent;
- //this is the slowest part of processing, but as strings process in powers of 2, and the paths always exist, it will not result in that many allocations
+ // This is the slowest part of processing, but as strings process in powers of 2, and the paths always exist, it will not result in that many allocations.
if (p_new_parent) {
new_parent = p_new_parent;
new_path = String(base_path) + String(p_subpath) + "/";
@@ -286,10 +291,15 @@ double AnimationNode::_blend_node(const StringName &p_subpath, const Vector<Stri
new_path = String(parent->base_path) + String(p_subpath) + "/";
}
- if (!p_seek && p_optimize && !any_valid) {
- return p_node->_pre_process(new_path, new_parent, state, 0, p_seek, p_connections);
+ // If tracks for blending don't exist for one of the animations, Rest or RESET animation is blended as init animation instead.
+ // Then blend weight is 0 means that the init animation blend weight is 1.
+ // In that case, processing only the animation with the lacking track will not process the lacking track, and will not properly apply the Reset value.
+ // This means that all tracks which the animations in the branch that may be blended have must be processed.
+ // Therefore, the blending process must be executed even if the blend weight is 0.
+ if (!p_seek && !p_sync && !any_valid) {
+ return p_node->_pre_process(new_path, new_parent, state, 0, p_seek, p_seek_root, p_connections);
}
- return p_node->_pre_process(new_path, new_parent, state, p_time, p_seek, p_connections);
+ return p_node->_pre_process(new_path, new_parent, state, p_time, p_seek, p_seek_root, p_connections);
}
int AnimationNode::get_input_count() const {
@@ -333,9 +343,9 @@ void AnimationNode::remove_input(int p_index) {
emit_changed();
}
-double AnimationNode::process(double p_time, bool p_seek) {
+double AnimationNode::process(double p_time, bool p_seek, bool p_seek_root) {
double ret;
- if (GDVIRTUAL_CALL(_process, p_time, p_seek, ret)) {
+ if (GDVIRTUAL_CALL(_process, p_time, p_seek, p_seek_root, ret)) {
return ret;
}
@@ -374,9 +384,8 @@ bool AnimationNode::has_filter() const {
Array AnimationNode::_get_filters() const {
Array paths;
- const NodePath *K = nullptr;
- while ((K = filter.next(K))) {
- paths.push_back(String(*K)); //use strings, so sorting is possible
+ for (const KeyValue<NodePath, bool> &E : filter) {
+ paths.push_back(String(E.key)); //use strings, so sorting is possible
}
paths.sort(); //done so every time the scene is saved, it does not change
@@ -420,9 +429,9 @@ void AnimationNode::_bind_methods() {
ClassDB::bind_method(D_METHOD("_set_filters", "filters"), &AnimationNode::_set_filters);
ClassDB::bind_method(D_METHOD("_get_filters"), &AnimationNode::_get_filters);
- ClassDB::bind_method(D_METHOD("blend_animation", "animation", "time", "delta", "seeked", "blend", "pingponged"), &AnimationNode::blend_animation, DEFVAL(0));
- ClassDB::bind_method(D_METHOD("blend_node", "name", "node", "time", "seek", "blend", "filter", "optimize"), &AnimationNode::blend_node, DEFVAL(FILTER_IGNORE), DEFVAL(true));
- ClassDB::bind_method(D_METHOD("blend_input", "input_index", "time", "seek", "blend", "filter", "optimize"), &AnimationNode::blend_input, DEFVAL(FILTER_IGNORE), DEFVAL(true));
+ ClassDB::bind_method(D_METHOD("blend_animation", "animation", "time", "delta", "seeked", "seek_root", "blend", "pingponged"), &AnimationNode::blend_animation, DEFVAL(0));
+ ClassDB::bind_method(D_METHOD("blend_node", "name", "node", "time", "seek", "seek_root", "blend", "filter", "sync"), &AnimationNode::blend_node, DEFVAL(FILTER_IGNORE), DEFVAL(true));
+ ClassDB::bind_method(D_METHOD("blend_input", "input_index", "time", "seek", "seek_root", "blend", "filter", "sync"), &AnimationNode::blend_input, DEFVAL(FILTER_IGNORE), DEFVAL(true));
ClassDB::bind_method(D_METHOD("set_parameter", "name", "value"), &AnimationNode::set_parameter);
ClassDB::bind_method(D_METHOD("get_parameter", "name"), &AnimationNode::get_parameter);
@@ -434,7 +443,7 @@ void AnimationNode::_bind_methods() {
GDVIRTUAL_BIND(_get_parameter_list);
GDVIRTUAL_BIND(_get_child_by_name, "name");
GDVIRTUAL_BIND(_get_parameter_default_value, "parameter");
- GDVIRTUAL_BIND(_process, "time", "seek");
+ GDVIRTUAL_BIND(_process, "time", "seek", "seek_root");
GDVIRTUAL_BIND(_get_caption);
GDVIRTUAL_BIND(_has_filter);
@@ -488,9 +497,9 @@ void AnimationTree::set_active(bool p_active) {
}
if (!active && is_inside_tree()) {
- for (Set<TrackCache *>::Element *E = playing_caches.front(); E; E = E->next()) {
- if (ObjectDB::get_instance(E->get()->object_id)) {
- E->get()->object->call(SNAME("stop"));
+ for (const TrackCache *E : playing_caches) {
+ if (ObjectDB::get_instance(E->object_id)) {
+ E->object->call(SNAME("stop"));
}
}
@@ -541,9 +550,9 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
player->get_animation_list(&sname);
Ref<Animation> reset_anim;
- bool has_reset_anim = player->has_animation("RESET");
+ bool has_reset_anim = player->has_animation(SceneStringNames::get_singleton()->RESET);
if (has_reset_anim) {
- reset_anim = player->get_animation("RESET");
+ reset_anim = player->get_animation(SceneStringNames::get_singleton()->RESET);
}
for (const StringName &E : sname) {
Ref<Animation> anim = player->get_animation(E);
@@ -570,7 +579,7 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
}
if (!track) {
- RES resource;
+ Ref<Resource> resource;
Vector<StringName> leftover_path;
Node *child = parent->get_node_and_resource(path, resource, leftover_path);
@@ -633,8 +642,7 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
track_xform->bone_idx = bone_idx;
Transform3D rest = sk->get_bone_rest(bone_idx);
track_xform->init_loc = rest.origin;
- track_xform->ref_rot = rest.basis.get_rotation_quaternion();
- track_xform->init_rot = track_xform->ref_rot.log();
+ track_xform->init_rot = rest.basis.get_rotation_quaternion();
track_xform->init_scale = rest.basis.get_scale();
}
}
@@ -667,8 +675,7 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
track_xform->init_loc = reset_anim->track_get_key_value(rt, 0);
} break;
case Animation::TYPE_ROTATION_3D: {
- track_xform->ref_rot = reset_anim->track_get_key_value(rt, 0);
- track_xform->init_rot = track_xform->ref_rot.log();
+ track_xform->init_rot = reset_anim->track_get_key_value(rt, 0);
} break;
case Animation::TYPE_SCALE_3D: {
track_xform->init_scale = reset_anim->track_get_key_value(rt, 0);
@@ -805,11 +812,10 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
List<NodePath> to_delete;
- const NodePath *K = nullptr;
- while ((K = track_cache.next(K))) {
- TrackCache *tc = track_cache[*K];
+ for (const KeyValue<NodePath, TrackCache *> &K : track_cache) {
+ TrackCache *tc = track_cache[K.key];
if (tc->setup_pass != setup_pass) {
- to_delete.push_back(*K);
+ to_delete.push_back(K.key);
}
}
@@ -822,10 +828,9 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
state.track_map.clear();
- K = nullptr;
int idx = 0;
- while ((K = track_cache.next(K))) {
- state.track_map[*K] = idx;
+ for (const KeyValue<NodePath, TrackCache *> &K : track_cache) {
+ state.track_map[K.key] = idx;
idx++;
}
@@ -837,9 +842,8 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
}
void AnimationTree::_clear_caches() {
- const NodePath *K = nullptr;
- while ((K = track_cache.next(K))) {
- memdelete(track_cache[*K]);
+ for (KeyValue<NodePath, TrackCache *> &K : track_cache) {
+ memdelete(K.value);
}
playing_caches.clear();
@@ -866,7 +870,6 @@ void AnimationTree::_process_graph(double p_delta) {
_update_properties(); //if properties need updating, update them
//check all tracks, see if they need modification
-
root_motion_transform = Transform3D();
if (!root.is_valid()) {
@@ -926,7 +929,6 @@ void AnimationTree::_process_graph(double p_delta) {
state.valid = true;
state.invalid_reasons = "";
state.animation_states.clear(); //will need to be re-created
- state.valid = true;
state.player = player;
state.last_pass = process_pass;
state.tree = this;
@@ -945,11 +947,11 @@ void AnimationTree::_process_graph(double p_delta) {
{
if (started) {
//if started, seek
- root->_pre_process(SceneStringNames::get_singleton()->parameters_base_path, nullptr, &state, 0, true, Vector<StringName>());
+ root->_pre_process(SceneStringNames::get_singleton()->parameters_base_path, nullptr, &state, 0, true, false, Vector<StringName>());
started = false;
}
- root->_pre_process(SceneStringNames::get_singleton()->parameters_base_path, nullptr, &state, p_delta, false, Vector<StringName>());
+ root->_pre_process(SceneStringNames::get_singleton()->parameters_base_path, nullptr, &state, p_delta, false, false, Vector<StringName>());
}
if (!state.valid) {
@@ -969,9 +971,14 @@ void AnimationTree::_process_graph(double p_delta) {
int pingponged = as.pingponged;
#ifndef _3D_DISABLED
bool backward = signbit(delta);
+ bool calc_root = !seeked || as.seek_root;
#endif // _3D_DISABLED
for (int i = 0; i < a->get_track_count(); i++) {
+ if (!a->track_is_enabled(i)) {
+ continue;
+ }
+
NodePath path = a->track_get_path(i);
ERR_CONTINUE(!track_cache.has(path));
@@ -997,11 +1004,11 @@ void AnimationTree::_process_graph(double p_delta) {
case Animation::TYPE_POSITION_3D: {
#ifndef _3D_DISABLED
TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track);
- if (track->root_motion) {
+ if (track->root_motion && calc_root) {
if (t->process_pass != process_pass) {
t->process_pass = process_pass;
t->loc = Vector3(0, 0, 0);
- t->rot = Quaternion(0, 0, 0, 0);
+ t->rot = Quaternion(0, 0, 0, 1);
t->scale = Vector3(0, 0, 0);
}
double prev_time = time - delta;
@@ -1059,7 +1066,7 @@ void AnimationTree::_process_graph(double p_delta) {
}
a->position_track_interpolate(i, 0, &loc[1]);
t->loc += (loc[1] - loc[0]) * blend;
- prev_time = 0;
+ prev_time = (double)a->get_length();
}
}
@@ -1093,11 +1100,11 @@ void AnimationTree::_process_graph(double p_delta) {
case Animation::TYPE_ROTATION_3D: {
#ifndef _3D_DISABLED
TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track);
- if (track->root_motion) {
+ if (track->root_motion && calc_root) {
if (t->process_pass != process_pass) {
t->process_pass = process_pass;
t->loc = Vector3(0, 0, 0);
- t->rot = Quaternion(0, 0, 0, 0);
+ t->rot = Quaternion(0, 0, 0, 1);
t->scale = Vector3(0, 0, 0);
}
double prev_time = time - delta;
@@ -1144,7 +1151,7 @@ void AnimationTree::_process_graph(double p_delta) {
continue;
}
a->rotation_track_interpolate(i, (double)a->get_length(), &rot[1]);
- t->rot += (rot[1].log() - rot[0].log()) * blend;
+ t->rot = (t->rot * Quaternion().slerp(rot[0].inverse() * rot[1], blend)).normalized();
prev_time = 0;
}
} else {
@@ -1154,8 +1161,8 @@ void AnimationTree::_process_graph(double p_delta) {
continue;
}
a->rotation_track_interpolate(i, 0, &rot[1]);
- t->rot += (rot[1].log() - rot[0].log()) * blend;
- prev_time = 0;
+ t->rot = (t->rot * Quaternion().slerp(rot[0].inverse() * rot[1], blend)).normalized();
+ prev_time = (double)a->get_length();
}
}
@@ -1165,7 +1172,7 @@ void AnimationTree::_process_graph(double p_delta) {
}
a->rotation_track_interpolate(i, time, &rot[1]);
- t->rot += (rot[1].log() - rot[0].log()) * blend;
+ t->rot = (t->rot * Quaternion().slerp(rot[0].inverse() * rot[1], blend)).normalized();
prev_time = !backward ? 0 : (double)a->get_length();
} else {
@@ -1182,21 +1189,18 @@ void AnimationTree::_process_graph(double p_delta) {
continue;
}
- if (signbit(rot.dot(t->ref_rot))) {
- rot = -rot;
- }
- t->rot += (rot.log() - t->init_rot) * blend;
+ t->rot = (t->rot * Quaternion().slerp(t->init_rot.inverse() * rot, blend)).normalized();
}
#endif // _3D_DISABLED
} break;
case Animation::TYPE_SCALE_3D: {
#ifndef _3D_DISABLED
TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track);
- if (track->root_motion) {
+ if (track->root_motion && calc_root) {
if (t->process_pass != process_pass) {
t->process_pass = process_pass;
t->loc = Vector3(0, 0, 0);
- t->rot = Quaternion(0, 0, 0, 0);
+ t->rot = Quaternion(0, 0, 0, 1);
t->scale = Vector3(0, 0, 0);
}
double prev_time = time - delta;
@@ -1254,7 +1258,7 @@ void AnimationTree::_process_graph(double p_delta) {
}
a->scale_track_interpolate(i, 0, &scale[1]);
t->scale += (scale[1] - scale[0]) * blend;
- prev_time = 0;
+ prev_time = (double)a->get_length();
}
}
@@ -1311,8 +1315,7 @@ void AnimationTree::_process_graph(double p_delta) {
Animation::UpdateMode update_mode = a->value_track_get_update_mode(i);
- if (update_mode == Animation::UPDATE_CONTINUOUS || update_mode == Animation::UPDATE_CAPTURE) { //delta == 0 means seek
-
+ if (update_mode == Animation::UPDATE_CONTINUOUS || update_mode == Animation::UPDATE_CAPTURE) {
Variant value = a->value_track_interpolate(i, time);
if (value == Variant()) {
@@ -1334,12 +1337,21 @@ void AnimationTree::_process_graph(double p_delta) {
if (blend < CMP_EPSILON) {
continue; //nothing to blend
}
- List<int> indices;
- a->value_track_get_key_indices(i, time, delta, &indices, pingponged);
- for (int &F : indices) {
- Variant value = a->track_get_key_value(i, F);
+ if (seeked) {
+ int idx = a->track_find_key(i, time);
+ if (idx < 0) {
+ continue;
+ }
+ Variant value = a->track_get_key_value(i, idx);
t->object->set_indexed(t->subpath, value);
+ } else {
+ List<int> indices;
+ a->value_track_get_key_indices(i, time, delta, &indices, pingponged);
+ for (int &F : indices) {
+ Variant value = a->track_get_key_value(i, F);
+ t->object->set_indexed(t->subpath, value);
+ }
}
}
@@ -1348,20 +1360,27 @@ void AnimationTree::_process_graph(double p_delta) {
if (blend < CMP_EPSILON) {
continue; //nothing to blend
}
- if (!seeked && Math::is_zero_approx(delta)) {
- continue;
- }
TrackCacheMethod *t = static_cast<TrackCacheMethod *>(track);
- List<int> indices;
-
- a->method_track_get_key_indices(i, time, delta, &indices, pingponged);
-
- for (int &F : indices) {
- StringName method = a->method_track_get_name(i, F);
- Vector<Variant> params = a->method_track_get_params(i, F);
+ if (seeked) {
+ int idx = a->track_find_key(i, time);
+ if (idx < 0) {
+ continue;
+ }
+ StringName method = a->method_track_get_name(i, idx);
+ Vector<Variant> params = a->method_track_get_params(i, idx);
if (can_call) {
- _call_object(t->object, method, params, true);
+ _call_object(t->object, method, params, false);
+ }
+ } else {
+ List<int> indices;
+ a->method_track_get_key_indices(i, time, delta, &indices, pingponged);
+ for (int &F : indices) {
+ StringName method = a->method_track_get_name(i, F);
+ Vector<Variant> params = a->method_track_get_params(i, F);
+ if (can_call) {
+ _call_object(t->object, method, params, true);
+ }
}
}
} break;
@@ -1453,7 +1472,7 @@ void AnimationTree::_process_graph(double p_delta) {
t->start = time;
}
} else if (t->playing) {
- bool loop = a->get_loop_mode() != Animation::LoopMode::LOOP_NONE;
+ bool loop = a->get_loop_mode() != Animation::LOOP_NONE;
bool stop = false;
@@ -1522,13 +1541,13 @@ void AnimationTree::_process_graph(double p_delta) {
double at_anim_pos = 0.0;
switch (anim->get_loop_mode()) {
- case Animation::LoopMode::LOOP_NONE: {
+ case Animation::LOOP_NONE: {
at_anim_pos = MAX((double)anim->get_length(), time - pos); //seek to end
} break;
- case Animation::LoopMode::LOOP_LINEAR: {
+ case Animation::LOOP_LINEAR: {
at_anim_pos = Math::fposmod(time - pos, (double)anim->get_length()); //seek to loop
} break;
- case Animation::LoopMode::LOOP_PINGPONG: {
+ case Animation::LOOP_PINGPONG: {
at_anim_pos = Math::pingpong(time - pos, (double)a->get_length());
} break;
default:
@@ -1574,9 +1593,8 @@ void AnimationTree::_process_graph(double p_delta) {
{
// finally, set the tracks
- const NodePath *K = nullptr;
- while ((K = track_cache.next(K))) {
- TrackCache *track = track_cache[*K];
+ for (const KeyValue<NodePath, TrackCache *> &K : track_cache) {
+ TrackCache *track = K.value;
if (track->process_pass != process_pass) {
continue; //not processed, ignore
}
@@ -1585,7 +1603,6 @@ void AnimationTree::_process_graph(double p_delta) {
case Animation::TYPE_POSITION_3D: {
#ifndef _3D_DISABLED
TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track);
- t->rot = t->rot.exp();
if (t->root_motion) {
Transform3D xform;
@@ -1694,6 +1711,14 @@ NodePath AnimationTree::get_animation_player() const {
return animation_player;
}
+void AnimationTree::set_advance_expression_base_node(const NodePath &p_advance_expression_base_node) {
+ advance_expression_base_node = p_advance_expression_base_node;
+}
+
+NodePath AnimationTree::get_advance_expression_base_node() const {
+ return advance_expression_base_node;
+}
+
bool AnimationTree::is_state_invalid() const {
return !state.valid;
}
@@ -1889,6 +1914,9 @@ void AnimationTree::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_animation_player", "root"), &AnimationTree::set_animation_player);
ClassDB::bind_method(D_METHOD("get_animation_player"), &AnimationTree::get_animation_player);
+ ClassDB::bind_method(D_METHOD("set_advance_expression_base_node", "node"), &AnimationTree::set_advance_expression_base_node);
+ ClassDB::bind_method(D_METHOD("get_advance_expression_base_node"), &AnimationTree::get_advance_expression_base_node);
+
ClassDB::bind_method(D_METHOD("set_root_motion_track", "path"), &AnimationTree::set_root_motion_track);
ClassDB::bind_method(D_METHOD("get_root_motion_track"), &AnimationTree::get_root_motion_track);
@@ -1902,6 +1930,8 @@ void AnimationTree::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "tree_root", PROPERTY_HINT_RESOURCE_TYPE, "AnimationRootNode"), "set_tree_root", "get_tree_root");
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "anim_player", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "AnimationPlayer"), "set_animation_player", "get_animation_player");
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "advance_expression_base_node", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node"), "set_advance_expression_base_node", "get_advance_expression_base_node");
+
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "active"), "set_active", "is_active");
ADD_PROPERTY(PropertyInfo(Variant::INT, "process_callback", PROPERTY_HINT_ENUM, "Physics,Idle,Manual"), "set_process_callback", "get_process_callback");
ADD_GROUP("Root Motion", "root_motion_");
diff --git a/scene/animation/animation_tree.h b/scene/animation/animation_tree.h
index 524f735894..4f9a330a89 100644
--- a/scene/animation/animation_tree.h
+++ b/scene/animation/animation_tree.h
@@ -37,6 +37,8 @@
#include "scene/resources/animation.h"
class AnimationNodeBlendTree;
+class AnimationNodeStartState;
+class AnimationNodeEndState;
class AnimationPlayer;
class AnimationTree;
@@ -66,6 +68,7 @@ public:
const Vector<real_t> *track_blends = nullptr;
real_t blend = 0.0;
bool seeked = false;
+ bool seek_root = false;
int pingponged = 0;
};
@@ -83,7 +86,7 @@ public:
Vector<real_t> blends;
State *state = nullptr;
- double _pre_process(const StringName &p_base_path, AnimationNode *p_parent, State *p_state, double p_time, bool p_seek, const Vector<StringName> &p_connections);
+ double _pre_process(const StringName &p_base_path, AnimationNode *p_parent, State *p_state, double p_time, bool p_seek, bool p_seek_root, const Vector<StringName> &p_connections);
//all this is temporary
StringName base_path;
@@ -96,14 +99,15 @@ public:
Array _get_filters() const;
void _set_filters(const Array &p_filters);
friend class AnimationNodeBlendTree;
- double _blend_node(const StringName &p_subpath, const Vector<StringName> &p_connections, AnimationNode *p_new_parent, Ref<AnimationNode> p_node, double p_time, bool p_seek, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_optimize = true, real_t *r_max = nullptr);
+ double _blend_node(const StringName &p_subpath, const Vector<StringName> &p_connections, AnimationNode *p_new_parent, Ref<AnimationNode> p_node, double p_time, bool p_seek, bool p_seek_root, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_sync = true, real_t *r_max = nullptr);
protected:
- void blend_animation(const StringName &p_animation, double p_time, double p_delta, bool p_seeked, real_t p_blend, int p_pingponged = 0);
- double blend_node(const StringName &p_sub_path, Ref<AnimationNode> p_node, double p_time, bool p_seek, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_optimize = true);
- double blend_input(int p_input, double p_time, bool p_seek, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_optimize = true);
+ void blend_animation(const StringName &p_animation, double p_time, double p_delta, bool p_seeked, bool p_seek_root, real_t p_blend, int p_pingponged = 0);
+ double blend_node(const StringName &p_sub_path, Ref<AnimationNode> p_node, double p_time, bool p_seek, bool p_seek_root, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_sync = true);
+ double blend_input(int p_input, double p_time, bool p_seek, bool p_seek_root, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_sync = true);
void make_invalid(const String &p_reason);
+ AnimationTree *get_animation_tree() const;
static void _bind_methods();
@@ -113,7 +117,7 @@ protected:
GDVIRTUAL0RC(Array, _get_parameter_list)
GDVIRTUAL1RC(Ref<AnimationNode>, _get_child_by_name, StringName)
GDVIRTUAL1RC(Variant, _get_parameter_default_value, StringName)
- GDVIRTUAL2RC(double, _process, double, bool)
+ GDVIRTUAL3RC(double, _process, double, bool, bool)
GDVIRTUAL0RC(String, _get_caption)
GDVIRTUAL0RC(bool, _has_filter)
@@ -131,7 +135,7 @@ public:
virtual void get_child_nodes(List<ChildNode> *r_child_nodes);
- virtual double process(double p_time, bool p_seek);
+ virtual double process(double p_time, bool p_seek, bool p_seek_root);
virtual String get_caption() const;
int get_input_count() const;
@@ -164,6 +168,14 @@ public:
AnimationRootNode() {}
};
+class AnimationNodeStartState : public AnimationRootNode {
+ GDCLASS(AnimationNodeStartState, AnimationRootNode);
+};
+
+class AnimationNodeEndState : public AnimationRootNode {
+ GDCLASS(AnimationNodeEndState, AnimationRootNode);
+};
+
class AnimationTree : public Node {
GDCLASS(AnimationTree, Node);
@@ -198,8 +210,7 @@ private:
bool rot_used = false;
bool scale_used = false;
Vector3 init_loc = Vector3(0, 0, 0);
- Quaternion ref_rot = Quaternion(0, 0, 0, 1);
- Quaternion init_rot = Quaternion(0, 0, 0, 0);
+ Quaternion init_rot = Quaternion(0, 0, 0, 1);
Vector3 init_scale = Vector3(1, 1, 1);
Vector3 loc;
Quaternion rot;
@@ -257,9 +268,10 @@ private:
};
HashMap<NodePath, TrackCache *> track_cache;
- Set<TrackCache *> playing_caches;
+ HashSet<TrackCache *> playing_caches;
Ref<AnimationNode> root;
+ NodePath advance_expression_base_node = NodePath(String("."));
AnimationProcessCallback process_callback = ANIMATION_PROCESS_IDLE;
bool active = false;
@@ -322,6 +334,9 @@ public:
void set_animation_player(const NodePath &p_player);
NodePath get_animation_player() const;
+ void set_advance_expression_base_node(const NodePath &p_advance_expression_base_node);
+ NodePath get_advance_expression_base_node() const;
+
TypedArray<String> get_configuration_warnings() const override;
bool is_state_invalid() const;
diff --git a/scene/animation/root_motion_view.cpp b/scene/animation/root_motion_view.cpp
index 42adc1ea02..47f08219a9 100644
--- a/scene/animation/root_motion_view.cpp
+++ b/scene/animation/root_motion_view.cpp
@@ -114,9 +114,8 @@ void RootMotionView::_notification(int p_what) {
first = false;
transform.orthonormalize(); //don't want scale, too imprecise
- transform.affine_invert();
- accumulated = transform * accumulated;
+ accumulated = accumulated * transform;
accumulated.origin.x = Math::fposmod(accumulated.origin.x, cell_size);
if (zero_y) {
accumulated.origin.y = 0;
@@ -134,9 +133,9 @@ void RootMotionView::_notification(int p_what) {
Vector3 from(i * cell_size, 0, j * cell_size);
Vector3 from_i((i + 1) * cell_size, 0, j * cell_size);
Vector3 from_j(i * cell_size, 0, (j + 1) * cell_size);
- from = accumulated.xform(from);
- from_i = accumulated.xform(from_i);
- from_j = accumulated.xform(from_j);
+ from = accumulated.xform_inv(from);
+ from_i = accumulated.xform_inv(from_i);
+ from_j = accumulated.xform_inv(from_j);
Color c = color, c_i = color, c_j = color;
c.a *= MAX(0, 1.0 - from.length() / radius);
@@ -184,8 +183,8 @@ void RootMotionView::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "animation_path", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "AnimationTree"), "set_animation_path", "get_animation_path");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_color", "get_color");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "cell_size", PROPERTY_HINT_RANGE, "0.1,16,0.01,or_greater"), "set_cell_size", "get_cell_size");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.1,16,0.01,or_greater"), "set_radius", "get_radius");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "cell_size", PROPERTY_HINT_RANGE, "0.1,16,0.01,or_greater,suffix:m"), "set_cell_size", "get_cell_size");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.1,16,0.01,or_greater,suffix:m"), "set_radius", "get_radius");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "zero_y"), "set_zero_y", "get_zero_y");
}
diff --git a/scene/animation/tween.cpp b/scene/animation/tween.cpp
index ccc878a6ec..5457da472f 100644
--- a/scene/animation/tween.cpp
+++ b/scene/animation/tween.cpp
@@ -152,10 +152,6 @@ bool Tween::is_running() {
return running;
}
-void Tween::set_valid(bool p_valid) {
- valid = p_valid;
-}
-
bool Tween::is_valid() {
return valid;
}
@@ -282,14 +278,15 @@ bool Tween::step(float p_delta) {
bool step_active = false;
total_time += rem_delta;
+#ifdef DEBUG_ENABLED
+ float initial_delta = rem_delta;
+ bool potential_infinite = false;
+#endif
+
while (rem_delta > 0 && running) {
float step_delta = rem_delta;
step_active = false;
-#ifdef DEBUG_ENABLED
- float prev_delta = rem_delta;
-#endif
-
for (Ref<Tweener> &tweener : tweeners.write[current_step]) {
// Modified inside Tweener.step().
float temp_delta = rem_delta;
@@ -314,17 +311,21 @@ bool Tween::step(float p_delta) {
emit_signal(SNAME("loop_finished"), loops_done);
current_step = 0;
start_tweeners();
+#ifdef DEBUG_ENABLED
+ if (loops <= 0 && Math::is_equal_approx(rem_delta, initial_delta)) {
+ if (!potential_infinite) {
+ potential_infinite = true;
+ } else {
+ // Looped twice without using any time, this is 100% certain infinite loop.
+ ERR_FAIL_V_MSG(false, "Infinite loop detected. Check set_loops() description for more info.");
+ }
+ }
+#endif
}
} else {
start_tweeners();
}
}
-
-#ifdef DEBUG_ENABLED
- if (Math::is_equal_approx(rem_delta, prev_delta) && running && loops <= 0) {
- ERR_FAIL_V_MSG(false, "Infinite loop detected. Check set_loops() description for more info.");
- }
-#endif
}
return true;
@@ -334,7 +335,7 @@ bool Tween::can_process(bool p_tree_paused) const {
if (is_bound && pause_mode == TWEEN_PAUSE_BOUND) {
Node *bound_node = get_bound_node();
if (bound_node) {
- return bound_node->can_process();
+ return bound_node->is_inside_tree() && bound_node->can_process();
}
}
@@ -455,12 +456,12 @@ Variant Tween::interpolate_variant(Variant p_initial_val, Variant p_delta_val, f
Transform2D d = p_delta_val;
Transform2D r;
- APPLY_EQUATION(elements[0][0]);
- APPLY_EQUATION(elements[0][1]);
- APPLY_EQUATION(elements[1][0]);
- APPLY_EQUATION(elements[1][1]);
- APPLY_EQUATION(elements[2][0]);
- APPLY_EQUATION(elements[2][1]);
+ APPLY_EQUATION(columns[0][0]);
+ APPLY_EQUATION(columns[0][1]);
+ APPLY_EQUATION(columns[1][0]);
+ APPLY_EQUATION(columns[1][1]);
+ APPLY_EQUATION(columns[2][0]);
+ APPLY_EQUATION(columns[2][1]);
return r;
}
@@ -495,15 +496,15 @@ Variant Tween::interpolate_variant(Variant p_initial_val, Variant p_delta_val, f
Basis d = p_delta_val;
Basis r;
- APPLY_EQUATION(elements[0][0]);
- APPLY_EQUATION(elements[0][1]);
- APPLY_EQUATION(elements[0][2]);
- APPLY_EQUATION(elements[1][0]);
- APPLY_EQUATION(elements[1][1]);
- APPLY_EQUATION(elements[1][2]);
- APPLY_EQUATION(elements[2][0]);
- APPLY_EQUATION(elements[2][1]);
- APPLY_EQUATION(elements[2][2]);
+ APPLY_EQUATION(rows[0][0]);
+ APPLY_EQUATION(rows[0][1]);
+ APPLY_EQUATION(rows[0][2]);
+ APPLY_EQUATION(rows[1][0]);
+ APPLY_EQUATION(rows[1][1]);
+ APPLY_EQUATION(rows[1][2]);
+ APPLY_EQUATION(rows[2][0]);
+ APPLY_EQUATION(rows[2][1]);
+ APPLY_EQUATION(rows[2][2]);
return r;
}
@@ -512,15 +513,15 @@ Variant Tween::interpolate_variant(Variant p_initial_val, Variant p_delta_val, f
Transform3D d = p_delta_val;
Transform3D r;
- APPLY_EQUATION(basis.elements[0][0]);
- APPLY_EQUATION(basis.elements[0][1]);
- APPLY_EQUATION(basis.elements[0][2]);
- APPLY_EQUATION(basis.elements[1][0]);
- APPLY_EQUATION(basis.elements[1][1]);
- APPLY_EQUATION(basis.elements[1][2]);
- APPLY_EQUATION(basis.elements[2][0]);
- APPLY_EQUATION(basis.elements[2][1]);
- APPLY_EQUATION(basis.elements[2][2]);
+ APPLY_EQUATION(basis.rows[0][0]);
+ APPLY_EQUATION(basis.rows[0][1]);
+ APPLY_EQUATION(basis.rows[0][2]);
+ APPLY_EQUATION(basis.rows[1][0]);
+ APPLY_EQUATION(basis.rows[1][1]);
+ APPLY_EQUATION(basis.rows[1][2]);
+ APPLY_EQUATION(basis.rows[2][0]);
+ APPLY_EQUATION(basis.rows[2][1]);
+ APPLY_EQUATION(basis.rows[2][2]);
APPLY_EQUATION(origin.x);
APPLY_EQUATION(origin.y);
APPLY_EQUATION(origin.z);
@@ -569,12 +570,12 @@ Variant Tween::calculate_delta_value(Variant p_intial_val, Variant p_final_val)
case Variant::TRANSFORM2D: {
Transform2D i = p_intial_val;
Transform2D f = p_final_val;
- return Transform2D(f.elements[0][0] - i.elements[0][0],
- f.elements[0][1] - i.elements[0][1],
- f.elements[1][0] - i.elements[1][0],
- f.elements[1][1] - i.elements[1][1],
- f.elements[2][0] - i.elements[2][0],
- f.elements[2][1] - i.elements[2][1]);
+ return Transform2D(f.columns[0][0] - i.columns[0][0],
+ f.columns[0][1] - i.columns[0][1],
+ f.columns[1][0] - i.columns[1][0],
+ f.columns[1][1] - i.columns[1][1],
+ f.columns[2][0] - i.columns[2][0],
+ f.columns[2][1] - i.columns[2][1]);
}
case Variant::AABB: {
@@ -586,29 +587,29 @@ Variant Tween::calculate_delta_value(Variant p_intial_val, Variant p_final_val)
case Variant::BASIS: {
Basis i = p_intial_val;
Basis f = p_final_val;
- return Basis(f.elements[0][0] - i.elements[0][0],
- f.elements[0][1] - i.elements[0][1],
- f.elements[0][2] - i.elements[0][2],
- f.elements[1][0] - i.elements[1][0],
- f.elements[1][1] - i.elements[1][1],
- f.elements[1][2] - i.elements[1][2],
- f.elements[2][0] - i.elements[2][0],
- f.elements[2][1] - i.elements[2][1],
- f.elements[2][2] - i.elements[2][2]);
+ return Basis(f.rows[0][0] - i.rows[0][0],
+ f.rows[0][1] - i.rows[0][1],
+ f.rows[0][2] - i.rows[0][2],
+ f.rows[1][0] - i.rows[1][0],
+ f.rows[1][1] - i.rows[1][1],
+ f.rows[1][2] - i.rows[1][2],
+ f.rows[2][0] - i.rows[2][0],
+ f.rows[2][1] - i.rows[2][1],
+ f.rows[2][2] - i.rows[2][2]);
}
case Variant::TRANSFORM3D: {
Transform3D i = p_intial_val;
Transform3D f = p_final_val;
- return Transform3D(f.basis.elements[0][0] - i.basis.elements[0][0],
- f.basis.elements[0][1] - i.basis.elements[0][1],
- f.basis.elements[0][2] - i.basis.elements[0][2],
- f.basis.elements[1][0] - i.basis.elements[1][0],
- f.basis.elements[1][1] - i.basis.elements[1][1],
- f.basis.elements[1][2] - i.basis.elements[1][2],
- f.basis.elements[2][0] - i.basis.elements[2][0],
- f.basis.elements[2][1] - i.basis.elements[2][1],
- f.basis.elements[2][2] - i.basis.elements[2][2],
+ return Transform3D(f.basis.rows[0][0] - i.basis.rows[0][0],
+ f.basis.rows[0][1] - i.basis.rows[0][1],
+ f.basis.rows[0][2] - i.basis.rows[0][2],
+ f.basis.rows[1][0] - i.basis.rows[1][0],
+ f.basis.rows[1][1] - i.basis.rows[1][1],
+ f.basis.rows[1][2] - i.basis.rows[1][2],
+ f.basis.rows[2][0] - i.basis.rows[2][0],
+ f.basis.rows[2][1] - i.basis.rows[2][1],
+ f.basis.rows[2][2] - i.basis.rows[2][2],
f.origin.x - i.origin.x,
f.origin.y - i.origin.y,
f.origin.z - i.origin.z);
@@ -648,7 +649,7 @@ void Tween::_bind_methods() {
ClassDB::bind_method(D_METHOD("parallel"), &Tween::parallel);
ClassDB::bind_method(D_METHOD("chain"), &Tween::chain);
- ClassDB::bind_method(D_METHOD("interpolate_value", "initial_value", "delta_value", "elapsed_time", "duration", "trans_type", "ease_type"), &Tween::interpolate_variant);
+ ClassDB::bind_static_method("Tween", D_METHOD("interpolate_value", "initial_value", "delta_value", "elapsed_time", "duration", "trans_type", "ease_type"), &Tween::interpolate_variant);
ADD_SIGNAL(MethodInfo("step_finished", PropertyInfo(Variant::INT, "idx")));
ADD_SIGNAL(MethodInfo("loop_finished", PropertyInfo(Variant::INT, "loop_count")));
@@ -679,6 +680,14 @@ void Tween::_bind_methods() {
BIND_ENUM_CONSTANT(EASE_OUT_IN);
}
+Tween::Tween() {
+ ERR_FAIL_MSG("Tween can't be created directly. Use create_tween() method.");
+}
+
+Tween::Tween(bool p_valid) {
+ valid = p_valid;
+}
+
Ref<PropertyTweener> PropertyTweener::from(Variant p_value) {
initial_val = p_value;
do_continue = false;
@@ -846,7 +855,7 @@ bool CallbackTweener::step(float &r_delta) {
Callable::CallError ce;
callback.call(nullptr, 0, result, ce);
if (ce.error != Callable::CallError::CALL_OK) {
- ERR_FAIL_V_MSG(false, "Error calling method from CallbackTweener: " + Variant::get_call_error_text(this, callback.get_method(), nullptr, 0, ce));
+ ERR_FAIL_V_MSG(false, "Error calling method from CallbackTweener: " + Variant::get_callable_error_text(callback, nullptr, 0, ce));
}
finished = true;
@@ -917,7 +926,7 @@ bool MethodTweener::step(float &r_delta) {
Callable::CallError ce;
callback.call(argptr, 1, result, ce);
if (ce.error != Callable::CallError::CALL_OK) {
- ERR_FAIL_V_MSG(false, "Error calling method from MethodTweener: " + Variant::get_call_error_text(this, callback.get_method(), argptr, 1, ce));
+ ERR_FAIL_V_MSG(false, "Error calling method from MethodTweener: " + Variant::get_callable_error_text(callback, argptr, 1, ce));
}
if (time < duration) {
diff --git a/scene/animation/tween.h b/scene/animation/tween.h
index e28a499259..40268405cf 100644
--- a/scene/animation/tween.h
+++ b/scene/animation/tween.h
@@ -116,6 +116,9 @@ private:
bool valid = false;
bool default_parallel = false;
bool parallel_enabled = false;
+#ifdef DEBUG_ENABLED
+ bool is_infinite = false;
+#endif
typedef real_t (*interpolater)(real_t t, real_t b, real_t c, real_t d);
static interpolater interpolaters[TRANS_MAX][EASE_MAX];
@@ -139,7 +142,6 @@ public:
void kill();
bool is_running();
- void set_valid(bool p_valid);
bool is_valid();
void clear();
@@ -160,8 +162,8 @@ public:
Ref<Tween> parallel();
Ref<Tween> chain();
- real_t run_equation(TransitionType p_trans_type, EaseType p_ease_type, real_t t, real_t b, real_t c, real_t d);
- Variant interpolate_variant(Variant p_initial_val, Variant p_delta_val, float p_time, float p_duration, Tween::TransitionType p_trans, Tween::EaseType p_ease);
+ static real_t run_equation(TransitionType p_trans_type, EaseType p_ease_type, real_t t, real_t b, real_t c, real_t d);
+ static Variant interpolate_variant(Variant p_initial_val, Variant p_delta_val, float p_time, float p_duration, Tween::TransitionType p_trans, Tween::EaseType p_ease);
Variant calculate_delta_value(Variant p_intial_val, Variant p_final_val);
bool step(float p_delta);
@@ -169,7 +171,8 @@ public:
Node *get_bound_node() const;
float get_total_time() const;
- Tween() {}
+ Tween();
+ Tween(bool p_valid);
};
VARIANT_ENUM_CAST(Tween::TweenPauseMode);