godotengine / godot

Godot Engine – Multi-platform 2D and 3D game engine
https://godotengine.org
MIT License
91.1k stars 21.18k forks source link

Callback from animation tree gets called forever if in the end of an animation #39711

Open fbcosentino opened 4 years ago

fbcosentino commented 4 years ago

Godot version:

3.2.1 stable

OS/device including version:

Windows 7, Intel 64bit, GLES2

Issue description:

If an animation comes from AnimationTree, and the track has a callback in the end of the animation, that callback will be called in repetition forever. If the animation is called from the AnimationPlayer directly it doesn't happen. If the callback is not in the end of animation, it doesn't happen as well. Only happens when called from AnimationTree and the key is in the end of animation. Tested with state machine root only.

Steps to reproduce:

callback will be called in infinite loop

Minimal reproduction project:

In the example below two callbacks are set. The only difference is callback 1 key frame is in the middle of animation, callback 2 is in the end. Callback 1 gets called only once, callback 2 gets called forever. AnimCallbackTest.zip

Sl3dge78 commented 4 years ago

Here are a few results from my preliminary investigation:

In the Animation Tree code, there is a check that doesn't call the method if delta is zero : https://github.com/godotengine/godot/blob/05395cb14fa0fbd270f6d8c63bcb7b80f45f4dfe/scene/animation/animation_tree.cpp#L969-L972

Seems great, however, delta is almost never 0 ; it still has a value set when going from 1 to 1 because it doesn't get updated line 106 here (step is the delta): https://github.com/godotengine/godot/blob/05395cb14fa0fbd270f6d8c63bcb7b80f45f4dfe/scene/animation/animation_blend_tree.cpp#L88-L107

So far I've tried two things :

To me the issue is that a new Animation State doesn't get created if we go from 1 to 1 (because it's not a change, so the blend_animation doesn't get called), and as we read the delta from the states, it always reads a value, so never skips the method call.