Skip to content

added some simple functions to AnimationPlayer#5912

Closed
asafigan wants to merge 4 commits intobevyengine:mainfrom
asafigan:convenient-animation-player
Closed

added some simple functions to AnimationPlayer#5912
asafigan wants to merge 4 commits intobevyengine:mainfrom
asafigan:convenient-animation-player

Conversation

@asafigan
Copy link
Copy Markdown
Contributor

@asafigan asafigan commented Sep 8, 2022

Objective

Fixes #5848.

Solution

  • Adding function to get animation_clip from AnimationPlayer while documenting that it could be the default handle.
  • Adding a function which tells if the animation player has finished playing the animation_clip.

The language used to explain what the default handle implies could be more clear. My problem is that the default handle could actually refer to an actual animation if an AnimationClip is set for the default handle, which leads me to ask, "Should animation_clip should be an Option?"

@alice-i-cecile alice-i-cecile added A-Animation Make things move and change over time C-Usability A targeted quality-of-life change that makes Bevy easier to use labels Sep 8, 2022
@asafigan
Copy link
Copy Markdown
Contributor Author

asafigan commented Sep 8, 2022

I changed it to an option which was a really simple change.

if elapsed < 0.0 {
elapsed += animation_clip.duration;
} else if elapsed > animation_clip.duration {
player.finished = true;
Copy link
Copy Markdown
Contributor

@afonsolage afonsolage Sep 8, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this information (player.finished) be used to avoid calculating the elapsed time over and over again?

like add it together if paused check:

if player.finished || (player.paused && !player.is_changed()) {
    continue;
}

Copy link
Copy Markdown
Contributor Author

@asafigan asafigan Sep 8, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well I think:

if (player.paused || player.finished) && !player.is_changed() {
    continue;
}

would be more correct with this implementation, because some field like repeat could have changed.

@mockersf
Copy link
Copy Markdown
Member

mockersf commented Sep 8, 2022

I have a change stashed locally that does this and a bit more, but it's not finished yet because it's actually a bit more complicated than that.

I don't think this PR handles animations being played backward, repeating animations or setting the elapsed manually

@asafigan
Copy link
Copy Markdown
Contributor Author

asafigan commented Sep 8, 2022

So for playing backward. It looks like the animation will repeat even if repeat is false. But I didn't change that. I could fix it in this PR.

In this PR, repeating animations never finish. The correctness of this is up to debate.

Setting elapsed manually has a one frame delay before finished is updated. This is unavoidable unless the AnimationPlayer keeps track of the AnimationClip's duration. It would take a frame to get this information whenever the animation player starts playing an animation. It could also be temporarily incorrect if the AnimationClip in Assets has been changed.

Which of these would you like me to work on?

@mockersf
Copy link
Copy Markdown
Member

mockersf commented Sep 9, 2022

Which of these would you like me to work on?

I actually haven't found yet a fix that makes me happy, which is why I didn't open the PR. Try some things, let's see how it works out 😄

Another thing about repeat that I don't like if you have ideas on how to fix: setting repeat to false on a currently playing and repeating animation should let the current loop finish before stopping. This is currently true if done during the first repeat, not during others

@asafigan
Copy link
Copy Markdown
Contributor Author

So for playing backward. It looks like the animation will repeat even if repeat is false. But I didn't change that. I could fix it in this PR.

In this PR, repeating animations never finish. The correctness of this is up to debate.

After a deeper look this is actually incorrect. I will set up so test cases and iterate over some solutions.

@asafigan
Copy link
Copy Markdown
Contributor Author

What should the behavior of a non-repeating animation player with a negative speed at time 0?

Should it stay on the first frame or should it play the entire animation backwards. What should happen if speed is negative with any positive time? Should you have to set the time to the duration of the animation to play it once backwards? What if you want to play it forward and then backward?

With the current implementation, it seems like a non-repeating animation with negative speed set to the duration of the animation will play the animation backward twice.

Also I find it odd that the elapsed time goes up (or down if negative) forever. This is also weird because a non-repeating backward animation with an elapsed time greater than the duration of the animation will be on the last frame for a period of time before playing the animation backward (twice).

Copy link
Copy Markdown
Member

@james7132 james7132 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM other than this one nit.

) {
for (entity, mut player) in &mut animation_players {
if let Some(animation_clip) = animations.get(&player.animation_clip) {
if let Some(animation_clip) = player.animation_clip().and_then(|x| animations.get(x)) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd actually prefer for us to use let ... else with continue here instead of and_then inside the if expression.

@james7132 james7132 requested review from afonsolage, alice-i-cecile and tguichaoua and removed request for afonsolage and tguichaoua December 17, 2022 00:44
@james7132
Copy link
Copy Markdown
Member

james7132 commented Dec 17, 2022

What should the behavior of a non-repeating animation player with a negative speed at time 0?
Should it stay on the first frame or should it play the entire animation backwards. What should happen if speed is negative with any positive time? Should you have to set the time to the duration of the animation to play it once backwards? What if you want to play it forward and then backward?

IMO, we should treat non-repeating behavior as a clamp on the time modulus the length of the clip. If it's not repeating and the speed is negative, we should play to the beginning then mark it finished. This is essentially playing the clip backwards, and allows users to ping-pong the clip if they so desire. It's the least surprising interaction between these two features.

@alice-i-cecile alice-i-cecile added the S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it label Jun 30, 2023
@alice-i-cecile
Copy link
Copy Markdown
Member

@asafigan can you resolve the merge conflicts? I'd like to get this moving again and they're more complex than I want to tackle in the web UI :)

@alice-i-cecile
Copy link
Copy Markdown
Member

@DevinLeamy your review here would be welcome, since you're building on it.

@alice-i-cecile alice-i-cecile removed the S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it label Jun 30, 2023
@DevinLeamy
Copy link
Copy Markdown
Contributor

@DevinLeamy your review here would be welcome, since you're building on it.

I'm on board with the changes here! IMO it probably would be easiest to patch them into #9002. @alice-i-cecile Any opposition to doing that?

@alice-i-cecile
Copy link
Copy Markdown
Member

No objections; just credit the original author at the top of your PR description :)

@DevinLeamy
Copy link
Copy Markdown
Contributor

No objections; just credit the original author at the top of your PR description :)

