godot icon indicating copy to clipboard operation
godot copied to clipboard

Animation CAPTURE mode does not work in AnimationTree

Open kingoftheconnors opened this issue 4 years ago • 3 comments

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

image

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

AnimationTreeProblem.zip

kingoftheconnors avatar Nov 19 '21 02:11 kingoftheconnors

May be related by #48432.

TokageItLab avatar Nov 27 '21 00:11 TokageItLab

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 avatar Feb 08 '24 14:02 akien-mga

@akien-mga I'll do it tonight!

kingoftheconnors avatar Feb 08 '24 19:02 kingoftheconnors

@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 avatar Feb 09 '24 14:02 kingoftheconnors

@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 avatar Feb 09 '24 15:02 TokageItLab

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

kingoftheconnors avatar Feb 09 '24 19:02 kingoftheconnors

Use animation_tree.capture(), not animation_player.capture() if you want to use AnimationTree.

TokageItLab avatar Feb 09 '24 22:02 TokageItLab

Ah, that works!

I can confirm this issue is fixed. I'll close this.

kingoftheconnors avatar Feb 09 '24 23:02 kingoftheconnors