Skip to content

Animation events aren't fired for frame-by-frame control animations #21122

@hansler

Description

@hansler

Note: This could be considered either a bug report or a feature request. I'm labeling this as a bug report because this used to work before #15677 landed.

tl;dr: Bevy animation events fire when animations are progressed via play, but not when progressed manually via pause + seek_to.

Bevy version and features

0.17.0-rc-1, though this has existed since 0.16 or earlier as well.

Background

There are two approaches to animation for games:

  1. Time-based animation. This is the most common approach and is the one used in all of Bevy's example code. Given an animation, at some point a .play() method is called, and the animation automatically progresses over time, possibly on a loop or with a simple speed multiplier.
  2. Frame-by-frame animation. Under this method, given an animation, some code that runs each frame manually sets the desired frame or timestamp.

Time-based animation is usually the go-to, but there are times when frame-by-frame animation is needed. If animations need to be frame-perfect, synchronized over the network, variable based on gameplay mechanics, procedural, or able to be reversible or skip frames, then animations need frame-by-frame control. For example, fighting games use this since the displayed animation frame needs to be perfectly in sync with hitboxes, network delays, etc. Or a platformer game might manually set "jump" animation frames based on the character's vertical velocity or distance from ground.

You can do time-based animations in Bevy using AnimationPlayer's play method. Or, you can do frame-by-frame animations by keeping an animation paused and using AnimationPlayer's seek_to or set_seek_time methods.

What you did

I modified the animated_mesh_events example to use frame-by-frame animations instead of time-based animations to demonstrate the issue. See the code here: hansler@e7ba0fe

Just to show a close-to-real-world example, I tied the animation's speed to a sin wave that varies from 10% to 300% over time.

What went wrong

The animation works fine this way, but animation events don't fire. I think animation events (or at least #15677) are assuming that all animations done with AnimationPlayer are time-based, not frame-by-frame. If I revert the two lines of code changed in #15677 (which my branch does here: hansler@e7ba0fe), then events work again.

Current state:

2.mp4

With #15677 reverted:

1.mp4

#15677 was intended to fix a different bug: paused animations causing events to fire every frame if paused at just the right moment. I think we need a way to fix that bug while still allowing "paused" animations to fire events so we can support animations which progress via seek_to rather than play.

As an aside, there is some API weirdness when manually advancing animations frame-by-frame that you can see in my example code. For example, you need to initially start the animation using player.play(animations.index).pause();, because play sets the animation as active (which we want) and also starts the animation (which we don't want). So beyond this bug I think there might be room for API improvements for this use-case.

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-AnimationMake things move and change over timeC-BugAn unexpected or incorrect behavior

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions