#![deny(
warnings,
missing_copy_implementations,
trivial_casts,
trivial_numeric_casts,
unsafe_code,
unstable_features,
unused_import_braces,
unused_qualifications,
missing_docs
)]
use std::time::Duration;
use bevy::{asset::Asset, prelude::*};
use interpolation::Ease as IEase;
pub use interpolation::EaseFunction;
pub use interpolation::Lerp;
mod lens;
mod plugin;
pub use lens::{
ColorMaterialColorLens, Lens, SpriteColorLens, TextColorLens, TransformPositionLens,
TransformRotationLens, TransformScaleLens, UiPositionLens,
};
pub use plugin::TweeningPlugin;
#[derive(Clone, Copy)]
pub enum TweeningType {
Once {
duration: Duration,
},
Loop {
duration: Duration,
pause: Option<Duration>,
},
PingPong {
duration: Duration,
pause: Option<Duration>,
},
}
#[derive(PartialEq, Eq, Debug, Clone, Copy)]
pub enum AnimatorState {
Playing,
Paused,
}
impl std::ops::Not for AnimatorState {
type Output = AnimatorState;
fn not(self) -> Self::Output {
match self {
AnimatorState::Paused => AnimatorState::Playing,
AnimatorState::Playing => AnimatorState::Paused,
}
}
}
#[derive(Clone, Copy)]
pub enum EaseMethod {
EaseFunction(EaseFunction),
Linear,
Discrete(f32),
CustomFunction(fn(f32) -> f32),
}
impl EaseMethod {
fn sample(self, x: f32) -> f32 {
match self {
EaseMethod::EaseFunction(function) => x.calc(function),
EaseMethod::Linear => x,
EaseMethod::Discrete(limit) => {
if x > limit {
1.
} else {
0.
}
}
EaseMethod::CustomFunction(function) => function(x),
}
}
}
impl Into<EaseMethod> for EaseFunction {
fn into(self) -> EaseMethod {
EaseMethod::EaseFunction(self)
}
}
#[derive(Component)]
pub struct Animator<T> {
ease_function: EaseMethod,
timer: Timer,
pub state: AnimatorState,
paused: bool,
tweening_type: TweeningType,
direction: i16,
lens: Box<dyn Lens<T> + Send + Sync + 'static>,
}
impl<T: std::fmt::Debug> std::fmt::Debug for Animator<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Animator")
.field("state", &self.state)
.finish()
}
}
impl<T> Animator<T> {
pub fn new<L>(
ease_function: impl Into<EaseMethod>,
tweening_type: TweeningType,
lens: L,
) -> Self
where
L: Lens<T> + Send + Sync + 'static,
{
Animator {
ease_function: ease_function.into(),
timer: match tweening_type {
TweeningType::Once { duration } => Timer::new(duration, false),
TweeningType::Loop { duration, .. } => Timer::new(duration, false),
TweeningType::PingPong { duration, .. } => Timer::new(duration, false),
},
state: AnimatorState::Playing,
paused: false,
tweening_type,
direction: 1,
lens: Box::new(lens),
}
}
#[inline(always)]
fn apply(&mut self, target: &mut T, ratio: f32) {
self.lens.lerp(target, ratio);
}
}
#[derive(Component)]
pub struct AssetAnimator<T: Asset> {
ease_function: EaseMethod,
timer: Timer,
pub state: AnimatorState,
paused: bool,
tweening_type: TweeningType,
direction: i16,
lens: Box<dyn Lens<T> + Send + Sync + 'static>,
handle: Handle<T>,
}
impl<T: Asset + std::fmt::Debug> std::fmt::Debug for AssetAnimator<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("AssetAnimator")
.field("state", &self.state)
.finish()
}
}
impl<T: Asset> AssetAnimator<T> {
pub fn new<L>(
handle: Handle<T>,
ease_function: impl Into<EaseMethod>,
tweening_type: TweeningType,
lens: L,
) -> Self
where
L: Lens<T> + Send + Sync + 'static,
{
AssetAnimator {
ease_function: ease_function.into(),
timer: match tweening_type {
TweeningType::Once { duration } => Timer::new(duration, false),
TweeningType::Loop { duration, .. } => Timer::new(duration, false),
TweeningType::PingPong { duration, .. } => Timer::new(duration, false),
},
state: AnimatorState::Playing,
paused: false,
tweening_type,
direction: 1,
lens: Box::new(lens),
handle,
}
}
fn handle(&self) -> Handle<T> {
self.handle.clone()
}
#[inline(always)]
fn apply(&mut self, target: &mut T, ratio: f32) {
self.lens.lerp(target, ratio);
}
}