diff --git a/doc/classes/AnimationMixer.xml b/doc/classes/AnimationMixer.xml index a77e9e28c694..98b0ad886d39 100644 --- a/doc/classes/AnimationMixer.xml +++ b/doc/classes/AnimationMixer.xml @@ -309,7 +309,7 @@ Notifies when an animation finished playing. - [b]Note:[/b] This signal is not emitted if an animation is looping. + [b]Note:[/b] This signal is not emitted if an animation is looping. For looping animations, use [signal animation_looped] instead. @@ -322,6 +322,14 @@ Notifies when an animation list is changed. + + + + + Notifies when a looping animation finished playing. [param backwards] is [code]true[/code] if the looping "barrier" has been crossed backwards when playing the animation, [code]false[/code] otherwise. In practice, [param backwards] is [code]true[/code] every time when the animation is played backwards, or every 2 signals emitted when ping-pong looping is used. + [b]Note:[/b] This signal is not emitted if an animation is not looping. For non-looping animations, use [signal animation_finished] instead. + + diff --git a/scene/animation/animation_blend_tree.cpp b/scene/animation/animation_blend_tree.cpp index f3385b4cdc38..37a98e306783 100644 --- a/scene/animation/animation_blend_tree.cpp +++ b/scene/animation/animation_blend_tree.cpp @@ -147,6 +147,7 @@ AnimationNode::NodeTimeInfo AnimationNodeAnimation::_process(const AnimationMixe if (!Math::is_zero_approx(cur_len)) { if (prev_time <= cur_len && cur_time > cur_len) { is_just_looped = true; // Don't break with negative timescale since remain will not be 0. + process_state->tree->call_deferred(SNAME("emit_signal"), SceneStringNames::get_singleton()->animation_looped, animation, node_backward); } cur_time = Math::fposmod(cur_time, cur_len); } @@ -155,9 +156,11 @@ AnimationNode::NodeTimeInfo AnimationNodeAnimation::_process(const AnimationMixe if (!Math::is_zero_approx(cur_len)) { if (prev_time >= 0 && cur_time < 0) { backward = !backward; + process_state->tree->call_deferred(SNAME("emit_signal"), SceneStringNames::get_singleton()->animation_looped, animation, !node_backward); } else if (prev_time <= cur_len && cur_time > cur_len) { backward = !backward; is_just_looped = true; // Don't break with negative timescale since remain will not be 0. + process_state->tree->call_deferred(SNAME("emit_signal"), SceneStringNames::get_singleton()->animation_looped, animation, node_backward); } cur_time = Math::pingpong(cur_time, cur_len); } diff --git a/scene/animation/animation_mixer.cpp b/scene/animation/animation_mixer.cpp index d22b58346f03..2461a34842f2 100644 --- a/scene/animation/animation_mixer.cpp +++ b/scene/animation/animation_mixer.cpp @@ -2276,6 +2276,7 @@ void AnimationMixer::_bind_methods() { ADD_SIGNAL(MethodInfo(SNAME("animation_list_changed"))); ADD_SIGNAL(MethodInfo(SNAME("animation_libraries_updated"))); ADD_SIGNAL(MethodInfo(SNAME("animation_finished"), PropertyInfo(Variant::STRING_NAME, "anim_name"))); + ADD_SIGNAL(MethodInfo(SNAME("animation_looped"), PropertyInfo(Variant::STRING_NAME, "anim_name"), PropertyInfo(Variant::BOOL, "backwards"))); ADD_SIGNAL(MethodInfo(SNAME("animation_started"), PropertyInfo(Variant::STRING_NAME, "anim_name"))); ADD_SIGNAL(MethodInfo(SNAME("caches_cleared"))); ADD_SIGNAL(MethodInfo(SNAME("mixer_applied"))); diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp index 22138004769d..f44b6a358622 100644 --- a/scene/animation/animation_player.cpp +++ b/scene/animation/animation_player.cpp @@ -181,9 +181,11 @@ void AnimationPlayer::_process_playback_data(PlaybackData &cd, double p_delta, f case Animation::LOOP_LINEAR: { if (next_pos < 0 && cd.pos >= 0) { looped_flag = Animation::LOOPED_FLAG_START; + emit_signal(SceneStringNames::get_singleton()->animation_looped, playback.assigned, !backwards); } if (next_pos > len && cd.pos <= len) { looped_flag = Animation::LOOPED_FLAG_END; + emit_signal(SceneStringNames::get_singleton()->animation_looped, playback.assigned, backwards); } next_pos = Math::fposmod(next_pos, (double)len); } break; @@ -192,10 +194,12 @@ void AnimationPlayer::_process_playback_data(PlaybackData &cd, double p_delta, f if (next_pos < 0 && cd.pos >= 0) { cd.speed_scale *= -1.0; looped_flag = Animation::LOOPED_FLAG_START; + emit_signal(SceneStringNames::get_singleton()->animation_looped, playback.assigned, !backwards); } if (next_pos > len && cd.pos <= len) { cd.speed_scale *= -1.0; looped_flag = Animation::LOOPED_FLAG_END; + emit_signal(SceneStringNames::get_singleton()->animation_looped, playback.assigned, backwards); } next_pos = Math::pingpong(next_pos, (double)len); } break; diff --git a/scene/scene_string_names.cpp b/scene/scene_string_names.cpp index 5610b2f6421f..e72a8feba6aa 100644 --- a/scene/scene_string_names.cpp +++ b/scene/scene_string_names.cpp @@ -58,6 +58,7 @@ SceneStringNames::SceneStringNames() { finished = StaticCString::create("finished"); animation_finished = StaticCString::create("animation_finished"); + animation_looped = StaticCString::create("animation_looped"); animation_changed = StaticCString::create("animation_changed"); animation_started = StaticCString::create("animation_started"); RESET = StaticCString::create("RESET"); diff --git a/scene/scene_string_names.h b/scene/scene_string_names.h index 60254e3006a9..a9121f4c8781 100644 --- a/scene/scene_string_names.h +++ b/scene/scene_string_names.h @@ -94,6 +94,7 @@ class SceneStringNames { StringName finished; StringName animation_finished; + StringName animation_looped; StringName animation_changed; StringName animation_started; StringName RESET;