From 327d1413d526e9508736958506dda432d578d6fb Mon Sep 17 00:00:00 2001 From: "Silc Lizard (Tokage) Renew" <61938263+TokageItLab@users.noreply.github.com> Date: Fri, 12 Jun 2026 20:21:04 +0900 Subject: [PATCH] Fix stretch mode in custom_timeline on AnimationNodeAnimation --- scene/animation/animation_blend_tree.cpp | 57 +++++++++++++----------- 1 file changed, 30 insertions(+), 27 deletions(-) diff --git a/scene/animation/animation_blend_tree.cpp b/scene/animation/animation_blend_tree.cpp index 3662205990..a532c6d62b 100644 --- a/scene/animation/animation_blend_tree.cpp +++ b/scene/animation/animation_blend_tree.cpp @@ -111,18 +111,20 @@ AnimationNode::NodeTimeInfo AnimationNodeAnimation::_process(ProcessState &p_pro double anim_size = anim->get_length(); double cur_len; // The animation length seen in NodeTimeInfo, propagated throughout the tree. + + // The start/end section passed to AnimationMixer, to trim for Discrete/Method/Audio key section. double playback_start; // Start of the current section in AnimationMixer. double playback_end; // End of the current section in AnimationMixer. Animation::LoopMode cur_loop_mode; if (use_custom_timeline) { - if (stretch_time_scale) { - // When time scale is stretched, the entire animation is played anyway using a time scale based on the timeline length. + cur_len = timeline_length; + cur_loop_mode = loop_mode; + if (stretch_time_scale || cur_loop_mode != Animation::LOOP_NONE) { + // When time scale is stretched or looped, the entire animation is played anyway using a time scale based on the timeline length. // Therefore, the end of the animation section is the animation length. - cur_len = anim_size; playback_start = 0.0; playback_end = anim_size; } else { - cur_len = timeline_length; if (play_mode == PLAY_MODE_FORWARD) { playback_start = start_offset; playback_end = playback_start + timeline_length; @@ -131,7 +133,6 @@ AnimationNode::NodeTimeInfo AnimationNodeAnimation::_process(ProcessState &p_pro playback_start = playback_end - timeline_length; } } - cur_loop_mode = loop_mode; } else { cur_len = anim_size; playback_start = 0.0; @@ -139,8 +140,6 @@ AnimationNode::NodeTimeInfo AnimationNodeAnimation::_process(ProcessState &p_pro cur_loop_mode = anim->get_loop_mode(); } - // AnimationNodeAnimation always uses times relative to the start of the section. ([0, cur_len]) - // When creating the next PlaybackInfo, the relative time is converted back to absolute time. ([playback_start, playback_end]) double cur_time = p_playback_info.time; double cur_delta = p_playback_info.delta; bool cur_backward = p_instance.get_parameter_backward(); @@ -211,42 +210,46 @@ AnimationNode::NodeTimeInfo AnimationNodeAnimation::_process(ProcessState &p_pro // 3. Progress for Animation. double prev_playback_time = prev_time; double cur_playback_time = cur_time; - if (use_custom_timeline && stretch_time_scale) { - double mlt = anim_size / timeline_length; - prev_playback_time *= mlt; - cur_playback_time *= mlt; - cur_delta *= mlt; + if (use_custom_timeline) { + prev_playback_time += start_offset; + cur_playback_time += start_offset; + if (stretch_time_scale) { + double mlt = anim_size / timeline_length; + prev_playback_time *= mlt; + cur_playback_time *= mlt; + cur_delta *= mlt; + } } if (cur_loop_mode == Animation::LOOP_LINEAR) { - if (!Math::is_zero_approx(cur_len)) { - prev_playback_time = Math::fposmod(prev_playback_time, cur_len); - cur_playback_time = Math::fposmod(cur_playback_time, cur_len); + if (!Math::is_zero_approx(anim_size)) { + prev_playback_time = Math::fposmod(prev_playback_time, anim_size); + cur_playback_time = Math::fposmod(cur_playback_time, anim_size); if (Animation::is_greater_or_equal_approx(prev_playback_time, 0) && Animation::is_less_approx(cur_playback_time, 0)) { looped_flag = node_backward ? Animation::LOOPED_FLAG_END : Animation::LOOPED_FLAG_START; } - if (Animation::is_less_or_equal_approx(prev_playback_time, cur_len) && Animation::is_greater_approx(cur_playback_time, cur_len)) { + if (Animation::is_less_or_equal_approx(prev_playback_time, anim_size) && Animation::is_greater_approx(cur_playback_time, anim_size)) { looped_flag = node_backward ? Animation::LOOPED_FLAG_START : Animation::LOOPED_FLAG_END; } } } else if (cur_loop_mode == Animation::LOOP_PINGPONG) { - if (!Math::is_zero_approx(cur_len)) { - if (Animation::is_greater_or_equal_approx(Math::fposmod(cur_playback_time, cur_len * 2.0), cur_len)) { + if (!Math::is_zero_approx(anim_size)) { + if (Animation::is_greater_or_equal_approx(Math::fposmod(cur_playback_time, anim_size * 2.0), anim_size)) { cur_delta = -cur_delta; // Needed for retrieving discrete keys correctly. } - prev_playback_time = Math::pingpong(prev_playback_time, cur_len); - cur_playback_time = Math::pingpong(cur_playback_time, cur_len); + prev_playback_time = Math::pingpong(prev_playback_time, anim_size); + cur_playback_time = Math::pingpong(cur_playback_time, anim_size); if (Animation::is_greater_or_equal_approx(prev_playback_time, 0) && Animation::is_less_approx(cur_playback_time, 0)) { looped_flag = node_backward ? Animation::LOOPED_FLAG_END : Animation::LOOPED_FLAG_START; } - if (Animation::is_less_or_equal_approx(prev_playback_time, cur_len) && Animation::is_greater_approx(cur_playback_time, cur_len)) { + if (Animation::is_less_or_equal_approx(prev_playback_time, anim_size) && Animation::is_greater_approx(cur_playback_time, anim_size)) { looped_flag = node_backward ? Animation::LOOPED_FLAG_START : Animation::LOOPED_FLAG_END; } } } else { if (Animation::is_less_approx(cur_playback_time, 0)) { cur_playback_time = 0; - } else if (Animation::is_greater_approx(cur_playback_time, cur_len)) { - cur_playback_time = cur_len; + } else if (Animation::is_greater_approx(cur_playback_time, anim_size)) { + cur_playback_time = anim_size; } // Emit start & finish signal. Internally, the detections are the same for backward. @@ -257,8 +260,8 @@ AnimationNode::NodeTimeInfo AnimationNodeAnimation::_process(ProcessState &p_pro p_process_state.tree->call_deferred(SNAME("emit_signal"), SceneStringName(animation_started), animation); } // Finished. - if (Animation::is_less_approx(prev_playback_time, cur_len) && Animation::is_greater_or_equal_approx(cur_playback_time, cur_len)) { - cur_playback_time = cur_len; + if (Animation::is_less_approx(prev_playback_time, anim_size) && Animation::is_greater_or_equal_approx(cur_playback_time, anim_size)) { + cur_playback_time = anim_size; p_process_state.tree->call_deferred(SNAME("emit_signal"), SceneStringName(animation_finished), animation); } } @@ -284,9 +287,9 @@ AnimationNode::NodeTimeInfo AnimationNodeAnimation::_process(ProcessState &p_pro pi.start = playback_start; pi.end = playback_end; if (play_mode == PLAY_MODE_FORWARD) { - pi.time = playback_start + cur_playback_time; + pi.time = cur_playback_time; } else { - pi.time = playback_start + cur_len - cur_playback_time; + pi.time = anim_size - cur_playback_time; } if (node_backward ? cur_backward : !cur_backward) { pi.delta = cur_delta;