bevyengine / bevyengine/bevy

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

Open
#21,122 0 comments 0 reactions 0 assignees View on GitHub
A-Animation C-Bug
Dominant language
Rust
Stars
48.2k
Forks
4.8k
Avg merge
3d 16h
Merged PRs (30d)
171

Description

_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: https://github.com/hansler/bevy/commit/e7ba0fe863f6ed2141696347ebadc9f3b3ca5b3c

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: https://github.com/hansler/bevy/commit/e7ba0fe863f6ed2141696347ebadc9f3b3ca5b3c), then events work again.

Current state:

https://github.com/user-attachments/assets/2ec15743-1e1e-4c4a-90d7-effb81290a97

With #15677 reverted:

https://github.com/user-attachments/assets/205d78f9-134e-4536-81b1-53c170cc1d93

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

Contributor guide

Open the contributing guide

Research direction

Start with the modified `animated_mesh_events` example and the `AnimationPlayer` paths involving `play`, `pause`, `seek_to`, and `set_seek_time`. Compare the behavior introduced by #15677 with the linked reproduction commits. Done means animation events fire when paused animations advance manually, without firing repeatedly when a paused animation remains at the same time.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
game-dev
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.