Fix seeking Animation immediate after playback for Discrete track

This commit is contained in:
Silc Lizard (Tokage) Renew 2024-06-07 17:51:19 +09:00
parent e96ad5af98
commit bea47d877b
8 changed files with 47 additions and 31 deletions

View File

@ -360,9 +360,12 @@
<param index="1" name="time" type="float" /> <param index="1" name="time" type="float" />
<param index="2" name="find_mode" type="int" enum="Animation.FindMode" default="0" /> <param index="2" name="find_mode" type="int" enum="Animation.FindMode" default="0" />
<param index="3" name="limit" type="bool" default="false" /> <param index="3" name="limit" type="bool" default="false" />
<param index="4" name="backward" type="bool" default="false" />
<description> <description>
Finds the key index by time in a given track. Optionally, only find it if the approx/exact time is given. 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 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.
</description> </description>
</method> </method>
<method name="track_get_interpolation_loop_wrap" qualifiers="const"> <method name="track_get_interpolation_loop_wrap" qualifiers="const">
@ -583,6 +586,7 @@
<param index="2" name="backward" type="bool" default="false" /> <param index="2" name="backward" type="bool" default="false" />
<description> <description>
Returns the interpolated value at the given time (in seconds). The [param track_idx] must be the index of a value track. 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].
</description> </description>
</method> </method>
<method name="value_track_set_update_mode"> <method name="value_track_set_update_mode">

View File

@ -243,12 +243,12 @@ void AnimationPlayerEditor::_play_from_pressed() {
String current = _get_current(); String current = _get_current();
if (!current.is_empty()) { 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()) { 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."); 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); player->play(current);
} }
@ -281,12 +281,12 @@ void AnimationPlayerEditor::_play_bw_from_pressed() {
String current = _get_current(); String current = _get_current();
if (!current.is_empty()) { if (!current.is_empty()) {
float time = player->get_current_animation_position(); double time = player->get_current_animation_position();
if (current == player->get_assigned_animation()) { 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."); 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); player->play_backwards(current);
} }

View File

@ -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. 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. 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.

View File

@ -1109,6 +1109,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
real_t weight = ai.playback_info.weight; real_t weight = ai.playback_info.weight;
Vector<real_t> track_weights = ai.playback_info.track_weights; Vector<real_t> 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 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 #ifndef _3D_DISABLED
bool calc_root = !seeked || is_external_seeking; bool calc_root = !seeked || is_external_seeking;
#endif // _3D_DISABLED #endif // _3D_DISABLED
@ -1463,7 +1464,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
} }
} else { } else {
if (seeked) { 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) { if (idx < 0) {
continue; continue;
} }
@ -1744,8 +1745,10 @@ void AnimationMixer::_blend_apply() {
case Animation::TYPE_VALUE: { case Animation::TYPE_VALUE: {
TrackCacheValue *t = static_cast<TrackCacheValue *>(track); TrackCacheValue *t = static_cast<TrackCacheValue *>(track);
if (t->use_discrete && !t->use_continuous) { if (callback_mode_discrete == ANIMATION_CALLBACK_MODE_DISCRETE_FORCE_CONTINUOUS) {
t->is_init = true; // If only disctere value is applied, no more RESET. 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)) || 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. break; // Don't overwrite the value set by UPDATE_DISCRETE.
} }
if (callback_mode_discrete == ANIMATION_CALLBACK_MODE_DISCRETE_FORCE_CONTINUOUS) { if (callback_mode_discrete != ANIMATION_CALLBACK_MODE_DISCRETE_FORCE_CONTINUOUS) {
t->is_init = false; // Always update in Force Continuous.
} else {
t->is_init = !t->use_continuous; // If there is no Continuous in non-Force Continuous type, it means RESET. t->is_init = !t->use_continuous; // If there is no Continuous in non-Force Continuous type, it means RESET.
} }

View File

@ -234,6 +234,9 @@ void AnimationPlayer::_process_playback_data(PlaybackData &cd, double p_delta, f
pi.delta = delta; pi.delta = delta;
pi.seeked = p_seeked; 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. // 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. // 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; 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. 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; c.seeked = false;
} }
@ -581,6 +584,8 @@ void AnimationPlayer::seek(double p_time, bool p_update, bool p_update_only) {
return; return;
} }
bool is_backward = p_time < playback.current.pos;
_check_immediately_after_start(); _check_immediately_after_start();
playback.current.pos = p_time; 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; playback.seeked = true;
if (p_update) { 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. playback.seeked = false; // If animation was proceeded here, no more seek in internal process.
} }
} }

