diff --git a/.gitignore b/.gitignore index 8ec23a9f51d5e0a5936f2b3ac5e83fdca3269864..676e74baec06fadec734bebacc8885bcf057f5d3 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ target/ Cargo.lock .vs* -.DS_Store \ No newline at end of file +.DS_Store +.idea diff --git a/README.md b/README.md index 626a337130c4284640e06832887c594f09149610..e4797b4ce5316d33b2d1cba1a87dd68409affa5b 100644 --- a/README.md +++ b/README.md @@ -74,9 +74,9 @@ commands sprite: Sprite { color: Color::RED, custom_size: Some(Vec2::new(size, size)), - ..Default::default() + ..default() }, - ..Default::default() + ..default() }) // Add an Animator component to control and execute the animation. .insert(Animator::new(tween)); diff --git a/examples/colormaterial_color.rs b/examples/colormaterial_color.rs index 69cd4bd72318fa349461da52c106be383347e2c1..ad6e950728074fffd354348e3e1ed81510dd6c3b 100644 --- a/examples/colormaterial_color.rs +++ b/examples/colormaterial_color.rs @@ -5,21 +5,19 @@ use bevy::{ use bevy_tweening::{lens::*, *}; use std::time::Duration; -fn main() -> Result<(), Box<dyn std::error::Error>> { +fn main() { App::default() .insert_resource(WindowDescriptor { title: "ColorMaterialColorLens".to_string(), width: 1200., height: 600., present_mode: bevy::window::PresentMode::Fifo, // vsync - ..Default::default() + ..default() }) .add_plugins(DefaultPlugins) .add_plugin(TweeningPlugin) .add_startup_system(setup) .run(); - - Ok(()) } fn setup( @@ -93,7 +91,7 @@ fn setup( transform: Transform::from_translation(Vec3::new(x, y, 0.)) .with_scale(Vec3::splat(size)), material: unique_material.clone(), - ..Default::default() + ..default() }) .insert(AssetAnimator::new(unique_material.clone(), tween)); y -= size * spacing; diff --git a/examples/menu.rs b/examples/menu.rs index 5dafd84e66cf35b15727ab0fd2f08424cec27934..1990afaec1f178eee7070f6a162b3052cce58d57 100644 --- a/examples/menu.rs +++ b/examples/menu.rs @@ -3,22 +3,20 @@ use bevy_inspector_egui::WorldInspectorPlugin; use bevy_tweening::{lens::*, *}; use std::time::Duration; -fn main() -> Result<(), Box<dyn std::error::Error>> { +fn main() { App::default() .insert_resource(WindowDescriptor { title: "Menu".to_string(), width: 800., height: 400., present_mode: bevy::window::PresentMode::Fifo, // vsync - ..Default::default() + ..default() }) .add_plugins(DefaultPlugins) .add_plugin(TweeningPlugin) .add_plugin(WorldInspectorPlugin::new()) .add_startup_system(setup) .run(); - - Ok(()) } fn setup(mut commands: Commands, asset_server: Res<AssetServer>) { @@ -38,10 +36,10 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) { align_items: AlignItems::Center, align_self: AlignSelf::Center, justify_content: JustifyContent::Center, - ..Default::default() + ..default() }, color: UiColor(Color::NONE), - ..Default::default() + ..default() }) .insert(Name::new("menu")) .id(); @@ -73,11 +71,11 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) { align_items: AlignItems::Center, align_self: AlignSelf::Center, justify_content: JustifyContent::Center, - ..Default::default() + ..default() }, color: UiColor(Color::rgb_u8(162, 226, 95)), transform: Transform::from_scale(Vec3::splat(0.01)), - ..Default::default() + ..default() }) .insert(Name::new(format!("button:{}", text))) .insert(Parent(container)) @@ -96,7 +94,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) { horizontal: HorizontalAlign::Center, }, ), - ..Default::default() + ..default() }); }); } diff --git a/examples/sequence.rs b/examples/sequence.rs index ebbb51fd5c172aa8e002e2fef2c0d02266d0b6ae..d79a4bc55441f2ed501fafaf9aab931d493fe345 100644 --- a/examples/sequence.rs +++ b/examples/sequence.rs @@ -2,22 +2,20 @@ use bevy::prelude::*; use bevy_tweening::{lens::*, *}; use std::time::Duration; -fn main() -> Result<(), Box<dyn std::error::Error>> { +fn main() { App::default() .insert_resource(WindowDescriptor { title: "Sequence".to_string(), width: 600., height: 600., present_mode: bevy::window::PresentMode::Fifo, // vsync - ..Default::default() + ..default() }) .add_plugins(DefaultPlugins) .add_plugin(TweeningPlugin) .add_startup_system(setup) .add_system(update_text) .run(); - - Ok(()) } #[derive(Component)] @@ -69,7 +67,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) { alignment: text_alignment, }, transform: Transform::from_translation(Vec3::new(0., 40., 0.)), - ..Default::default() + ..default() }) .insert(RedProgress); @@ -90,7 +88,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) { alignment: text_alignment, }, transform: Transform::from_translation(Vec3::new(0., -40., 0.)), - ..Default::default() + ..default() }) .insert(BlueProgress); @@ -128,9 +126,9 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) { sprite: Sprite { color: Color::RED, custom_size: Some(Vec2::new(size, size)), - ..Default::default() + ..default() }, - ..Default::default() + ..default() }) .insert(RedSprite) .insert(Animator::new(seq)); @@ -179,7 +177,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) { sprite: Sprite { color: Color::BLUE, custom_size: Some(Vec2::new(size * 3., size)), - ..Default::default() + ..default() }, ..Default::default() }) diff --git a/examples/sprite_color.rs b/examples/sprite_color.rs index 44b8a82684a441d32c164b9637fa7f241443f643..3f1a94a883dc9f40644330b943930fb70150a690 100644 --- a/examples/sprite_color.rs +++ b/examples/sprite_color.rs @@ -1,21 +1,19 @@ use bevy::prelude::*; use bevy_tweening::{lens::*, *}; -fn main() -> Result<(), Box<dyn std::error::Error>> { +fn main() { App::default() .insert_resource(WindowDescriptor { title: "SpriteColorLens".to_string(), width: 1200., height: 600., present_mode: bevy::window::PresentMode::Fifo, // vsync - ..Default::default() + ..default() }) .add_plugins(DefaultPlugins) .add_plugin(TweeningPlugin) .add_startup_system(setup) .run(); - - Ok(()) } fn setup(mut commands: Commands) { @@ -77,9 +75,9 @@ fn setup(mut commands: Commands) { sprite: Sprite { color: Color::BLACK, custom_size: Some(Vec2::new(size, size)), - ..Default::default() + ..default() }, - ..Default::default() + ..default() }) .insert(Animator::new(tween)); diff --git a/examples/text_color.rs b/examples/text_color.rs index 29a9daea90c37cbdd94c1f420bc4383c80dd0928..523233008d50744112319a13c7ec34aa33e6b491 100644 --- a/examples/text_color.rs +++ b/examples/text_color.rs @@ -4,21 +4,19 @@ use bevy_tweening::{lens::*, *}; const WIDTH: f32 = 1200.; const HEIGHT: f32 = 600.; -fn main() -> Result<(), Box<dyn std::error::Error>> { +fn main() { App::default() .insert_resource(WindowDescriptor { title: "TextColorLens".to_string(), width: WIDTH, height: HEIGHT, present_mode: bevy::window::PresentMode::Fifo, // vsync - ..Default::default() + ..default() }) .add_plugins(DefaultPlugins) .add_plugin(TweeningPlugin) .add_startup_system(setup) .run(); - - Ok(()) } fn setup(mut commands: Commands, asset_server: Res<AssetServer>) { @@ -94,7 +92,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) { align_items: AlignItems::Center, align_self: AlignSelf::Center, justify_content: JustifyContent::Center, - ..Default::default() + ..default() }, text: Text::with_section( *ease_name, @@ -104,9 +102,9 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) { color: Color::WHITE, }, // you can still use Default - Default::default(), + default(), ), - ..Default::default() + ..default() }) .insert(Animator::new(tween)); diff --git a/examples/transform_rotation.rs b/examples/transform_rotation.rs index cd100ff868ab0928ae237ca8ec0b257e61e2aee0..a259ff323ad9913c63f60de89ceae761b632be48 100644 --- a/examples/transform_rotation.rs +++ b/examples/transform_rotation.rs @@ -10,7 +10,7 @@ fn main() { width: 1400., height: 600., present_mode: bevy::window::PresentMode::Fifo, // vsync - ..Default::default() + ..default() }) .add_plugins(DefaultPlugins) .add_plugin(TweeningPlugin) @@ -96,9 +96,9 @@ fn setup(mut commands: Commands) { sprite: Sprite { color: Color::RED, custom_size: Some(Vec2::new(size, size * 0.5)), - ..Default::default() + ..default() }, - ..Default::default() + ..default() }) .insert(Animator::new(tween)); }); diff --git a/examples/transform_translation.rs b/examples/transform_translation.rs index a1e996b7228adb1744f92f4272fd5fa4edda1a91..75cc678cdfbf4cb9b13ade645b79e26d2e6fe912 100644 --- a/examples/transform_translation.rs +++ b/examples/transform_translation.rs @@ -10,7 +10,7 @@ fn main() { width: 1400., height: 600., present_mode: bevy::window::PresentMode::Fifo, // vsync - ..Default::default() + ..default() }) .add_plugins(DefaultPlugins) .add_plugin(TweeningPlugin) @@ -89,9 +89,9 @@ fn setup(mut commands: Commands) { sprite: Sprite { color: Color::RED, custom_size: Some(Vec2::new(size, size)), - ..Default::default() + ..default() }, - ..Default::default() + ..default() }) .insert(Animator::new(tween)); diff --git a/examples/ui_position.rs b/examples/ui_position.rs index 1778b58eec16463283de0618da4b0a4edeaa7dc3..24ff0e3ab90c97f8a20ce11c9a70f772df008dfd 100644 --- a/examples/ui_position.rs +++ b/examples/ui_position.rs @@ -10,7 +10,7 @@ fn main() { width: 1400., height: 600., present_mode: bevy::window::PresentMode::Fifo, // vsync - ..Default::default() + ..default() }) .add_plugins(DefaultPlugins) .add_plugin(TweeningPlugin) @@ -109,10 +109,10 @@ fn setup(mut commands: Commands) { align_items: AlignItems::Center, align_self: AlignSelf::Center, justify_content: JustifyContent::Center, - ..Default::default() + ..default() }, color: UiColor(Color::RED), - ..Default::default() + ..default() }) .insert(Animator::new(tween)); diff --git a/src/lens.rs b/src/lens.rs index 288658980573938b143ec4ae4696d4d0ef854bfc..a8eb3458085fcf2a79a19cd97e5c3deafbe32f22 100644 --- a/src/lens.rs +++ b/src/lens.rs @@ -167,7 +167,7 @@ pub struct TransformRotateXLens { impl Lens<Transform> for TransformRotateXLens { fn lerp(&mut self, target: &mut Transform, ratio: f32) { - let angle = self.start + (self.end - self.start) * ratio; + let angle = (self.end - self.start).mul_add(ratio, self.start); target.rotation = Quat::from_rotation_x(angle); } } @@ -193,7 +193,7 @@ pub struct TransformRotateYLens { impl Lens<Transform> for TransformRotateYLens { fn lerp(&mut self, target: &mut Transform, ratio: f32) { - let angle = self.start + (self.end - self.start) * ratio; + let angle = (self.end - self.start).mul_add(ratio, self.start); target.rotation = Quat::from_rotation_y(angle); } } @@ -219,7 +219,7 @@ pub struct TransformRotateZLens { impl Lens<Transform> for TransformRotateZLens { fn lerp(&mut self, target: &mut Transform, ratio: f32) { - let angle = self.start + (self.end - self.start) * ratio; + let angle = (self.end - self.start).mul_add(ratio, self.start); target.rotation = Quat::from_rotation_z(angle); } } @@ -251,7 +251,7 @@ pub struct TransformRotateAxisLens { impl Lens<Transform> for TransformRotateAxisLens { fn lerp(&mut self, target: &mut Transform, ratio: f32) { - let angle = self.start + (self.end - self.start) * ratio; + let angle = (self.end - self.start).mul_add(ratio, self.start); target.rotation = Quat::from_axis_angle(self.axis, angle); } } @@ -291,8 +291,10 @@ pub struct UiPositionLens { #[cfg(feature = "bevy_ui")] fn lerp_val(start: &Val, end: &Val, ratio: f32) -> Val { match (start, end) { - (Val::Percent(start), Val::Percent(end)) => Val::Percent(start + (end - start) * ratio), - (Val::Px(start), Val::Px(end)) => Val::Px(start + (end - start) * ratio), + (Val::Percent(start), Val::Percent(end)) => { + Val::Percent((end - start).mul_add(ratio, *start)) + } + (Val::Px(start), Val::Px(end)) => Val::Px((end - start).mul_add(ratio, *start)), _ => *start, } } @@ -370,7 +372,7 @@ mod tests { end: Color::BLUE, section: 0, }; - let mut text = Text::with_section("", Default::default(), Default::default()); + let mut text = Text::with_section("", default(), default()); lens.lerp(&mut text, 0.); assert_eq!(text.sections[0].style.color, Color::RED); @@ -623,7 +625,7 @@ mod tests { }; let mut sprite = Sprite { color: Color::WHITE, - ..Default::default() + ..default() }; lens.lerp(&mut sprite, 0.); diff --git a/src/lib.rs b/src/lib.rs index 51f0f8611f4ed62ab56f4bc60aa3e2ebcecafa1b..79b102423c7e3911bd32ad81c5402bff11ea7c14 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -178,7 +178,7 @@ pub enum TweeningType { impl Default for TweeningType { fn default() -> Self { - TweeningType::Once + Self::Once } } @@ -193,17 +193,17 @@ pub enum AnimatorState { impl Default for AnimatorState { fn default() -> Self { - AnimatorState::Playing + Self::Playing } } impl std::ops::Not for AnimatorState { - type Output = AnimatorState; + type Output = Self; fn not(self) -> Self::Output { match self { - AnimatorState::Paused => AnimatorState::Playing, - AnimatorState::Playing => AnimatorState::Paused, + Self::Paused => Self::Playing, + Self::Playing => Self::Paused, } } } @@ -223,31 +223,32 @@ pub enum EaseMethod { } impl EaseMethod { + #[must_use] fn sample(self, x: f32) -> f32 { match self { - EaseMethod::EaseFunction(function) => x.calc(function), - EaseMethod::Linear => x, - EaseMethod::Discrete(limit) => { + Self::EaseFunction(function) => x.calc(function), + Self::Linear => x, + Self::Discrete(limit) => { if x > limit { 1. } else { 0. } } - EaseMethod::CustomFunction(function) => function(x), + Self::CustomFunction(function) => function(x), } } } impl Default for EaseMethod { fn default() -> Self { - EaseMethod::Linear + Self::Linear } } impl From<EaseFunction> for EaseMethod { fn from(ease_function: EaseFunction) -> Self { - EaseMethod::EaseFunction(ease_function) + Self::EaseFunction(ease_function) } } @@ -274,33 +275,153 @@ pub enum TweeningDirection { impl TweeningDirection { /// Is the direction equal to [`TweeningDirection::Forward`]? + #[must_use] pub fn is_forward(&self) -> bool { - *self == TweeningDirection::Forward + *self == Self::Forward } /// Is the direction equal to [`TweeningDirection::Backward`]? + #[must_use] pub fn is_backward(&self) -> bool { - *self == TweeningDirection::Backward + *self == Self::Backward } } impl Default for TweeningDirection { fn default() -> Self { - TweeningDirection::Forward + Self::Forward } } impl std::ops::Not for TweeningDirection { - type Output = TweeningDirection; + type Output = Self; fn not(self) -> Self::Output { match self { - TweeningDirection::Forward => TweeningDirection::Backward, - TweeningDirection::Backward => TweeningDirection::Forward, + Self::Forward => Self::Backward, + Self::Backward => Self::Forward, } } } +macro_rules! animator_impl { + () => { + /// Set the initial playback state of the animator. + #[must_use] + pub fn with_state(mut self, state: AnimatorState) -> Self { + self.state = state; + self + } + + /// Set the initial speed of the animator. See [`Animator::set_speed`] for details. + #[must_use] + pub fn with_speed(mut self, speed: f32) -> Self { + self.speed = speed; + self + } + + /// Set the animation speed. Defaults to 1. + /// + /// A speed of 2 means the animation will run twice as fast while a speed of 0.1 will result in + /// a 10x slowed animation. + pub fn set_speed(&mut self, speed: f32) { + self.speed = speed; + } + + /// Set the top-level tweenable item this animator controls. + pub fn set_tweenable(&mut self, tween: impl Tweenable<T> + Send + Sync + 'static) { + self.tweenable = Some(Box::new(tween)); + } + + /// Get the top-level tweenable this animator is currently controlling. + #[must_use] + pub fn tweenable(&self) -> Option<&(dyn Tweenable<T> + Send + Sync + 'static)> { + if let Some(tweenable) = &self.tweenable { + Some(tweenable.as_ref()) + } else { + None + } + } + + /// Get the top-level mutable tweenable this animator is currently controlling. + #[must_use] + pub fn tweenable_mut(&mut self) -> Option<&mut (dyn Tweenable<T> + Send + Sync + 'static)> { + if let Some(tweenable) = &mut self.tweenable { + Some(tweenable.as_mut()) + } else { + None + } + } + + /// Set the current animation playback progress. + /// + /// See [`progress()`] for details on the meaning. + /// + /// [`progress()`]: Animator::progress + pub fn set_progress(&mut self, progress: f32) { + if let Some(tweenable) = &mut self.tweenable { + tweenable.set_progress(progress) + } + } + + /// Get the current progress in \[0:1\] (non-looping) or \[0:1\[ (looping) of the animation. + /// + /// For looping animations, this reports the progress of the current iteration, in the current direction: + /// - [`TweeningType::Loop`] is 0 at start and 1 at end. The exact value 1.0 is never reached, + /// since the tweenable loops over to 0.0 immediately. + /// - [`TweeningType::PingPong`] is 0 at the source endpoint and 1 and the destination one, + /// which are respectively the start/end for [`TweeningDirection::Forward`], or the end/start + /// for [`TweeningDirection::Backward`]. The exact value 1.0 is never reached, since the tweenable + /// loops over to 0.0 immediately when it changes direction at either endpoint. + /// + /// For sequences, the progress is measured over the entire sequence, from 0 at the start of the first + /// child tweenable to 1 at the end of the last one. + /// + /// For tracks (parallel execution), the progress is measured like a sequence over the longest "path" of + /// child tweenables. In other words, this is the current elapsed time over the total tweenable duration. + #[must_use] + pub fn progress(&self) -> f32 { + if let Some(tweenable) = &self.tweenable { + tweenable.progress() + } else { + 0. + } + } + + /// Ticks the tween, if present. See [`Tweenable::tick`] for details. + pub fn tick( + &mut self, + delta: Duration, + target: &mut T, + entity: Entity, + event_writer: &mut EventWriter<TweenCompleted>, + ) -> Option<TweenState> { + if let Some(tweenable) = &mut self.tweenable { + Some(tweenable.tick(delta.mul_f32(self.speed), target, entity, event_writer)) + } else { + None + } + } + + /// Stop animation playback and rewind the animation. + /// + /// This changes the animator state to [`AnimatorState::Paused`] and rewind its tweenable. + pub fn stop(&mut self) { + self.state = AnimatorState::Paused; + self.rewind(); + } + + /// Rewind animation playback to its initial state. + /// + /// This does not change the playback state (playing/paused). + pub fn rewind(&mut self) { + if let Some(tweenable) = &mut self.tweenable { + tweenable.rewind(); + } + } + }; +} + /// Component to control the animation of another component. #[derive(Component)] pub struct Animator<T: Component> { @@ -320,8 +441,8 @@ impl<T: Component + std::fmt::Debug> std::fmt::Debug for Animator<T> { impl<T: Component> Default for Animator<T> { fn default() -> Self { - Animator { - state: Default::default(), + Self { + state: default(), tweenable: None, speed: 1., } @@ -330,121 +451,15 @@ impl<T: Component> Default for Animator<T> { impl<T: Component> Animator<T> { /// Create a new animator component from a single tweenable. + #[must_use] pub fn new(tween: impl Tweenable<T> + Send + Sync + 'static) -> Self { - Animator { + Self { tweenable: Some(Box::new(tween)), - ..Default::default() + ..default() } } - /// Set the initial playback state of the animator. - pub fn with_state(mut self, state: AnimatorState) -> Self { - self.state = state; - self - } - - /// Set the initial speed of the animator. See [`Animator::set_speed`] for details. - pub fn with_speed(mut self, speed: f32) -> Self { - self.speed = speed; - self - } - - /// Set the animation speed. Defaults to 1. - /// - /// A speed of 2 means the animation will run twice as fast while a speed of 0.1 will result in - /// a 10x slowed animation. - pub fn set_speed(&mut self, speed: f32) { - self.speed = speed; - } - - /// Set the top-level tweenable item this animator controls. - pub fn set_tweenable(&mut self, tween: impl Tweenable<T> + Send + Sync + 'static) { - self.tweenable = Some(Box::new(tween)); - } - - /// Get the top-level tweenable this animator is currently controlling. - pub fn tweenable(&self) -> Option<&(dyn Tweenable<T> + Send + Sync + 'static)> { - if let Some(tweenable) = &self.tweenable { - Some(tweenable.as_ref()) - } else { - None - } - } - - /// Get the top-level mutable tweenable this animator is currently controlling. - pub fn tweenable_mut(&mut self) -> Option<&mut (dyn Tweenable<T> + Send + Sync + 'static)> { - if let Some(tweenable) = &mut self.tweenable { - Some(tweenable.as_mut()) - } else { - None - } - } - - /// Set the current animation playback progress. - /// - /// See [`progress()`] for details on the meaning. - /// - /// [`progress()`]: Animator::progress - pub fn set_progress(&mut self, progress: f32) { - if let Some(tweenable) = &mut self.tweenable { - tweenable.set_progress(progress) - } - } - - /// Get the current progress in \[0:1\] (non-looping) or \[0:1\[ (looping) of the animation. - /// - /// For looping animations, this reports the progress of the current iteration, in the current direction: - /// - [`TweeningType::Loop`] is 0 at start and 1 at end. The exact value 1.0 is never reached, - /// since the tweenable loops over to 0.0 immediately. - /// - [`TweeningType::PingPong`] is 0 at the source endpoint and 1 and the destination one, - /// which are respectively the start/end for [`TweeningDirection::Forward`], or the end/start - /// for [`TweeningDirection::Backward`]. The exact value 1.0 is never reached, since the tweenable - /// loops over to 0.0 immediately when it changes direction at either endpoint. - /// - /// For sequences, the progress is measured over the entire sequence, from 0 at the start of the first - /// child tweenable to 1 at the end of the last one. - /// - /// For tracks (parallel execution), the progress is measured like a sequence over the longest "path" of - /// child tweenables. In other words, this is the current elapsed time over the total tweenable duration. - pub fn progress(&self) -> f32 { - if let Some(tweenable) = &self.tweenable { - tweenable.progress() - } else { - 0. - } - } - - /// Ticks the tween, if present. See [`Tweenable::tick`] for details. - pub fn tick( - &mut self, - delta: Duration, - target: &mut T, - entity: Entity, - event_writer: &mut EventWriter<TweenCompleted>, - ) -> Option<TweenState> { - if let Some(tweenable) = &mut self.tweenable { - Some(tweenable.tick(delta.mul_f32(self.speed), target, entity, event_writer)) - } else { - None - } - } - - /// Stop animation playback and rewind the animation. - /// - /// This changes the animator state to [`AnimatorState::Paused`] and rewind its tweenable. - pub fn stop(&mut self) { - self.state = AnimatorState::Paused; - self.rewind(); - } - - /// Rewind animation playback to its initial state. - /// - /// This does not change the playback state (playing/paused). - pub fn rewind(&mut self) { - if let Some(tweenable) = &mut self.tweenable { - tweenable.rewind(); - } - } + animator_impl!(); } /// Component to control the animation of an asset. @@ -467,10 +482,10 @@ impl<T: Asset + std::fmt::Debug> std::fmt::Debug for AssetAnimator<T> { impl<T: Asset> Default for AssetAnimator<T> { fn default() -> Self { - AssetAnimator { - state: Default::default(), + Self { + state: default(), tweenable: None, - handle: Default::default(), + handle: default(), speed: 1., } } @@ -478,123 +493,18 @@ impl<T: Asset> Default for AssetAnimator<T> { impl<T: Asset> AssetAnimator<T> { /// Create a new asset animator component from a single tweenable. + #[must_use] pub fn new(handle: Handle<T>, tween: impl Tweenable<T> + Send + Sync + 'static) -> Self { - AssetAnimator { + Self { tweenable: Some(Box::new(tween)), handle, - ..Default::default() + ..default() } } - /// Set the initial playback state of the animator. - pub fn with_state(mut self, state: AnimatorState) -> Self { - self.state = state; - self - } - - /// Set the initial speed of the animator. See [`Animator::set_speed`] for details. - pub fn with_speed(mut self, speed: f32) -> Self { - self.speed = speed; - self - } - - /// Set the animation speed. Defaults to 1. - /// - /// A speed of 2 means the animation will run twice as fast while a speed of 0.1 will result in - /// a 10x slowed animation. - pub fn set_speed(&mut self, speed: f32) { - self.speed = speed; - } - - /// Set the top-level tweenable item this animator controls. - pub fn set_tweenable(&mut self, tween: impl Tweenable<T> + Send + Sync + 'static) { - self.tweenable = Some(Box::new(tween)); - } - - /// Get the top-level tweenable this animator is currently controlling. - pub fn tweenable(&self) -> Option<&(dyn Tweenable<T> + Send + Sync + 'static)> { - if let Some(tweenable) = &self.tweenable { - Some(tweenable.as_ref()) - } else { - None - } - } - - /// Get the top-level mutable tweenable this animator is currently controlling. - pub fn tweenable_mut(&mut self) -> Option<&mut (dyn Tweenable<T> + Send + Sync + 'static)> { - if let Some(tweenable) = &mut self.tweenable { - Some(tweenable.as_mut()) - } else { - None - } - } - - /// Set the current animation playback progress. - /// - /// See [`progress()`] for details on the meaning. - /// - /// [`progress()`]: Animator::progress - pub fn set_progress(&mut self, progress: f32) { - if let Some(tweenable) = &mut self.tweenable { - tweenable.set_progress(progress) - } - } - - /// Get the current progress in \[0:1\] (non-looping) or \[0:1\[ (looping) of the animation. - /// - /// For looping animations, this reports the progress of the current iteration, in the current direction: - /// - [`TweeningType::Loop`] is 0 at start and 1 at end. The exact value 1.0 is never reached, - /// since the tweenable loops over to 0.0 immediately. - /// - [`TweeningType::PingPong`] is 0 at the source endpoint and 1 and the destination one, - /// which are respectively the start/end for [`TweeningDirection::Forward`], or the end/start - /// for [`TweeningDirection::Backward`]. The exact value 1.0 is never reached, since the tweenable - /// loops over to 0.0 immediately when it changes direction at either endpoint. - /// - /// For sequences, the progress is measured over the entire sequence, from 0 at the start of the first - /// child tweenable to 1 at the end of the last one. - /// - /// For tracks (parallel execution), the progress is measured like a sequence over the longest "path" of - /// child tweenables. In other words, this is the current elapsed time over the total tweenable duration. - pub fn progress(&self) -> f32 { - if let Some(tweenable) = &self.tweenable { - tweenable.progress() - } else { - 0. - } - } - - /// Ticks the tween, if present. See [`Tweenable::tick`] for details. - pub fn tick( - &mut self, - delta: Duration, - target: &mut T, - entity: Entity, - event_writer: &mut EventWriter<TweenCompleted>, - ) -> Option<TweenState> { - if let Some(tweenable) = &mut self.tweenable { - Some(tweenable.tick(delta.mul_f32(self.speed), target, entity, event_writer)) - } else { - None - } - } - - /// Stop animation playback and rewind the animation. - /// - /// This changes the animator state to [`AnimatorState::Paused`] and rewind its tweenable. - pub fn stop(&mut self) { - self.state = AnimatorState::Paused; - self.rewind(); - } - - /// Rewind animation playback to its initial state. - /// - /// This does not change the playback state (playing/paused). - pub fn rewind(&mut self) { - if let Some(tweenable) = &mut self.tweenable { - tweenable.rewind(); - } - } + animator_impl!(); + #[must_use] fn handle(&self) -> Handle<T> { self.handle.clone() } @@ -687,7 +597,7 @@ mod tests { let tween = Tween::new( EaseFunction::QuadraticInOut, TweeningType::PingPong, - std::time::Duration::from_secs(1), + Duration::from_secs(1), DummyLens { start: 0., end: 1. }, ); let animator = Animator::<DummyComponent>::new(tween); @@ -703,7 +613,7 @@ mod tests { let tween = Tween::<DummyComponent>::new( EaseFunction::QuadraticInOut, TweeningType::PingPong, - std::time::Duration::from_secs(1), + Duration::from_secs(1), DummyLens { start: 0., end: 1. }, ); let animator = Animator::new(tween).with_state(state); @@ -721,7 +631,7 @@ mod tests { let tween = Tween::<DummyComponent>::new( EaseFunction::QuadraticInOut, TweeningType::PingPong, - std::time::Duration::from_secs(1), + Duration::from_secs(1), DummyLens { start: 0., end: 1. }, ); animator.set_tweenable(tween); @@ -735,7 +645,7 @@ mod tests { let tween = Tween::<DummyComponent>::new( EaseFunction::QuadraticInOut, TweeningType::PingPong, - std::time::Duration::from_secs(1), + Duration::from_secs(1), DummyLens { start: 0., end: 1. }, ); let mut animator = Animator::new(tween); @@ -774,7 +684,7 @@ mod tests { let tween = Tween::<DummyAsset>::new( EaseFunction::QuadraticInOut, TweeningType::PingPong, - std::time::Duration::from_secs(1), + Duration::from_secs(1), DummyLens { start: 0., end: 1. }, ); let animator = AssetAnimator::new(Handle::<DummyAsset>::default(), tween); @@ -790,7 +700,7 @@ mod tests { let tween = Tween::<DummyAsset>::new( EaseFunction::QuadraticInOut, TweeningType::PingPong, - std::time::Duration::from_secs(1), + Duration::from_secs(1), DummyLens { start: 0., end: 1. }, ); let animator = @@ -810,7 +720,7 @@ mod tests { let tween = Tween::new( EaseFunction::QuadraticInOut, TweeningType::PingPong, - std::time::Duration::from_secs(1), + Duration::from_secs(1), DummyLens { start: 0., end: 1. }, ); animator.set_tweenable(tween); @@ -825,7 +735,7 @@ mod tests { let tween = Tween::new( EaseFunction::QuadraticInOut, TweeningType::PingPong, - std::time::Duration::from_secs(1), + Duration::from_secs(1), DummyLens { start: 0., end: 1. }, ); let mut animator = AssetAnimator::new(Handle::<DummyAsset>::default(), tween); diff --git a/src/tweenable.rs b/src/tweenable.rs index 5004487b5f44548e1007a81d4467386a9470ef0d..03a868e6d0dab14f32c716e62a673fa7ea7af76c 100644 --- a/src/tweenable.rs +++ b/src/tweenable.rs @@ -51,7 +51,7 @@ struct AnimClock { impl AnimClock { fn new(duration: Duration, is_looping: bool) -> Self { - AnimClock { + Self { elapsed: Duration::ZERO, duration, is_looping, @@ -256,6 +256,7 @@ impl<T: 'static> Tween<T> { /// ); /// let seq = tween1.then(tween2); /// ``` + #[must_use] pub fn then(self, tween: impl Tweenable<T> + Send + Sync + 'static) -> Sequence<T> { Sequence::with_capacity(2).then(self).then(tween) } @@ -279,6 +280,7 @@ impl<T> Tween<T> { /// }, /// ); /// ``` + #[must_use] pub fn new<L>( ease_function: impl Into<EaseMethod>, tweening_type: TweeningType, @@ -288,7 +290,7 @@ impl<T> Tween<T> { where L: Lens<T> + Send + Sync + 'static, { - Tween { + Self { ease_function: ease_function.into(), clock: AnimClock::new(duration, tweening_type != TweeningType::Once), times_completed: 0, @@ -331,6 +333,7 @@ impl<T> Tween<T> { /// ``` /// /// [`set_completed()`]: Tween::set_completed + #[must_use] pub fn with_completed_event(mut self, enabled: bool, user_data: u64) -> Self { self.event_data = if enabled { Some(user_data) } else { None }; self @@ -354,6 +357,7 @@ impl<T> Tween<T> { /// Set the playback direction of the tween. /// /// See [`Tween::set_direction()`]. + #[must_use] pub fn with_direction(mut self, direction: TweeningDirection) -> Self { self.direction = direction; self @@ -362,6 +366,7 @@ impl<T> Tween<T> { /// The current animation direction. /// /// See [`TweeningDirection`] for details. + #[must_use] pub fn direction(&self) -> TweeningDirection { self.direction } @@ -374,7 +379,7 @@ impl<T> Tween<T> { /// Only non-looping tweenables can complete. pub fn set_completed<C>(&mut self, callback: C) where - C: Fn(Entity, &Tween<T>) + Send + Sync + 'static, + C: Fn(Entity, &Self) + Send + Sync + 'static, { self.on_completed = Some(Box::new(callback)); } @@ -486,14 +491,15 @@ impl<T> Sequence<T> { /// Create a new sequence of tweens. /// /// This method panics if the input collection is empty. + #[must_use] pub fn new(items: impl IntoIterator<Item = impl IntoBoxDynTweenable<T>>) -> Self { let tweens: Vec<_> = items .into_iter() .map(IntoBoxDynTweenable::into_box_dyn) .collect(); assert!(!tweens.is_empty()); - let duration = tweens.iter().map(|t| t.duration()).sum(); - Sequence { + let duration = tweens.iter().map(Tweenable::duration).sum(); + Self { tweens, index: 0, duration, @@ -503,9 +509,10 @@ impl<T> Sequence<T> { } /// Create a new sequence containing a single tween. + #[must_use] pub fn from_single(tween: impl Tweenable<T> + Send + Sync + 'static) -> Self { let duration = tween.duration(); - Sequence { + Self { tweens: vec![Box::new(tween)], index: 0, duration, @@ -515,8 +522,9 @@ impl<T> Sequence<T> { } /// Create a new sequence with the specified capacity. + #[must_use] pub fn with_capacity(capacity: usize) -> Self { - Sequence { + Self { tweens: Vec::with_capacity(capacity), index: 0, duration: Duration::ZERO, @@ -526,6 +534,7 @@ impl<T> Sequence<T> { } /// Append a [`Tweenable`] to this sequence. + #[must_use] pub fn then(mut self, tween: impl Tweenable<T> + Send + Sync + 'static) -> Self { self.duration += tween.duration(); self.tweens.push(Box::new(tween)); @@ -533,11 +542,13 @@ impl<T> Sequence<T> { } /// Index of the current active tween in the sequence. + #[must_use] pub fn index(&self) -> usize { self.index.min(self.tweens.len() - 1) } /// Get the current active tween in the sequence. + #[must_use] pub fn current(&self) -> &dyn Tweenable<T> { self.tweens[self.index()].as_ref() } @@ -634,13 +645,14 @@ pub struct Tracks<T> { impl<T> Tracks<T> { /// Create a new [`Tracks`] from an iterator over a collection of [`Tweenable`]. + #[must_use] pub fn new(items: impl IntoIterator<Item = impl IntoBoxDynTweenable<T>>) -> Self { let tracks: Vec<_> = items .into_iter() .map(IntoBoxDynTweenable::into_box_dyn) .collect(); - let duration = tracks.iter().map(|t| t.duration()).max().unwrap(); - Tracks { + let duration = tracks.iter().map(Tweenable::duration).max().unwrap(); + Self { tracks, duration, time: Duration::ZERO, @@ -718,13 +730,15 @@ pub struct Delay { impl Delay { /// Create a new [`Delay`] with a given duration. + #[must_use] pub fn new(duration: Duration) -> Self { - Delay { + Self { timer: Timer::new(duration, false), } } /// Chain another [`Tweenable`] after this tween, making a sequence with the two. + #[must_use] pub fn then<T>(self, tween: impl Tweenable<T> + Send + Sync + 'static) -> Sequence<T> { Sequence::with_capacity(2).then(self).then(tween) }