godotengine / godot-proposals

Godot Improvement Proposals (GIPs)
MIT License
1.14k stars 93 forks source link

Add an "animation about to start" signal #8474

Open nlupugla opened 10 months ago

nlupugla commented 10 months ago

Describe the project you are working on

A 2d game with JRPG and visual-novel elements.

Describe the problem or limitation you are having in your project

I want to be able to run some setup code just before an animation plays. Responding to the AnimationPlayer's animation_started was my first thought, but this seems to be emitted only after the animation starts so it's too late to run certain initialization routines.

For example, I have a health bar that I animate to drain slowly from its current value to a specified final value which needs to be synchronized with other visual and sound effects. Just before the animation starts, I need to grab the current value of the health bar. Doing this in response to animation_started is too late because the value has already been changed to its default value.

Another example is animating one character dashing over to another. Just before this animation starts, I need to set the start and end point of the dash. Doing this in response to animation_started is too late.

Describe the feature / enhancement and how it helps to overcome the problem or limitation

Emitting a signal just before the animation actually starts would give users a chance to run custom initialization logic for each animation. For example, you could grab a snapshot of animated properties as they are just before an animation starts changes them.

Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams

AnimationPlayer (and any other relevant animation nodes) would emit a signal indicating that an animation is about to start. Perhaps just after this block of code: https://github.com/godotengine/godot/blob/80de898d721f952dac0b102d48bb73d6b02ee1e8/scene/animation/animation_player.cpp#L364-L374.

I'm not entirely sure what the best name for this signal would be but I have a few ideas: animation_initialized(anim_name), animation_prepared(anim_name), animation_starting(anim_name).

If this enhancement will not be used often, can it be worked around with a few lines of script?

I have thought of several workarounds, but they all have awkward consequences. The most straightforward workaround is to extend AnimationPlayer and add a few wrapper methods like

func play(anim_name : StringName):
    animation_initialized.emit(anim_name)
    super.play(anim_name)

The issue with this approach is that it makes coordinating multiple animation players awkward. For example, instead of using an AnimationPlaybackTrack, you have to use a CallMethodTrack.

Is there a reason why this should be core and not an add-on in the asset library?

The animation workflow is core.

AThousandShips commented 10 months ago

If you are calling play directly a signal doesn't make much sense, but on transitions it could make sense

Are the signals animation_changed or current_animation_changed also not usable for this?

I'm not sure how advisable it is to depend on the state of the animation player like this, instead maybe you should separate the logic and have the control of the animation also control this

Also have you tried method tracks and other ways to have the animation influence things directly?

nlupugla commented 10 months ago

Thanks for the comment AThousandShips!

Are the signals animation_changed or current_animation_changed also not usable for this?

Do animation_changed or current_animation_changed get emitted even if the next animation you play is the same as the previous? I have several cases where I want to play the same animation, but with a slightly different setup. For example, if one of my characters dashes to another, but mid-dash the player cancels and dashes to a different character, the requested animation is "dash" in both cases, so I'm not sure whether the changed signals would get emitted. If it does, I think that solves my issue. If it doesn't it might be nice to update the documentation to make that clear.

I'm not sure how advisable it is to depend on the state of the animation player like this, instead maybe you should separate the logic and have the control of the animation also control this

This is another approach I considered, but it has a few issues. The first is that if I want to play the animation from another animation player, I have to use a CallMethodTrack instead of an AnimationPlaybackTrack. This makes for an awkward UX where, for example, I don't get to see the length of the animation as I would with an AnimationPlaybackTrack. The second is that I can't test the animation as easily with the bottom panel animation plugin. For example, clicking the "play" button at the bottom wouldn't execute my custom play logic. Neither of these issues are "game breaking" so to speak, but they do call for awkward workarounds.

AThousandShips commented 10 months ago

I haven't tested them extensively but I also don't know the exact details of your needed situation so I'd suggest you test them out to see if they're sufficient

nlupugla commented 10 months ago

I did some testing and it looks these signals are also emitted too late for what I want.