View File

@ -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); 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 { 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); return track_find_key(p_track, p_time, p_find_mode, false, false);
} }
void Animation::_bind_compatibility_methods() { 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("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("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("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 #endif // DISABLE_DEPRECATED

View File

@ -1496,7 +1496,7 @@ void Animation::track_remove_key(int p_track, int p_idx) {
emit_changed(); 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); ERR_FAIL_INDEX_V(p_track, tracks.size(), -1);
Track *t = tracks[p_track]; 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; 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()) { if (k < 0 || k >= tt->positions.size()) {
return -1; return -1;
} }
@ -1545,7 +1545,7 @@ int Animation::track_find_key(int p_track, double p_time, FindMode p_find_mode,
return key_index; 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()) { if (k < 0 || k >= rt->rotations.size()) {
return -1; return -1;
} }
@ -1572,7 +1572,7 @@ int Animation::track_find_key(int p_track, double p_time, FindMode p_find_mode,
return key_index; 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()) { if (k < 0 || k >= st->scales.size()) {
return -1; return -1;
} }
@ -1599,7 +1599,7 @@ int Animation::track_find_key(int p_track, double p_time, FindMode p_find_mode,
return key_index; 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()) { if (k < 0 || k >= bst->blend_shapes.size()) {
return -1; return -1;
} }
@ -1611,7 +1611,7 @@ int Animation::track_find_key(int p_track, double p_time, FindMode p_find_mode,
} break; } break;
case TYPE_VALUE: { case TYPE_VALUE: {
ValueTrack *vt = static_cast<ValueTrack *>(t); ValueTrack *vt = static_cast<ValueTrack *>(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()) { if (k < 0 || k >= vt->values.size()) {
return -1; return -1;
} }
@ -1623,7 +1623,7 @@ int Animation::track_find_key(int p_track, double p_time, FindMode p_find_mode,
} break; } break;
case TYPE_METHOD: { case TYPE_METHOD: {
MethodTrack *mt = static_cast<MethodTrack *>(t); MethodTrack *mt = static_cast<MethodTrack *>(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()) { if (k < 0 || k >= mt->methods.size()) {
return -1; return -1;
} }
@ -1635,7 +1635,7 @@ int Animation::track_find_key(int p_track, double p_time, FindMode p_find_mode,
} break; } break;
case TYPE_BEZIER: { case TYPE_BEZIER: {
BezierTrack *bt = static_cast<BezierTrack *>(t); BezierTrack *bt = static_cast<BezierTrack *>(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()) { if (k < 0 || k >= bt->values.size()) {
return -1; return -1;
} }
@ -1647,7 +1647,7 @@ int Animation::track_find_key(int p_track, double p_time, FindMode p_find_mode,
} break; } break;
case TYPE_AUDIO: { case TYPE_AUDIO: {
AudioTrack *at = static_cast<AudioTrack *>(t); AudioTrack *at = static_cast<AudioTrack *>(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()) { if (k < 0 || k >= at->values.size()) {
return -1; return -1;
} }
@ -1659,7 +1659,7 @@ int Animation::track_find_key(int p_track, double p_time, FindMode p_find_mode,
} break; } break;
case TYPE_ANIMATION: { case TYPE_ANIMATION: {
AnimationTrack *at = static_cast<AnimationTrack *>(t); AnimationTrack *at = static_cast<AnimationTrack *>(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()) { if (k < 0 || k >= at->values.size()) {
return -1; 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_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_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_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_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); ClassDB::bind_method(D_METHOD("track_get_interpolation_type", "track_idx"), &Animation::track_get_interpolation_type);

View File

@ -388,7 +388,7 @@ protected:
Vector3 _scale_track_interpolate_bind_compat_86629(int p_track, double p_time) const; 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; 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; 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(); static void _bind_compatibility_methods();
#endif // DISABLE_DEPRECATED #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_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_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); 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(int p_track, int p_idx);
void track_remove_key_at_time(int p_track, double p_time); void track_remove_key_at_time(int p_track, double p_time);
int track_get_key_count(int p_track) const; int track_get_key_count(int p_track) const;