godot
godot copied to clipboard
Animation CAPTURE mode does not work in AnimationTree
Godot version
3.3.2
System information
Windows 10, GLES3, Intel HD Graphics 4600
Issue description
I created an animation using the CAPTURE transition mode. I expected this to work in an AnimationTreeStateMachine, with the captured tween starting at the node's position when the animation starts. Instead, it acts just like a regular transition mode, causing the property to be insta-set to the first key.
Steps to reproduce

When run as an AnimationPlayer using $AnimationPlayer.start("animation"), it works as expected; however, when placing the animation in an AnimationTreeStateMachine and using $AnimationTree['parameters/playback'].travel("animation") it does not do any capturing and starts the node property at the first key.
https://user-images.githubusercontent.com/23728939/142555174-33609e40-8cd3-4c34-a507-64183f81622c.mp4
It looks as though the AnimationTree does not handle the CAPTURE mode.
Animation_Player.cpp, line 400:
case Animation::TYPE_VALUE: {
Map<StringName, TrackNodeCache::PropertyAnim>::Element *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();
Animation::UpdateMode update_mode = a->value_track_get_update_mode(i);
if (update_mode == Animation::UPDATE_CAPTURE) {
if (p_started || pa->capture == Variant()) {
pa->capture = pa->object->get_indexed(pa->subpath);
}
int key_count = a->track_get_key_count(i);
if (key_count == 0)
continue; //eeh not worth it
float first_key_time = a->track_get_key_time(i, 0);
float transition = 1.0;
int first_key = 0;
if (first_key_time == 0.0) {
//ignore, use for transition
if (key_count == 1)
continue; //with one key we can't do anything
transition = a->track_get_key_transition(i, 0);
first_key_time = a->track_get_key_time(i, 1);
first_key = 1;
}
if (p_time < first_key_time) {
float c = Math::ease(p_time / first_key_time, transition);
Variant first_value = a->track_get_key_value(i, first_key);
Variant interp_value;
Variant::interpolate(pa->capture, first_value, c, interp_value);
if (pa->accum_pass != accum_pass) {
ERR_CONTINUE(cache_update_prop_size >= NODE_CACHE_UPDATE_MAX);
cache_update_prop[cache_update_prop_size++] = pa;
pa->value_accum = interp_value;
pa->accum_pass = accum_pass;
} else {
Variant::interpolate(pa->value_accum, interp_value, p_interp, pa->value_accum);
}
continue; //handled
}
}
if (update_mode == Animation::UPDATE_CONTINUOUS || update_mode == Animation::UPDATE_CAPTURE || (p_delta == 0 && update_mode == Animation::UPDATE_DISCRETE)) { //delta == 0 means seek
Variant value = a->value_track_interpolate(i, p_time);
...
Animation_Tree.cpp, line 937:
case Animation::TYPE_VALUE: {
TrackCacheValue *t = static_cast<TrackCacheValue *>(track);
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
Variant value = a->value_track_interpolate(i, time);
...
Both use a->value_track_interpolation(), but the AnimationTree does not intercept the request if p_time < first_time_key like the AnimationPlayer does.
Minimal reproduction project
May be related by #48432.
It's a stretch 2+ years later, but in case you still have a project trying to make use of CAPTURE in 4.x, could you assess whether #86715 solves the issue the way you would expect it to? You can test the PR by download a build (artifact) for your platform from the "Checks" (CI) tab.
@akien-mga I'll do it tonight!
@akien-mga No, it's not fixed. And it may be because capture node isn't working for AnimationPlayer either. So it's just straight-up broken.
https://github.com/godotengine/godot/assets/23728939/f82da7ee-0479-47ed-8704-b13dfaf87137
Note, if I go back to 4.1 and test it out, the AnimationPlayer IS working with capture (although AnimationTree isn't), so it must have been broken by this commit or a recent one.
https://github.com/godotengine/godot/assets/23728939/677492ac-65dc-4058-ae76-a3f591ead287
@kingoftheconnors You should read the PR description. Currently, the editor does not playback the capture so you must use capture() method in script to interpolate capture frame.
@TokageItLab Ah, thanks!
I've tested it out using capture(), and while running AnimationPlayer directly from a script DOES work, AnimationTree does not:
extends Timer
var is_left : bool = false
func _on_timeout() -> void:
var animator : AnimationPlayer = get_node("../AnimationPlayer")
var animation_tree : AnimationTree = get_node("../AnimationTree")
if is_left:
#animator.play_with_capture("go_right") #This DOES work
animator.capture("go_right", 1.0)
animation_tree['parameters/playback'].travel("go_right")
else:
#animator.play_with_capture("go_left") #This DOES work
animator.capture("go_left", 1.0)
animation_tree['parameters/playback'].travel("go_left")
is_left = !is_left
https://github.com/godotengine/godot/assets/23728939/800619d5-b98b-41bd-9889-274b341d0e75
I also tested it with start(), instead of travel() and it was the same deal.
Use animation_tree.capture(), not animation_player.capture() if you want to use AnimationTree.
Ah, that works!
I can confirm this issue is fixed. I'll close this.