diff --git a/doc/classes/Animation.xml b/doc/classes/Animation.xml index 26ed881502e..3b7a6e66fe5 100644 --- a/doc/classes/Animation.xml +++ b/doc/classes/Animation.xml @@ -360,9 +360,12 @@ + Finds the key index by time in a given track. Optionally, only find it if the approx/exact time is given. If [param limit] is [code]true[/code], it does not return keys outside the animation range. + If [param backward] is [code]true[/code], the direction is reversed in methods that rely on one directional processing. + For example, in case [param find_mode] is [constant FIND_MODE_NEAREST], if there is no key in the current position just after seeked, the first key found is retrieved by searching before the position, but if [param backward] is [code]true[/code], the first key found is retrieved after the position. @@ -583,6 +586,7 @@ Returns the interpolated value at the given time (in seconds). The [param track_idx] must be the index of a value track. + A [param backward] mainly affects the direction of key retrieval of the track with [constant UPDATE_DISCRETE] converted by [constant AnimationMixer.ANIMATION_CALLBACK_MODE_DISCRETE_FORCE_CONTINUOUS] to match the result with [method track_find_key]. diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp index fe56f488895..0a2c192ea40 100644 --- a/editor/plugins/animation_player_editor_plugin.cpp +++ b/editor/plugins/animation_player_editor_plugin.cpp @@ -243,12 +243,12 @@ void AnimationPlayerEditor::_play_from_pressed() { String current = _get_current(); if (!current.is_empty()) { - float time = player->get_current_animation_position(); + double time = player->get_current_animation_position(); if (current == player->get_assigned_animation() && player->is_playing()) { - player->stop(); //so it won't blend with itself + player->clear_caches(); //so it won't blend with itself } ERR_FAIL_COND_EDMSG(!_validate_tracks(player->get_animation(current)), "Animation tracks may have any invalid key, abort playing."); - player->seek(time); + player->seek(time, true, true); player->play(current); } @@ -281,12 +281,12 @@ void AnimationPlayerEditor::_play_bw_from_pressed() { String current = _get_current(); if (!current.is_empty()) { - float time = player->get_current_animation_position(); - if (current == player->get_assigned_animation()) { - player->stop(); //so it won't blend with itself + double time = player->get_current_animation_position(); + if (current == player->get_assigned_animation() && player->is_playing()) { + player->clear_caches(); //so it won't blend with itself } ERR_FAIL_COND_EDMSG(!_validate_tracks(player->get_animation(current)), "Animation tracks may have any invalid key, abort playing."); - player->seek(time); + player->seek(time, true, true); player->play_backwards(current); } diff --git a/misc/extension_api_validation/4.2-stable.expected b/misc/extension_api_validation/4.2-stable.expected index b27f80ee293..03c7a8291ac 100644 --- a/misc/extension_api_validation/4.2-stable.expected +++ b/misc/extension_api_validation/4.2-stable.expected @@ -364,3 +364,9 @@ GH-92322 Validate extension JSON: Error: Field 'classes/EditorInspectorPlugin/methods/add_property_editor/arguments': size changed value in new API, from 3 to 4. Optional arguments added. Compatibility methods registered. + +GH-86661 +-------- +Validate extension JSON: Error: Field 'classes/Animation/methods/track_find_key/arguments': size changed value in new API, from 3 to 5. + +Added optional argument to track_find_key to handle backward seeking. Compatibility method registered. diff --git a/scene/animation/animation_mixer.cpp b/scene/animation/animation_mixer.cpp index e600de6b8bb..5074145168a 100644 --- a/scene/animation/animation_mixer.cpp +++ b/scene/animation/animation_mixer.cpp @@ -1109,6 +1109,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) { real_t weight = ai.playback_info.weight; Vector track_weights = ai.playback_info.track_weights; bool backward = signbit(delta); // This flag is used by the root motion calculates or detecting the end of audio stream. + bool seeked_backward = signbit(p_delta); #ifndef _3D_DISABLED bool calc_root = !seeked || is_external_seeking; #endif // _3D_DISABLED @@ -1463,7 +1464,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) { } } else { if (seeked) { - int idx = a->track_find_key(i, time, is_external_seeking ? Animation::FIND_MODE_NEAREST : Animation::FIND_MODE_EXACT, true); + int idx = a->track_find_key(i, time, is_external_seeking ? Animation::FIND_MODE_NEAREST : Animation::FIND_MODE_EXACT, false, seeked_backward); if (idx < 0) { continue; } @@ -1744,8 +1745,10 @@ void AnimationMixer::_blend_apply() { case Animation::TYPE_VALUE: { TrackCacheValue *t = static_cast(track); - if (t->use_discrete && !t->use_continuous) { - t->is_init = true; // If only disctere value is applied, no more RESET. + if (callback_mode_discrete == ANIMATION_CALLBACK_MODE_DISCRETE_FORCE_CONTINUOUS) { + t->is_init = false; // Always update in Force Continuous. + } else if (!t->use_continuous && (t->use_discrete || !deterministic)) { + t->is_init = true; // If there is no continuous value and only disctere value is applied or just started, don't RESET. } if ((t->is_init && (is_zero_amount || !t->use_continuous)) || @@ -1756,9 +1759,7 @@ void AnimationMixer::_blend_apply() { break; // Don't overwrite the value set by UPDATE_DISCRETE. } - if (callback_mode_discrete == ANIMATION_CALLBACK_MODE_DISCRETE_FORCE_CONTINUOUS) { - t->is_init = false; // Always update in Force Continuous. - } else { + if (callback_mode_discrete != ANIMATION_CALLBACK_MODE_DISCRETE_FORCE_CONTINUOUS) { t->is_init = !t->use_continuous; // If there is no Continuous in non-Force Continuous type, it means RESET. } diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp index 0c24d79ad73..5756edaa488 100644 --- a/scene/animation/animation_player.cpp +++ b/scene/animation/animation_player.cpp @@ -234,6 +234,9 @@ void AnimationPlayer::_process_playback_data(PlaybackData &cd, double p_delta, f pi.delta = delta; pi.seeked = p_seeked; } + if (Math::is_zero_approx(pi.delta) && backwards) { + pi.delta = -0.0; // Sign is needed to handle converted Continuous track from Discrete track correctly. + } // AnimationPlayer doesn't have internal seeking. // However, immediately after playback, discrete keys should be retrieved with EXACT mode since behind keys must be ignored at that time. pi.is_external_seeking = !p_started; @@ -257,7 +260,7 @@ void AnimationPlayer::_blend_playback_data(double p_delta, bool p_started) { bool seeked = c.seeked; // The animation may be changed during process, so it is safer that the state is changed before process. - if (p_delta != 0) { + if (!Math::is_zero_approx(p_delta)) { c.seeked = false; } @@ -581,6 +584,8 @@ void AnimationPlayer::seek(double p_time, bool p_update, bool p_update_only) { return; } + bool is_backward = p_time < playback.current.pos; + _check_immediately_after_start(); playback.current.pos = p_time; @@ -596,7 +601,7 @@ void AnimationPlayer::seek(double p_time, bool p_update, bool p_update_only) { playback.seeked = true; if (p_update) { - _process_animation(0, p_update_only); + _process_animation(is_backward ? -0.0 : 0.0, p_update_only); playback.seeked = false; // If animation was proceeded here, no more seek in internal process. } } diff --git a/scene/resources/animation.compat.inc b/scene/resources/animation.compat.inc index d7d88677488..fc2672bb258 100644 --- a/scene/resources/animation.compat.inc +++ b/scene/resources/animation.compat.inc @@ -50,8 +50,8 @@ Variant Animation::_value_track_interpolate_bind_compat_86629(int p_track, doubl return value_track_interpolate(p_track, p_time, false); } -int Animation::_track_find_key_bind_compat_86661(int p_track, double p_time, FindMode p_find_mode) const { - return track_find_key(p_track, p_time, p_find_mode, false); +int Animation::_track_find_key_bind_compat_92861(int p_track, double p_time, FindMode p_find_mode) const { + return track_find_key(p_track, p_time, p_find_mode, false, false); } void Animation::_bind_compatibility_methods() { @@ -60,7 +60,7 @@ void Animation::_bind_compatibility_methods() { ClassDB::bind_compatibility_method(D_METHOD("scale_track_interpolate", "track_idx", "time_sec"), &Animation::_scale_track_interpolate_bind_compat_86629); ClassDB::bind_compatibility_method(D_METHOD("blend_shape_track_interpolate", "track_idx", "time_sec"), &Animation::_blend_shape_track_interpolate_bind_compat_86629); ClassDB::bind_compatibility_method(D_METHOD("value_track_interpolate", "track_idx", "time_sec"), &Animation::_value_track_interpolate_bind_compat_86629); - ClassDB::bind_compatibility_method(D_METHOD("track_find_key", "track_idx", "time", "find_mode"), &Animation::_track_find_key_bind_compat_86661, DEFVAL(FIND_MODE_NEAREST)); + ClassDB::bind_compatibility_method(D_METHOD("track_find_key", "track_idx", "time", "find_mode"), &Animation::_track_find_key_bind_compat_92861, DEFVAL(FIND_MODE_NEAREST)); } #endif // DISABLE_DEPRECATED diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp index a3bfa987c6a..254bd38be75 100644 --- a/scene/resources/animation.cpp +++ b/scene/resources/animation.cpp @@ -1496,7 +1496,7 @@ void Animation::track_remove_key(int p_track, int p_idx) { emit_changed(); } -int Animation::track_find_key(int p_track, double p_time, FindMode p_find_mode, bool p_limit) const { +int Animation::track_find_key(int p_track, double p_time, FindMode p_find_mode, bool p_limit, bool p_backward) const { ERR_FAIL_INDEX_V(p_track, tracks.size(), -1); Track *t = tracks[p_track]; @@ -1518,7 +1518,7 @@ int Animation::track_find_key(int p_track, double p_time, FindMode p_find_mode, return key_index; } - int k = _find(tt->positions, p_time, false, p_limit); + int k = _find(tt->positions, p_time, p_backward, p_limit); if (k < 0 || k >= tt->positions.size()) { return -1; } @@ -1545,7 +1545,7 @@ int Animation::track_find_key(int p_track, double p_time, FindMode p_find_mode, return key_index; } - int k = _find(rt->rotations, p_time, false, p_limit); + int k = _find(rt->rotations, p_time, p_backward, p_limit); if (k < 0 || k >= rt->rotations.size()) { return -1; } @@ -1572,7 +1572,7 @@ int Animation::track_find_key(int p_track, double p_time, FindMode p_find_mode, return key_index; } - int k = _find(st->scales, p_time, false, p_limit); + int k = _find(st->scales, p_time, p_backward, p_limit); if (k < 0 || k >= st->scales.size()) { return -1; } @@ -1599,7 +1599,7 @@ int Animation::track_find_key(int p_track, double p_time, FindMode p_find_mode, return key_index; } - int k = _find(bst->blend_shapes, p_time, false, p_limit); + int k = _find(bst->blend_shapes, p_time, p_backward, p_limit); if (k < 0 || k >= bst->blend_shapes.size()) { return -1; } @@ -1611,7 +1611,7 @@ int Animation::track_find_key(int p_track, double p_time, FindMode p_find_mode, } break; case TYPE_VALUE: { ValueTrack *vt = static_cast(t); - int k = _find(vt->values, p_time, false, p_limit); + int k = _find(vt->values, p_time, p_backward, p_limit); if (k < 0 || k >= vt->values.size()) { return -1; } @@ -1623,7 +1623,7 @@ int Animation::track_find_key(int p_track, double p_time, FindMode p_find_mode, } break; case TYPE_METHOD: { MethodTrack *mt = static_cast(t); - int k = _find(mt->methods, p_time, false, p_limit); + int k = _find(mt->methods, p_time, p_backward, p_limit); if (k < 0 || k >= mt->methods.size()) { return -1; } @@ -1635,7 +1635,7 @@ int Animation::track_find_key(int p_track, double p_time, FindMode p_find_mode, } break; case TYPE_BEZIER: { BezierTrack *bt = static_cast(t); - int k = _find(bt->values, p_time, false, p_limit); + int k = _find(bt->values, p_time, p_backward, p_limit); if (k < 0 || k >= bt->values.size()) { return -1; } @@ -1647,7 +1647,7 @@ int Animation::track_find_key(int p_track, double p_time, FindMode p_find_mode, } break; case TYPE_AUDIO: { AudioTrack *at = static_cast(t); - int k = _find(at->values, p_time, false, p_limit); + int k = _find(at->values, p_time, p_backward, p_limit); if (k < 0 || k >= at->values.size()) { return -1; } @@ -1659,7 +1659,7 @@ int Animation::track_find_key(int p_track, double p_time, FindMode p_find_mode, } break; case TYPE_ANIMATION: { AnimationTrack *at = static_cast(t); - int k = _find(at->values, p_time, false, p_limit); + int k = _find(at->values, p_time, p_backward, p_limit); if (k < 0 || k >= at->values.size()) { return -1; } @@ -3836,7 +3836,7 @@ void Animation::_bind_methods() { ClassDB::bind_method(D_METHOD("track_get_key_count", "track_idx"), &Animation::track_get_key_count); ClassDB::bind_method(D_METHOD("track_get_key_value", "track_idx", "key_idx"), &Animation::track_get_key_value); ClassDB::bind_method(D_METHOD("track_get_key_time", "track_idx", "key_idx"), &Animation::track_get_key_time); - ClassDB::bind_method(D_METHOD("track_find_key", "track_idx", "time", "find_mode", "limit"), &Animation::track_find_key, DEFVAL(FIND_MODE_NEAREST), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("track_find_key", "track_idx", "time", "find_mode", "limit", "backward"), &Animation::track_find_key, DEFVAL(FIND_MODE_NEAREST), DEFVAL(false), DEFVAL(false)); ClassDB::bind_method(D_METHOD("track_set_interpolation_type", "track_idx", "interpolation"), &Animation::track_set_interpolation_type); ClassDB::bind_method(D_METHOD("track_get_interpolation_type", "track_idx"), &Animation::track_get_interpolation_type); diff --git a/scene/resources/animation.h b/scene/resources/animation.h index 604bce497ac..e9bfc298a5e 100644 --- a/scene/resources/animation.h +++ b/scene/resources/animation.h @@ -388,7 +388,7 @@ protected: Vector3 _scale_track_interpolate_bind_compat_86629(int p_track, double p_time) const; float _blend_shape_track_interpolate_bind_compat_86629(int p_track, double p_time) const; Variant _value_track_interpolate_bind_compat_86629(int p_track, double p_time) const; - int _track_find_key_bind_compat_86661(int p_track, double p_time, FindMode p_find_mode = FIND_MODE_NEAREST) const; + int _track_find_key_bind_compat_92861(int p_track, double p_time, FindMode p_find_mode = FIND_MODE_NEAREST) const; static void _bind_compatibility_methods(); #endif // DISABLE_DEPRECATED @@ -423,7 +423,7 @@ public: void track_set_key_transition(int p_track, int p_key_idx, real_t p_transition); void track_set_key_value(int p_track, int p_key_idx, const Variant &p_value); void track_set_key_time(int p_track, int p_key_idx, double p_time); - int track_find_key(int p_track, double p_time, FindMode p_find_mode = FIND_MODE_NEAREST, bool p_limit = false) const; + int track_find_key(int p_track, double p_time, FindMode p_find_mode = FIND_MODE_NEAREST, bool p_limit = false, bool p_backward = false) const; void track_remove_key(int p_track, int p_idx); void track_remove_key_at_time(int p_track, double p_time); int track_get_key_count(int p_track) const;