use bevy::prelude::*;
use crate::{AnimCompletedEvent, CycleCompletedEvent, TweenAnim, TweenResolver};
#[derive(Debug, Clone, Copy)]
pub struct TweeningPlugin;
impl Plugin for TweeningPlugin {
fn build(&self, app: &mut App) {
app.init_resource::<TweenResolver>()
.add_message::<CycleCompletedEvent>()
.add_message::<AnimCompletedEvent>()
.add_systems(
Update,
animator_system.in_set(AnimationSystem::AnimationUpdate),
);
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, SystemSet)]
#[non_exhaustive]
pub enum AnimationSystem {
AnimationUpdate,
}
pub(crate) fn animator_system(world: &mut World) {
let delta_time = world.resource::<Time>().delta();
TweenAnim::step_all(world, delta_time);
}
#[cfg(test)]
mod tests {
use std::{
ops::DerefMut,
sync::{
atomic::{AtomicBool, Ordering},
Arc,
},
};
use bevy::time::TimePlugin;
use crate::{lens::TransformPositionLens, test_utils::TestEnv, *};
#[test]
fn app() {
let mut app = App::default();
app.add_plugins((TimePlugin, TweeningPlugin));
app.finish();
app.update();
}
#[test]
fn custom_target_entity() {
let tween = Tween::new(
EaseMethod::EaseFunction(EaseFunction::Linear),
Duration::from_secs(1),
TransformPositionLens {
start: Vec3::ZERO,
end: Vec3::ONE,
},
)
.with_cycle_completed_event(true);
let mut env = TestEnv::<Transform>::new(tween);
env.step_all(Duration::ZERO);
let transform = env.component_mut();
assert!(transform.translation.abs_diff_eq(Vec3::ZERO, 1e-5));
env.step_all(Duration::from_millis(500));
let transform = env.component_mut();
assert!(transform.translation.abs_diff_eq(Vec3::splat(0.5), 1e-5));
}
#[test]
fn change_detect_component() {
let tween = Tween::new(
EaseMethod::default(),
Duration::from_secs(1),
TransformPositionLens {
start: Vec3::ZERO,
end: Vec3::ONE,
},
)
.with_cycle_completed_event(true);
let mut env = TestEnv::<Transform>::new(tween);
let transform = env.component_mut();
assert!(transform.is_changed());
env.step_all(Duration::ZERO);
let anim = env.anim().unwrap();
assert_eq!(anim.playback_state, PlaybackState::Playing);
assert_eq!(anim.tweenable.cycles_completed(), 0);
let transform = env.component_mut();
assert!(transform.is_changed());
assert!(transform.translation.abs_diff_eq(Vec3::ZERO, 1e-5));
env.step_all(Duration::from_millis(500));
assert_eq!(env.event_count::<CycleCompletedEvent>(), 0);
let anim = env.anim().unwrap();
assert_eq!(anim.playback_state, PlaybackState::Playing);
assert_eq!(anim.tweenable.cycles_completed(), 0);
let transform = env.component_mut();
assert!(transform.is_changed());
assert!(transform.translation.abs_diff_eq(Vec3::splat(0.5), 1e-5));
env.step_all(Duration::from_millis(500));
assert_eq!(env.event_count::<CycleCompletedEvent>(), 1);
let anim = env.anim();
assert!(anim.is_none()); let transform = env.component_mut();
assert!(transform.is_changed());
assert!(transform.translation.abs_diff_eq(Vec3::ONE, 1e-5));
env.step_all(Duration::from_millis(100));
assert_eq!(env.event_count::<CycleCompletedEvent>(), 0);
let anim = env.anim();
assert!(anim.is_none()); let transform = env.component_mut();
assert!(!transform.is_changed());
assert!(transform.translation.abs_diff_eq(Vec3::ONE, 1e-5));
}
#[derive(Debug, Default, Clone, Copy, Component)]
struct DummyComponent {
value: f32,
}
struct ConditionalDeferLens {
pub defer: Arc<AtomicBool>,
}
impl Lens<DummyComponent> for ConditionalDeferLens {
fn lerp(&mut self, mut target: Mut<DummyComponent>, ratio: f32) {
if self.defer.load(Ordering::SeqCst) {
target.deref_mut().value += ratio;
}
}
}
#[test]
fn change_detect_component_conditional() {
let defer = Arc::new(AtomicBool::new(false));
let tween = Tween::new(
EaseMethod::default(),
Duration::from_secs(1),
ConditionalDeferLens {
defer: Arc::clone(&defer),
},
)
.with_cycle_completed_event(true);
let mut env = TestEnv::<DummyComponent>::new(tween);
let component = env.component_mut();
assert!(component.is_changed());
assert!(!defer.load(Ordering::SeqCst));
env.step_all(Duration::ZERO);
let anim = env.anim().unwrap();
assert_eq!(anim.playback_state, PlaybackState::Playing);
assert_eq!(anim.tweenable.cycles_completed(), 0);
let component = env.component_mut();
assert!(!component.is_changed());
assert!(((*component).value - 0.).abs() <= 1e-5);
env.step_all(Duration::ZERO);
let anim = env.anim().unwrap();
assert_eq!(anim.playback_state, PlaybackState::Playing);
assert_eq!(anim.tweenable.cycles_completed(), 0);
let component = env.component_mut();
assert!(!component.is_changed());
assert!(((*component).value - 0.).abs() <= 1e-5);
env.step_all(Duration::from_millis(200));
let anim = env.anim().unwrap();
assert_eq!(anim.playback_state, PlaybackState::Playing);
assert_eq!(anim.tweenable.cycles_completed(), 0);
let component = env.component_mut();
assert!(!component.is_changed());
assert!(((*component).value - 0.).abs() <= 1e-5);
defer.store(true, Ordering::SeqCst);
env.step_all(Duration::ZERO);
let anim = env.anim().unwrap();
assert_eq!(anim.playback_state, PlaybackState::Playing);
assert_eq!(anim.tweenable.cycles_completed(), 0);
let component = env.component_mut();
assert!(component.is_changed());
assert!(((*component).value - 0.2).abs() <= 1e-5);
env.step_all(Duration::from_millis(300));
let anim = env.anim().unwrap();
assert_eq!(anim.playback_state, PlaybackState::Playing);
assert_eq!(anim.tweenable.cycles_completed(), 0);
let component = env.component_mut();
assert!(component.is_changed());
assert!(((*component).value - 0.7).abs() <= 1e-5);
}
}