Of course! It's been updated.

@alice-i-cecile
Copy link
Copy Markdown
Member

Updated and incorporated into #9002. Thanks a ton for your work, and if you get a chance your review there would be welcome!

github-merge-queue bot pushed a commit that referenced this pull request Aug 28, 2023
# Objective

Added `AnimationPlayer` API UX improvements. 

- Succestor to #5912
- Fixes #5848

_(Credits to @asafigan for filing #5848, creating the initial pull
request, and the discussion in #5912)_
## Solution

- Created `RepeatAnimation` enum to describe an animation repetition
behavior.
- Added `is_finished()`, `set_repeat()`, and `is_playback_reversed()`
methods to the animation player.
- ~~Made the animation clip optional as per the comment from #5912~~
> ~~My problem is that the default handle [used the initialize a
`PlayingAnimation`] could actually refer to an actual animation if an
AnimationClip is set for the default handle, which leads me to ask,
"Should animation_clip should be an Option?"~~
- Added an accessor for the animation clip `animation_clip()` to the
animation player.

To determine if an animation is finished, we use the number of times the
animation has completed and the repetition behavior. If the animation is
playing in reverse then `elapsed < 0.0` counts as a completion.
Otherwise, `elapsed > animation.duration` counts as a completion. This
is what I would expect, personally. If there's any ambiguity, perhaps we
could add some `AnimationCompletionBehavior`, to specify that kind of
completion behavior to use.

Update: Previously `PlayingAnimation::elapsed` was being used as the
seek time into the animation clip. This was misleading because if you
increased the speed of the animation it would also increase (or
decrease) the elapsed time. In other words, the elapsed time was not
actually the elapsed time. To solve this, we introduce
`PlayingAnimation::seek_time` to serve as the value we manipulate the
move between keyframes. Consequently, `elapsed()` now returns the actual
elapsed time, and is not effected by the animation speed. Because
`set_elapsed` was being used to manipulate the displayed keyframe, we
introduce `AnimationPlayer::seek_to` and `AnimationPlayer::replay` to
provide this functionality.

## Migration Guide

- Removed `set_elapsed`.
- Removed `stop_repeating` in favour of
`AnimationPlayer::set_repeat(RepeatAnimation::Never)`.
- Introduced `seek_to` to seek to a given timestamp inside of the
animation.
- Introduced `seek_time` accessor for the `PlayingAnimation::seek_to`.
- Introduced `AnimationPlayer::replay` to reset the `PlayingAnimation`
to a state where no time has elapsed.

---------

Co-authored-by: Hennadii Chernyshchyk <genaloner@gmail.com>
Co-authored-by: François <mockersf@gmail.com>
rdrpenguin04 pushed a commit to rdrpenguin04/bevy that referenced this pull request Jan 9, 2024
# Objective

Added `AnimationPlayer` API UX improvements. 

- Succestor to bevyengine#5912
- Fixes bevyengine#5848

_(Credits to @asafigan for filing bevyengine#5848, creating the initial pull
request, and the discussion in bevyengine#5912)_
## Solution

- Created `RepeatAnimation` enum to describe an animation repetition
behavior.
- Added `is_finished()`, `set_repeat()`, and `is_playback_reversed()`
methods to the animation player.
- ~~Made the animation clip optional as per the comment from bevyengine#5912~~
> ~~My problem is that the default handle [used the initialize a
`PlayingAnimation`] could actually refer to an actual animation if an
AnimationClip is set for the default handle, which leads me to ask,
"Should animation_clip should be an Option?"~~
- Added an accessor for the animation clip `animation_clip()` to the
animation player.

To determine if an animation is finished, we use the number of times the
animation has completed and the repetition behavior. If the animation is
playing in reverse then `elapsed < 0.0` counts as a completion.
Otherwise, `elapsed > animation.duration` counts as a completion. This
is what I would expect, personally. If there's any ambiguity, perhaps we
could add some `AnimationCompletionBehavior`, to specify that kind of
completion behavior to use.

Update: Previously `PlayingAnimation::elapsed` was being used as the
seek time into the animation clip. This was misleading because if you
increased the speed of the animation it would also increase (or
decrease) the elapsed time. In other words, the elapsed time was not
actually the elapsed time. To solve this, we introduce
`PlayingAnimation::seek_time` to serve as the value we manipulate the
move between keyframes. Consequently, `elapsed()` now returns the actual
elapsed time, and is not effected by the animation speed. Because
`set_elapsed` was being used to manipulate the displayed keyframe, we
introduce `AnimationPlayer::seek_to` and `AnimationPlayer::replay` to
provide this functionality.

## Migration Guide

- Removed `set_elapsed`.
- Removed `stop_repeating` in favour of
`AnimationPlayer::set_repeat(RepeatAnimation::Never)`.
- Introduced `seek_to` to seek to a given timestamp inside of the
animation.
- Introduced `seek_time` accessor for the `PlayingAnimation::seek_to`.
- Introduced `AnimationPlayer::replay` to reset the `PlayingAnimation`
to a state where no time has elapsed.

---------

Co-authored-by: Hennadii Chernyshchyk <genaloner@gmail.com>
Co-authored-by: François <mockersf@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-Animation Make things move and change over time C-Usability A targeted quality-of-life change that makes Bevy easier to use

Projects

None yet

Development

Successfully merging this pull request may close these issues.

More erganomic AnimationPlayer API

7 participants