From e663e5105b47e142017c1afb4b758a14e3a876d1 Mon Sep 17 00:00:00 2001 From: Devin Leamy Date: Fri, 30 Jun 2023 00:48:40 -0400 Subject: [PATCH 01/28] RepeatAnimation, is_playback_reversed(), is_finished(), animation updates --- crates/bevy_animation/src/lib.rs | 53 +++++++++++++++++++++++++++++--- 1 file changed, 48 insertions(+), 5 deletions(-) diff --git a/crates/bevy_animation/src/lib.rs b/crates/bevy_animation/src/lib.rs index cd02bee00045e..6cf1333240386 100644 --- a/crates/bevy_animation/src/lib.rs +++ b/crates/bevy_animation/src/lib.rs @@ -123,23 +123,38 @@ impl AnimationClip { } } +/// Repetition behavior of the animation. +#[derive(Reflect, Copy, Clone)] +pub enum RepeatAnimation { + /// Repeat forever. + Forever, + /// Repeat forever. + Once, + /// Repeat "n" times. + Count(u32), +} + #[derive(Reflect)] struct PlayingAnimation { - repeat: bool, + repeat: RepeatAnimation, speed: f32, elapsed: f32, animation_clip: Handle, path_cache: Vec>>, + /// Number of times the animation has completed. + /// If the animation is playing in reverse, this increments when the animation passes the start. + completions: u32, } impl Default for PlayingAnimation { fn default() -> Self { Self { - repeat: false, + repeat: RepeatAnimation::Once, speed: 1.0, elapsed: 0.0, animation_clip: Default::default(), path_cache: Vec::new(), + completions: 0, } } } @@ -212,6 +227,16 @@ impl AnimationPlayer { self } + /// Predicate to check if the animation has finished, based on its repetition behavior and the number of times it has repeated. + /// Note: A repeating will never finish. + pub fn is_finished(&self) -> bool { + match self.animation.repeat { + RepeatAnimation::Forever => false, + RepeatAnimation::Once => self.animation.completions >= 1, + RepeatAnimation::Count(n) => self.animation.completions >= n, + } + } + /// Start playing an animation, resetting state of the player, unless the requested animation is already playing. /// If `transition_duration` is set, this will use a linear blending /// between the previous and the new animation to make a smooth transition @@ -237,16 +262,27 @@ impl AnimationPlayer { /// Set the animation to repeat pub fn repeat(&mut self) -> &mut Self { - self.animation.repeat = true; + self.animation.repeat = RepeatAnimation::Forever; self } /// Stop the animation from repeating pub fn stop_repeating(&mut self) -> &mut Self { - self.animation.repeat = false; + self.animation.repeat = RepeatAnimation::Once; self } + /// Set the repetition behaviour of the animation + pub fn set_repeat(&mut self, repeat: RepeatAnimation) -> &mut Self { + self.animation.repeat = repeat; + self + } + + /// Predicate to check if the animation is playing in reverse. + pub fn is_playback_reversed(&self) -> bool { + self.animation.speed < 0.0 + } + /// Pause the animation pub fn pause(&mut self) { self.paused = true; @@ -483,7 +519,14 @@ fn apply_animation( animation.elapsed += time.delta_seconds() * animation.speed; } let mut elapsed = animation.elapsed; - if animation.repeat { + + if elapsed > animation_clip.duration && animation.speed > 0.0 { + animation.completions += 1; + } else if elapsed < 0.0 && animation.speed < 0.0 { + animation.completions += 1; + } + + if matches!(animation.repeat, RepeatAnimation::Forever) { elapsed %= animation_clip.duration; } if elapsed < 0.0 { From a6488df87221fba5955d3c96c2e192d678f0a8ee Mon Sep 17 00:00:00 2001 From: Devin Leamy Date: Fri, 30 Jun 2023 01:05:50 -0400 Subject: [PATCH 02/28] Apply modulo to elapsed time --- crates/bevy_animation/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_animation/src/lib.rs b/crates/bevy_animation/src/lib.rs index 6cf1333240386..c6490dd1e2bae 100644 --- a/crates/bevy_animation/src/lib.rs +++ b/crates/bevy_animation/src/lib.rs @@ -526,7 +526,7 @@ fn apply_animation( animation.completions += 1; } - if matches!(animation.repeat, RepeatAnimation::Forever) { + if elapsed > animation_clip.duration { elapsed %= animation_clip.duration; } if elapsed < 0.0 { From 169a94c55b5ccc0f7a18adb8ce785897d0841298 Mon Sep 17 00:00:00 2001 From: Devin Leamy Date: Fri, 30 Jun 2023 01:06:32 -0400 Subject: [PATCH 03/28] Greater than or equals --- crates/bevy_animation/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_animation/src/lib.rs b/crates/bevy_animation/src/lib.rs index c6490dd1e2bae..2265c6ed46f4f 100644 --- a/crates/bevy_animation/src/lib.rs +++ b/crates/bevy_animation/src/lib.rs @@ -526,7 +526,7 @@ fn apply_animation( animation.completions += 1; } - if elapsed > animation_clip.duration { + if elapsed >= animation_clip.duration { elapsed %= animation_clip.duration; } if elapsed < 0.0 { From 1400461952b7fa21f9ea60b7d58bec02d842c844 Mon Sep 17 00:00:00 2001 From: Devin Leamy Date: Fri, 30 Jun 2023 01:17:39 -0400 Subject: [PATCH 04/28] lint --- crates/bevy_animation/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/bevy_animation/src/lib.rs b/crates/bevy_animation/src/lib.rs index 2265c6ed46f4f..2072d01b4de04 100644 --- a/crates/bevy_animation/src/lib.rs +++ b/crates/bevy_animation/src/lib.rs @@ -520,9 +520,9 @@ fn apply_animation( } let mut elapsed = animation.elapsed; - if elapsed > animation_clip.duration && animation.speed > 0.0 { - animation.completions += 1; - } else if elapsed < 0.0 && animation.speed < 0.0 { + if (elapsed > animation_clip.duration && animation.speed > 0.0) + || (elapsed < 0.0 && animation.speed < 0.0) + { animation.completions += 1; } From 6e3b15cc383e357513f140e5ed03714432abf267 Mon Sep 17 00:00:00 2001 From: Devin Leamy Date: Fri, 30 Jun 2023 01:22:15 -0400 Subject: [PATCH 05/28] Updated names and comments --- crates/bevy_animation/src/lib.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/crates/bevy_animation/src/lib.rs b/crates/bevy_animation/src/lib.rs index 2072d01b4de04..17c2b3b4fa6ad 100644 --- a/crates/bevy_animation/src/lib.rs +++ b/crates/bevy_animation/src/lib.rs @@ -126,11 +126,11 @@ impl AnimationClip { /// Repetition behavior of the animation. #[derive(Reflect, Copy, Clone)] pub enum RepeatAnimation { - /// Repeat forever. + /// The animation will never finish. Forever, - /// Repeat forever. - Once, - /// Repeat "n" times. + /// The animation will finish after running once. + Never, + /// Then animation will finishin after running "n" times. Count(u32), } @@ -149,7 +149,7 @@ struct PlayingAnimation { impl Default for PlayingAnimation { fn default() -> Self { Self { - repeat: RepeatAnimation::Once, + repeat: RepeatAnimation::Never, speed: 1.0, elapsed: 0.0, animation_clip: Default::default(), @@ -232,7 +232,7 @@ impl AnimationPlayer { pub fn is_finished(&self) -> bool { match self.animation.repeat { RepeatAnimation::Forever => false, - RepeatAnimation::Once => self.animation.completions >= 1, + RepeatAnimation::Never => self.animation.completions >= 1, RepeatAnimation::Count(n) => self.animation.completions >= n, } } @@ -268,7 +268,7 @@ impl AnimationPlayer { /// Stop the animation from repeating pub fn stop_repeating(&mut self) -> &mut Self { - self.animation.repeat = RepeatAnimation::Once; + self.animation.repeat = RepeatAnimation::Never; self } From eec1240d37b6d8da596ff6fc1c2e60c1bc2c8508 Mon Sep 17 00:00:00 2001 From: Devin Leamy Date: Fri, 30 Jun 2023 01:30:44 -0400 Subject: [PATCH 06/28] Typo --- crates/bevy_animation/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/bevy_animation/src/lib.rs b/crates/bevy_animation/src/lib.rs index 17c2b3b4fa6ad..bd319ded0e314 100644 --- a/crates/bevy_animation/src/lib.rs +++ b/crates/bevy_animation/src/lib.rs @@ -123,14 +123,14 @@ impl AnimationClip { } } -/// Repetition behavior of the animation. +/// Repetition behavior of an animation. #[derive(Reflect, Copy, Clone)] pub enum RepeatAnimation { /// The animation will never finish. Forever, /// The animation will finish after running once. Never, - /// Then animation will finishin after running "n" times. + /// The animation will finish after running "n" times. Count(u32), } From a9f82a3471f89f6143f07f54a90140e32fd26c39 Mon Sep 17 00:00:00 2001 From: Devin Leamy Date: Sat, 1 Jul 2023 09:45:55 -0400 Subject: [PATCH 07/28] Added animation_clip() accessor and made animation_clip optional --- crates/bevy_animation/src/lib.rs | 64 ++++++++++++++++++++++---------- 1 file changed, 45 insertions(+), 19 deletions(-) diff --git a/crates/bevy_animation/src/lib.rs b/crates/bevy_animation/src/lib.rs index bd319ded0e314..03efcbcb7103f 100644 --- a/crates/bevy_animation/src/lib.rs +++ b/crates/bevy_animation/src/lib.rs @@ -139,7 +139,7 @@ struct PlayingAnimation { repeat: RepeatAnimation, speed: f32, elapsed: f32, - animation_clip: Handle, + animation_clip: Option>, path_cache: Vec>>, /// Number of times the animation has completed. /// If the animation is playing in reverse, this increments when the animation passes the start. @@ -159,6 +159,18 @@ impl Default for PlayingAnimation { } } +impl PlayingAnimation { + /// Predicate to check if the animation has finished, based on its repetition behavior and the number of times it has repeated. + /// Note: A repeating will never finish. + pub fn finished(&self) -> bool { + match self.repeat { + RepeatAnimation::Forever => false, + RepeatAnimation::Never => self.completions >= 1, + RepeatAnimation::Count(n) => self.completions >= n, + } + } +} + /// An animation that is being faded out as part of a transition struct AnimationTransition { /// The current weight. Starts at 1.0 and goes to 0.0 during the fade-out. @@ -191,7 +203,7 @@ impl AnimationPlayer { /// This will use a linear blending between the previous and the new animation to make a smooth transition pub fn start(&mut self, handle: Handle) -> &mut Self { self.animation = PlayingAnimation { - animation_clip: handle, + animation_clip: Some(handle), ..Default::default() }; @@ -210,7 +222,7 @@ impl AnimationPlayer { transition_duration: Duration, ) -> &mut Self { let mut animation = PlayingAnimation { - animation_clip: handle, + animation_clip: Some(handle), ..Default::default() }; std::mem::swap(&mut animation, &mut self.animation); @@ -227,21 +239,11 @@ impl AnimationPlayer { self } - /// Predicate to check if the animation has finished, based on its repetition behavior and the number of times it has repeated. - /// Note: A repeating will never finish. - pub fn is_finished(&self) -> bool { - match self.animation.repeat { - RepeatAnimation::Forever => false, - RepeatAnimation::Never => self.animation.completions >= 1, - RepeatAnimation::Count(n) => self.animation.completions >= n, - } - } - /// Start playing an animation, resetting state of the player, unless the requested animation is already playing. /// If `transition_duration` is set, this will use a linear blending /// between the previous and the new animation to make a smooth transition pub fn play(&mut self, handle: Handle) -> &mut Self { - if self.animation.animation_clip != handle || self.is_paused() { + if !self.is_playing_clip(&handle) || self.is_paused() { self.start(handle); } self @@ -254,12 +256,31 @@ impl AnimationPlayer { handle: Handle, transition_duration: Duration, ) -> &mut Self { - if self.animation.animation_clip != handle || self.is_paused() { + if !self.is_playing_clip(&handle) || self.is_paused() { self.start_with_transition(handle, transition_duration); } self } + /// Handle to the animation clip being played. + pub fn animation_clip(&self) -> Option<&Handle> { + self.animation.animation_clip.as_ref() + } + + /// Predicate to check if the given animation clip is being played. + pub fn is_playing_clip(&self, handle: &Handle) -> bool { + if let Some(animation_clip_handle) = self.animation_clip() { + animation_clip_handle == handle + } else { + false + } + } + + /// Predicate to check if the playing animation has finished, according to the repetition behavior. + pub fn is_finished(&self) -> bool { + self.animation.finished() + } + /// Set the animation to repeat pub fn repeat(&mut self) -> &mut Self { self.animation.repeat = RepeatAnimation::Forever; @@ -514,10 +535,15 @@ fn apply_animation( parents: &Query<(Option>, Option<&Parent>)>, children: &Query<&Children>, ) { - if let Some(animation_clip) = animations.get(&animation.animation_clip) { - if !paused { - animation.elapsed += time.delta_seconds() * animation.speed; - } + let Some(animation_clip_handle) = &animation.animation_clip else { + return; + }; + // On update the animation while the player is not paused and the animation is not complete. + if animation.finished() || paused { + return; + } + if let Some(animation_clip) = animations.get(&animation_clip_handle) { + animation.elapsed += time.delta_seconds() * animation.speed; let mut elapsed = animation.elapsed; if (elapsed > animation_clip.duration && animation.speed > 0.0) From a920e904b6a9a7ee18ac244a1a0f3575e4291ca0 Mon Sep 17 00:00:00 2001 From: Devin Leamy Date: Sat, 1 Jul 2023 09:54:01 -0400 Subject: [PATCH 08/28] lint --- crates/bevy_animation/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_animation/src/lib.rs b/crates/bevy_animation/src/lib.rs index 03efcbcb7103f..035494096ee4c 100644 --- a/crates/bevy_animation/src/lib.rs +++ b/crates/bevy_animation/src/lib.rs @@ -542,7 +542,7 @@ fn apply_animation( if animation.finished() || paused { return; } - if let Some(animation_clip) = animations.get(&animation_clip_handle) { + if let Some(animation_clip) = animations.get(animation_clip_handle) { animation.elapsed += time.delta_seconds() * animation.speed; let mut elapsed = animation.elapsed; From 57df0acb9072145be9119e7e44b9d4b64a8160af Mon Sep 17 00:00:00 2001 From: Devin Leamy Date: Sat, 1 Jul 2023 09:59:44 -0400 Subject: [PATCH 09/28] Updated comment --- crates/bevy_animation/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_animation/src/lib.rs b/crates/bevy_animation/src/lib.rs index 035494096ee4c..c76a636fd8db1 100644 --- a/crates/bevy_animation/src/lib.rs +++ b/crates/bevy_animation/src/lib.rs @@ -161,7 +161,7 @@ impl Default for PlayingAnimation { impl PlayingAnimation { /// Predicate to check if the animation has finished, based on its repetition behavior and the number of times it has repeated. - /// Note: A repeating will never finish. + /// Note: An animation with `RepeatAnimation::Forever` will never finish. pub fn finished(&self) -> bool { match self.repeat { RepeatAnimation::Forever => false, From 5af3277039bdb5447c5847dd48dfb13b44f80e27 Mon Sep 17 00:00:00 2001 From: Devin Leamy Date: Sat, 1 Jul 2023 10:04:21 -0400 Subject: [PATCH 10/28] Conditionally update the elapsed time --- crates/bevy_animation/src/lib.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/crates/bevy_animation/src/lib.rs b/crates/bevy_animation/src/lib.rs index c76a636fd8db1..720e3a8e23a63 100644 --- a/crates/bevy_animation/src/lib.rs +++ b/crates/bevy_animation/src/lib.rs @@ -538,12 +538,13 @@ fn apply_animation( let Some(animation_clip_handle) = &animation.animation_clip else { return; }; - // On update the animation while the player is not paused and the animation is not complete. - if animation.finished() || paused { - return; - } + if let Some(animation_clip) = animations.get(animation_clip_handle) { - animation.elapsed += time.delta_seconds() * animation.speed; + // Only update the elapsed time while the player is not paused and the animation is not complete. + // We don't return early because set_elapsed() may have been called on the animation player. + if !animation.finished() && !paused { + animation.elapsed += time.delta_seconds() * animation.speed; + } let mut elapsed = animation.elapsed; if (elapsed > animation_clip.duration && animation.speed > 0.0) From 1d829db1c238f774814efa4efcbf2699d69016c5 Mon Sep 17 00:00:00 2001 From: Devin Leamy Date: Sat, 1 Jul 2023 11:03:48 -0400 Subject: [PATCH 11/28] repeat_mode() and completions() public accessors --- crates/bevy_animation/src/lib.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/crates/bevy_animation/src/lib.rs b/crates/bevy_animation/src/lib.rs index 720e3a8e23a63..929dd739b112b 100644 --- a/crates/bevy_animation/src/lib.rs +++ b/crates/bevy_animation/src/lib.rs @@ -299,6 +299,16 @@ impl AnimationPlayer { self } + /// Repetition behavior of the animation. + pub fn repeat_mode(&self) -> RepeatAnimation { + self.animation.repeat + } + + /// Number of times the animation has been completed. + pub fn completions(&self) -> u32 { + self.animation.completions + } + /// Predicate to check if the animation is playing in reverse. pub fn is_playback_reversed(&self) -> bool { self.animation.speed < 0.0 From 3ad8d27d3f71d29b995cdd339b65a4033ab905a9 Mon Sep 17 00:00:00 2001 From: Devin Leamy Date: Sat, 1 Jul 2023 11:04:19 -0400 Subject: [PATCH 12/28] Updated comment --- crates/bevy_animation/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_animation/src/lib.rs b/crates/bevy_animation/src/lib.rs index 929dd739b112b..8614a45272a62 100644 --- a/crates/bevy_animation/src/lib.rs +++ b/crates/bevy_animation/src/lib.rs @@ -304,7 +304,7 @@ impl AnimationPlayer { self.animation.repeat } - /// Number of times the animation has been completed. + /// Number of times the animation has completed. pub fn completions(&self) -> u32 { self.animation.completions } From 62c67e5a3a8d0295237a43fe099c0ec735fe66fa Mon Sep 17 00:00:00 2001 From: Devin Leamy Date: Wed, 16 Aug 2023 18:51:02 -0400 Subject: [PATCH 13/28] Implement default for RepeatAnimation, with Never as the default --- crates/bevy_animation/src/lib.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/crates/bevy_animation/src/lib.rs b/crates/bevy_animation/src/lib.rs index 8614a45272a62..8178c7d54cf83 100644 --- a/crates/bevy_animation/src/lib.rs +++ b/crates/bevy_animation/src/lib.rs @@ -124,14 +124,15 @@ impl AnimationClip { } /// Repetition behavior of an animation. -#[derive(Reflect, Copy, Clone)] +#[derive(Reflect, Copy, Clone, Default)] pub enum RepeatAnimation { - /// The animation will never finish. - Forever, /// The animation will finish after running once. + #[default] Never, /// The animation will finish after running "n" times. Count(u32), + /// The animation will never finish. + Forever, } #[derive(Reflect)] @@ -149,7 +150,7 @@ struct PlayingAnimation { impl Default for PlayingAnimation { fn default() -> Self { Self { - repeat: RepeatAnimation::Never, + repeat: RepeatAnimation::default(), speed: 1.0, elapsed: 0.0, animation_clip: Default::default(), From d36d5bc4b5bcc4727b1ecec90af00855ed0eb22c Mon Sep 17 00:00:00 2001 From: Devin Leamy Date: Wed, 16 Aug 2023 18:52:51 -0400 Subject: [PATCH 14/28] Renamed PlayingAnimation::finished to PlayingAnimation::is_finished to match the AnimationPlayer --- crates/bevy_animation/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/bevy_animation/src/lib.rs b/crates/bevy_animation/src/lib.rs index 8178c7d54cf83..28a6f4274f2fb 100644 --- a/crates/bevy_animation/src/lib.rs +++ b/crates/bevy_animation/src/lib.rs @@ -163,7 +163,7 @@ impl Default for PlayingAnimation { impl PlayingAnimation { /// Predicate to check if the animation has finished, based on its repetition behavior and the number of times it has repeated. /// Note: An animation with `RepeatAnimation::Forever` will never finish. - pub fn finished(&self) -> bool { + pub fn is_finished(&self) -> bool { match self.repeat { RepeatAnimation::Forever => false, RepeatAnimation::Never => self.completions >= 1, @@ -279,7 +279,7 @@ impl AnimationPlayer { /// Predicate to check if the playing animation has finished, according to the repetition behavior. pub fn is_finished(&self) -> bool { - self.animation.finished() + self.animation.is_finished() } /// Set the animation to repeat @@ -553,7 +553,7 @@ fn apply_animation( if let Some(animation_clip) = animations.get(animation_clip_handle) { // Only update the elapsed time while the player is not paused and the animation is not complete. // We don't return early because set_elapsed() may have been called on the animation player. - if !animation.finished() && !paused { + if !animation.is_finished() && !paused { animation.elapsed += time.delta_seconds() * animation.speed; } let mut elapsed = animation.elapsed; From a3a7eb4dc37bd8e01cdf905ef6985c44a4b4c0d9 Mon Sep 17 00:00:00 2001 From: Devin Leamy Date: Wed, 16 Aug 2023 19:06:28 -0400 Subject: [PATCH 15/28] animation_clip: Handle, rather than Option> --- crates/bevy_animation/src/lib.rs | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/crates/bevy_animation/src/lib.rs b/crates/bevy_animation/src/lib.rs index 28a6f4274f2fb..151f773c59947 100644 --- a/crates/bevy_animation/src/lib.rs +++ b/crates/bevy_animation/src/lib.rs @@ -140,7 +140,7 @@ struct PlayingAnimation { repeat: RepeatAnimation, speed: f32, elapsed: f32, - animation_clip: Option>, + animation_clip: Handle, path_cache: Vec>>, /// Number of times the animation has completed. /// If the animation is playing in reverse, this increments when the animation passes the start. @@ -204,7 +204,7 @@ impl AnimationPlayer { /// This will use a linear blending between the previous and the new animation to make a smooth transition pub fn start(&mut self, handle: Handle) -> &mut Self { self.animation = PlayingAnimation { - animation_clip: Some(handle), + animation_clip: handle, ..Default::default() }; @@ -223,7 +223,7 @@ impl AnimationPlayer { transition_duration: Duration, ) -> &mut Self { let mut animation = PlayingAnimation { - animation_clip: Some(handle), + animation_clip: handle, ..Default::default() }; std::mem::swap(&mut animation, &mut self.animation); @@ -264,17 +264,13 @@ impl AnimationPlayer { } /// Handle to the animation clip being played. - pub fn animation_clip(&self) -> Option<&Handle> { - self.animation.animation_clip.as_ref() + pub fn animation_clip(&self) -> &Handle { + &self.animation.animation_clip } /// Predicate to check if the given animation clip is being played. pub fn is_playing_clip(&self, handle: &Handle) -> bool { - if let Some(animation_clip_handle) = self.animation_clip() { - animation_clip_handle == handle - } else { - false - } + self.animation_clip() == handle } /// Predicate to check if the playing animation has finished, according to the repetition behavior. @@ -546,11 +542,7 @@ fn apply_animation( parents: &Query<(Option>, Option<&Parent>)>, children: &Query<&Children>, ) { - let Some(animation_clip_handle) = &animation.animation_clip else { - return; - }; - - if let Some(animation_clip) = animations.get(animation_clip_handle) { + if let Some(animation_clip) = animations.get(&animation.animation_clip) { // Only update the elapsed time while the player is not paused and the animation is not complete. // We don't return early because set_elapsed() may have been called on the animation player. if !animation.is_finished() && !paused { From 60fc68c4ca93df1809016b8a10b7798a61570e6e Mon Sep 17 00:00:00 2001 From: Devin Leamy Date: Wed, 16 Aug 2023 19:55:27 -0400 Subject: [PATCH 16/28] Introduced seek_time and PlayingAnimation::update() --- crates/bevy_animation/src/lib.rs | 68 +++++++++++++++++++++----------- 1 file changed, 45 insertions(+), 23 deletions(-) diff --git a/crates/bevy_animation/src/lib.rs b/crates/bevy_animation/src/lib.rs index 151f773c59947..2de31b983eb94 100644 --- a/crates/bevy_animation/src/lib.rs +++ b/crates/bevy_animation/src/lib.rs @@ -139,7 +139,12 @@ pub enum RepeatAnimation { struct PlayingAnimation { repeat: RepeatAnimation, speed: f32, + /// Total time the animation has been played. + /// Note: Time does not increase when the animation is paused or after it has completed. elapsed: f32, + /// The timestamp inside of the animation clip. + /// Note: This will always be in the range [0.0, animation clip duration] + seek_time: f32, animation_clip: Handle, path_cache: Vec>>, /// Number of times the animation has completed. @@ -153,6 +158,7 @@ impl Default for PlayingAnimation { repeat: RepeatAnimation::default(), speed: 1.0, elapsed: 0.0, + seek_time: 0.0, animation_clip: Default::default(), path_cache: Vec::new(), completions: 0, @@ -170,6 +176,29 @@ impl PlayingAnimation { RepeatAnimation::Count(n) => self.completions >= n, } } + + /// Update the animation given the delta time and the duration of the clip being played. + fn update(&mut self, delta: f32, clip_duration: f32) { + if self.is_finished() { + return; + } + + self.elapsed += delta; + self.seek_time += delta * self.speed; + + if (self.seek_time > clip_duration && self.speed > 0.0) + || (self.seek_time < 0.0 && self.speed < 0.0) + { + self.completions += 1; + } + + if self.seek_time >= clip_duration { + self.seek_time %= clip_duration; + } + if self.seek_time < 0.0 { + self.seek_time += clip_duration; + } + } } /// An animation that is being faded out as part of a transition @@ -342,9 +371,15 @@ impl AnimationPlayer { self.animation.elapsed } - /// Seek to a specific time in the animation - pub fn set_elapsed(&mut self, elapsed: f32) -> &mut Self { - self.animation.elapsed = elapsed; + /// Seek time inside of the animation. Always within the range [0.0, clip duration]. + pub fn seek_time(&self) -> f32 { + self.animation.seek_time + } + + /// Seek to a specific time in the animation. + pub fn seek_to(&mut self, seek_time: f32) -> &mut Self { + assert!(0.0 >= seek_time); + self.animation.seek_time = seek_time; self } } @@ -543,25 +578,12 @@ fn apply_animation( children: &Query<&Children>, ) { if let Some(animation_clip) = animations.get(&animation.animation_clip) { - // Only update the elapsed time while the player is not paused and the animation is not complete. - // We don't return early because set_elapsed() may have been called on the animation player. - if !animation.is_finished() && !paused { - animation.elapsed += time.delta_seconds() * animation.speed; - } - let mut elapsed = animation.elapsed; - - if (elapsed > animation_clip.duration && animation.speed > 0.0) - || (elapsed < 0.0 && animation.speed < 0.0) - { - animation.completions += 1; - } + // We don't return early because seek_to() may have been called on the animation player. + animation.update( + if paused { 0.0 } else { time.delta_seconds() }, + animation_clip.duration, + ); - if elapsed >= animation_clip.duration { - elapsed %= animation_clip.duration; - } - if elapsed < 0.0 { - elapsed += animation_clip.duration; - } if animation.path_cache.len() != animation_clip.paths.len() { animation.path_cache = vec![Vec::new(); animation_clip.paths.len()]; } @@ -615,7 +637,7 @@ fn apply_animation( // PERF: finding the current keyframe can be optimised let step_start = match curve .keyframe_timestamps - .binary_search_by(|probe| probe.partial_cmp(&elapsed).unwrap()) + .binary_search_by(|probe| probe.partial_cmp(&animation.seek_time).unwrap()) { Ok(n) if n >= curve.keyframe_timestamps.len() - 1 => continue, // this curve is finished Ok(i) => i, @@ -625,7 +647,7 @@ fn apply_animation( }; let ts_start = curve.keyframe_timestamps[step_start]; let ts_end = curve.keyframe_timestamps[step_start + 1]; - let lerp = (elapsed - ts_start) / (ts_end - ts_start); + let lerp = (animation.seek_time - ts_start) / (ts_end - ts_start); // Apply the keyframe match &curve.keyframes { From 63a71b6a4d652c29a30839a7dc729fcb595553e1 Mon Sep 17 00:00:00 2001 From: Devin Leamy Date: Wed, 16 Aug 2023 20:03:24 -0400 Subject: [PATCH 17/28] Introduced AnimationPlayer::replay() to reset the PlayingAnimation's to its initial state --- crates/bevy_animation/src/lib.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/crates/bevy_animation/src/lib.rs b/crates/bevy_animation/src/lib.rs index 2de31b983eb94..ca22a6e313784 100644 --- a/crates/bevy_animation/src/lib.rs +++ b/crates/bevy_animation/src/lib.rs @@ -199,6 +199,13 @@ impl PlayingAnimation { self.seek_time += clip_duration; } } + + /// Reset back to the initial state as if no time has elapsed. + fn replay(&mut self) { + self.completions = 0; + self.elapsed = 0.0; + self.seek_time = 0.0; + } } /// An animation that is being faded out as part of a transition @@ -378,10 +385,14 @@ impl AnimationPlayer { /// Seek to a specific time in the animation. pub fn seek_to(&mut self, seek_time: f32) -> &mut Self { - assert!(0.0 >= seek_time); self.animation.seek_time = seek_time; self } + + /// Reset the animation to its initial state, as if no no time has elapsed. + pub fn replay(&mut self) { + self.animation.replay(); + } } fn entity_from_path( From 88fd03698d2e7168cfc9fa9e489e2f8bafada771 Mon Sep 17 00:00:00 2001 From: Devin Leamy Date: Wed, 16 Aug 2023 20:08:20 -0400 Subject: [PATCH 18/28] Removed AnimationPlayer::stop_repeating() and added a comment to AnimationPlayer::repeat() --- crates/bevy_animation/src/lib.rs | 11 +++-------- examples/animation/animated_fox.rs | 14 ++++++++++---- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/crates/bevy_animation/src/lib.rs b/crates/bevy_animation/src/lib.rs index ca22a6e313784..7005b6282b97c 100644 --- a/crates/bevy_animation/src/lib.rs +++ b/crates/bevy_animation/src/lib.rs @@ -314,19 +314,14 @@ impl AnimationPlayer { self.animation.is_finished() } - /// Set the animation to repeat + /// Set the animation to repeat. + /// Equivalent to `animation_player.set_repeat(RepeatAnimation::Forever)`. pub fn repeat(&mut self) -> &mut Self { self.animation.repeat = RepeatAnimation::Forever; self } - /// Stop the animation from repeating - pub fn stop_repeating(&mut self) -> &mut Self { - self.animation.repeat = RepeatAnimation::Never; - self - } - - /// Set the repetition behaviour of the animation + /// Set the repetition behaviour of the animation. pub fn set_repeat(&mut self, repeat: RepeatAnimation) -> &mut Self { self.animation.repeat = repeat; self diff --git a/examples/animation/animated_fox.rs b/examples/animation/animated_fox.rs index 0052cbce75a9a..4fef37b4b0393 100644 --- a/examples/animation/animated_fox.rs +++ b/examples/animation/animated_fox.rs @@ -5,6 +5,7 @@ use std::time::Duration; use bevy::pbr::CascadeShadowConfigBuilder; use bevy::prelude::*; +use bevy_internal::animation::RepeatAnimation; fn main() { App::new() @@ -116,13 +117,13 @@ fn keyboard_animation_control( } if keyboard_input.just_pressed(KeyCode::Left) { - let elapsed = player.elapsed(); - player.set_elapsed(elapsed - 0.1); + let elapsed = player.seek_time(); + player.seek_to(elapsed - 0.1); } if keyboard_input.just_pressed(KeyCode::Right) { - let elapsed = player.elapsed(); - player.set_elapsed(elapsed + 0.1); + let elapsed = player.seek_time(); + player.seek_to(elapsed + 0.1); } if keyboard_input.just_pressed(KeyCode::Return) { @@ -134,5 +135,10 @@ fn keyboard_animation_control( ) .repeat(); } + + if keyboard_input.just_pressed(KeyCode::I) { + player.set_repeat(RepeatAnimation::Count(1)); + player.replay(); + } } } From 764ae7ebe7a84bd4f284a6741993679d04b2a972 Mon Sep 17 00:00:00 2001 From: Devin Leamy Date: Wed, 16 Aug 2023 20:11:44 -0400 Subject: [PATCH 19/28] Updated examples --- examples/animation/animated_fox.rs | 5 ----- examples/stress_tests/many_foxes.rs | 8 ++++---- examples/tools/scene_viewer/animation_plugin.rs | 2 +- 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/examples/animation/animated_fox.rs b/examples/animation/animated_fox.rs index 4fef37b4b0393..d272dda783f9f 100644 --- a/examples/animation/animated_fox.rs +++ b/examples/animation/animated_fox.rs @@ -135,10 +135,5 @@ fn keyboard_animation_control( ) .repeat(); } - - if keyboard_input.just_pressed(KeyCode::I) { - player.set_repeat(RepeatAnimation::Count(1)); - player.replay(); - } } } diff --git a/examples/stress_tests/many_foxes.rs b/examples/stress_tests/many_foxes.rs index 18dee869bcf79..194100658bd6e 100644 --- a/examples/stress_tests/many_foxes.rs +++ b/examples/stress_tests/many_foxes.rs @@ -270,13 +270,13 @@ fn keyboard_animation_control( } if keyboard_input.just_pressed(KeyCode::Left) { - let elapsed = player.elapsed(); - player.set_elapsed(elapsed - 0.1); + let elapsed = player.seek_time(); + player.seek_to(elapsed - 0.1); } if keyboard_input.just_pressed(KeyCode::Right) { - let elapsed = player.elapsed(); - player.set_elapsed(elapsed + 0.1); + let elapsed = player.seek_time(); + player.seek_to(elapsed + 0.1); } if keyboard_input.just_pressed(KeyCode::Return) { diff --git a/examples/tools/scene_viewer/animation_plugin.rs b/examples/tools/scene_viewer/animation_plugin.rs index 4c4e032a3871f..e1f2970547961 100644 --- a/examples/tools/scene_viewer/animation_plugin.rs +++ b/examples/tools/scene_viewer/animation_plugin.rs @@ -91,7 +91,7 @@ fn handle_inputs( let resume = !player.is_paused(); // set the current animation to its start and pause it to reset to its starting state - player.set_elapsed(0.0).pause(); + player.seek_to(0.0).pause(); clips.advance_to_next(); let current_clip = clips.current(); From e028c177ef8fc7bcfcc624ac26b64acac461265a Mon Sep 17 00:00:00 2001 From: Devin Leamy Date: Wed, 16 Aug 2023 20:27:20 -0400 Subject: [PATCH 20/28] Remove unused import --- examples/animation/animated_fox.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/animation/animated_fox.rs b/examples/animation/animated_fox.rs index d272dda783f9f..5d5e5511cc78a 100644 --- a/examples/animation/animated_fox.rs +++ b/examples/animation/animated_fox.rs @@ -5,7 +5,6 @@ use std::time::Duration; use bevy::pbr::CascadeShadowConfigBuilder; use bevy::prelude::*; -use bevy_internal::animation::RepeatAnimation; fn main() { App::new() From 96707235e0f3e47040cd82453b38dd54bae8e6ad Mon Sep 17 00:00:00 2001 From: Devin Leamy Date: Wed, 16 Aug 2023 22:47:47 -0400 Subject: [PATCH 21/28] nit: fix comment --- crates/bevy_animation/src/lib.rs | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/crates/bevy_animation/src/lib.rs b/crates/bevy_animation/src/lib.rs index e473450081e44..ecf05718a5241 100644 --- a/crates/bevy_animation/src/lib.rs +++ b/crates/bevy_animation/src/lib.rs @@ -102,12 +102,6 @@ impl AnimationClip { self.duration } - /// The bone ids mapped by their [`EntityPath`]. - #[inline] - pub fn paths(&self) -> &HashMap { - &self.paths - } - /// Add a [`VariableCurve`] to an [`EntityPath`]. pub fn add_curve_to_path(&mut self, path: EntityPath, curve: VariableCurve) { // Update the duration of the animation by this curve duration if it's longer @@ -390,7 +384,7 @@ impl AnimationPlayer { self } - /// Reset the animation to its initial state, as if no no time has elapsed. + /// Reset the animation to its initial state, as if no time has elapsed. pub fn replay(&mut self) { self.animation.replay(); } @@ -475,7 +469,7 @@ pub fn animation_player( ) { animation_players .par_iter_mut() - .for_each(|(root, maybe_parent, mut player)| { + .for_each_mut(|(root, maybe_parent, mut player)| { update_transitions(&mut player, &time); run_animation_player( root, @@ -713,7 +707,6 @@ impl Plugin for AnimationPlugin { app.add_asset::() .register_asset_reflect::() .register_type::() - .register_type::() .add_systems( PostUpdate, animation_player.before(TransformSystem::TransformPropagate), From 1d2a9b9139ac364f56423fd11293c90c9288c3a8 Mon Sep 17 00:00:00 2001 From: DevinLeamy Date: Thu, 17 Aug 2023 09:03:39 -0400 Subject: [PATCH 22/28] Nit: improved comment Co-authored-by: Hennadii Chernyshchyk --- crates/bevy_animation/src/lib.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/crates/bevy_animation/src/lib.rs b/crates/bevy_animation/src/lib.rs index ecf05718a5241..b3216a6c413f9 100644 --- a/crates/bevy_animation/src/lib.rs +++ b/crates/bevy_animation/src/lib.rs @@ -314,8 +314,9 @@ impl AnimationPlayer { self.animation.is_finished() } - /// Set the animation to repeat. - /// Equivalent to `animation_player.set_repeat(RepeatAnimation::Forever)`. + /// Sets repeat to [`RepeatAnimation::Forever`]. + /// + /// See also [`Self::set_repeat`]. pub fn repeat(&mut self) -> &mut Self { self.animation.repeat = RepeatAnimation::Forever; self From d59789b3acc06b4732c4d179c6a3c96d0b11fbd3 Mon Sep 17 00:00:00 2001 From: Devin Leamy Date: Thu, 17 Aug 2023 22:40:41 -0400 Subject: [PATCH 23/28] Replaced deprecated for_each_mut with for_each --- crates/bevy_animation/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_animation/src/lib.rs b/crates/bevy_animation/src/lib.rs index ecf05718a5241..317f319ec3cb3 100644 --- a/crates/bevy_animation/src/lib.rs +++ b/crates/bevy_animation/src/lib.rs @@ -469,7 +469,7 @@ pub fn animation_player( ) { animation_players .par_iter_mut() - .for_each_mut(|(root, maybe_parent, mut player)| { + .for_each(|(root, maybe_parent, mut player)| { update_transitions(&mut player, &time); run_animation_player( root, From 011d13f8cc9f3061d22f91d90d2ab4a00ec6a67d Mon Sep 17 00:00:00 2001 From: Devin Leamy Date: Thu, 17 Aug 2023 22:51:11 -0400 Subject: [PATCH 24/28] Updated animated_fox example to showcase the repeat modes --- examples/animation/animated_fox.rs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/examples/animation/animated_fox.rs b/examples/animation/animated_fox.rs index 5d5e5511cc78a..e8479492a53d1 100644 --- a/examples/animation/animated_fox.rs +++ b/examples/animation/animated_fox.rs @@ -5,6 +5,7 @@ use std::time::Duration; use bevy::pbr::CascadeShadowConfigBuilder; use bevy::prelude::*; +use bevy_internal::animation::RepeatAnimation; fn main() { App::new() @@ -77,6 +78,8 @@ fn setup( println!(" - spacebar: play / pause"); println!(" - arrow up / down: speed up / slow down animation playback"); println!(" - arrow left / right: seek backward / forward"); + println!(" - digit 1 / 3 / 5: play the animation times"); + println!(" - L: loop the animation forever"); println!(" - return: change animation"); } @@ -134,5 +137,24 @@ fn keyboard_animation_control( ) .repeat(); } + + if keyboard_input.just_pressed(KeyCode::Key1) { + player.set_repeat(RepeatAnimation::Count(1)); + player.replay(); + } + + if keyboard_input.just_pressed(KeyCode::Key3) { + player.set_repeat(RepeatAnimation::Count(3)); + player.replay(); + } + + if keyboard_input.just_pressed(KeyCode::Key5) { + player.set_repeat(RepeatAnimation::Count(5)); + player.replay(); + } + + if keyboard_input.just_pressed(KeyCode::L) { + player.set_repeat(RepeatAnimation::Forever); + } } } From 7910f05fbbdb67a8164de50c0278915becb29c5d Mon Sep 17 00:00:00 2001 From: Devin Leamy Date: Thu, 17 Aug 2023 22:57:06 -0400 Subject: [PATCH 25/28] nit: format --- examples/animation/animated_fox.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/animation/animated_fox.rs b/examples/animation/animated_fox.rs index e8479492a53d1..808d04adf13f7 100644 --- a/examples/animation/animated_fox.rs +++ b/examples/animation/animated_fox.rs @@ -147,7 +147,7 @@ fn keyboard_animation_control( player.set_repeat(RepeatAnimation::Count(3)); player.replay(); } - + if keyboard_input.just_pressed(KeyCode::Key5) { player.set_repeat(RepeatAnimation::Count(5)); player.replay(); From 6527d7279e7930aa8723c515c3b1019dffdb4b01 Mon Sep 17 00:00:00 2001 From: Devin Leamy Date: Tue, 22 Aug 2023 19:35:05 -0400 Subject: [PATCH 26/28] Reformat comments + re-run CI, post moving run-examples over to windows --- crates/bevy_animation/src/lib.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/crates/bevy_animation/src/lib.rs b/crates/bevy_animation/src/lib.rs index 361047a374c03..10427dec40c88 100644 --- a/crates/bevy_animation/src/lib.rs +++ b/crates/bevy_animation/src/lib.rs @@ -140,9 +140,11 @@ struct PlayingAnimation { repeat: RepeatAnimation, speed: f32, /// Total time the animation has been played. + /// /// Note: Time does not increase when the animation is paused or after it has completed. elapsed: f32, /// The timestamp inside of the animation clip. + /// /// Note: This will always be in the range [0.0, animation clip duration] seek_time: f32, animation_clip: Handle, @@ -168,6 +170,7 @@ impl Default for PlayingAnimation { impl PlayingAnimation { /// Predicate to check if the animation has finished, based on its repetition behavior and the number of times it has repeated. + /// /// Note: An animation with `RepeatAnimation::Forever` will never finish. pub fn is_finished(&self) -> bool { match self.repeat { From f50c8c49be5611bb52775bbfa48226915335f2d6 Mon Sep 17 00:00:00 2001 From: Devin Leamy Date: Tue, 22 Aug 2023 19:40:39 -0400 Subject: [PATCH 27/28] nit: cargo fmt --all --- crates/bevy_animation/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/bevy_animation/src/lib.rs b/crates/bevy_animation/src/lib.rs index 10427dec40c88..8058c30ccf64f 100644 --- a/crates/bevy_animation/src/lib.rs +++ b/crates/bevy_animation/src/lib.rs @@ -140,11 +140,11 @@ struct PlayingAnimation { repeat: RepeatAnimation, speed: f32, /// Total time the animation has been played. - /// + /// /// Note: Time does not increase when the animation is paused or after it has completed. elapsed: f32, /// The timestamp inside of the animation clip. - /// + /// /// Note: This will always be in the range [0.0, animation clip duration] seek_time: f32, animation_clip: Handle, @@ -170,7 +170,7 @@ impl Default for PlayingAnimation { impl PlayingAnimation { /// Predicate to check if the animation has finished, based on its repetition behavior and the number of times it has repeated. - /// + /// /// Note: An animation with `RepeatAnimation::Forever` will never finish. pub fn is_finished(&self) -> bool { match self.repeat { From 8d3b642f526271d5a06d9dc2b7f254bc05f4c2c7 Mon Sep 17 00:00:00 2001 From: DevinLeamy Date: Wed, 23 Aug 2023 20:00:45 -0400 Subject: [PATCH 28/28] Applied suggestions (updates to comments and inlines) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: François --- crates/bevy_animation/src/lib.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/crates/bevy_animation/src/lib.rs b/crates/bevy_animation/src/lib.rs index 8058c30ccf64f..b88344e5ee61a 100644 --- a/crates/bevy_animation/src/lib.rs +++ b/crates/bevy_animation/src/lib.rs @@ -169,9 +169,10 @@ impl Default for PlayingAnimation { } impl PlayingAnimation { - /// Predicate to check if the animation has finished, based on its repetition behavior and the number of times it has repeated. + /// Check if the animation has finished, based on its repetition behavior and the number of times it has repeated. /// /// Note: An animation with `RepeatAnimation::Forever` will never finish. + #[inline] pub fn is_finished(&self) -> bool { match self.repeat { RepeatAnimation::Forever => false, @@ -181,6 +182,7 @@ impl PlayingAnimation { } /// Update the animation given the delta time and the duration of the clip being played. + #[inline] fn update(&mut self, delta: f32, clip_duration: f32) { if self.is_finished() { return; @@ -307,12 +309,12 @@ impl AnimationPlayer { &self.animation.animation_clip } - /// Predicate to check if the given animation clip is being played. + /// Check if the given animation clip is being played. pub fn is_playing_clip(&self, handle: &Handle) -> bool { self.animation_clip() == handle } - /// Predicate to check if the playing animation has finished, according to the repetition behavior. + /// Check if the playing animation has finished, according to the repetition behavior. pub fn is_finished(&self) -> bool { self.animation.is_finished() } @@ -341,7 +343,7 @@ impl AnimationPlayer { self.animation.completions } - /// Predicate to check if the animation is playing in reverse. + /// Check if the animation is playing in reverse. pub fn is_playback_reversed(&self) -> bool { self.animation.speed < 0.0 }