1use bevy::prelude::*;
2
3use crate::{AnimCompletedEvent, CycleCompletedEvent, TweenAnim, TweenResolver};
4
5#[derive(Debug, Clone, Copy)]
23pub struct TweeningPlugin;
24
25impl Plugin for TweeningPlugin {
26 fn build(&self, app: &mut App) {
27 app.init_resource::<TweenResolver>()
28 .add_message::<CycleCompletedEvent>()
29 .add_message::<AnimCompletedEvent>()
30 .add_systems(
31 Update,
32 animator_system.in_set(AnimationSystem::AnimationUpdate),
33 );
34 }
35}
36
37#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, SystemSet)]
39#[non_exhaustive]
40pub enum AnimationSystem {
41 AnimationUpdate,
43}
44
45pub(crate) fn animator_system(world: &mut World) {
50 let delta_time = world.resource::<Time>().delta();
51 TweenAnim::step_all(world, delta_time);
52}
53
54#[cfg(test)]
55mod tests {
56 use std::{
57 ops::DerefMut,
58 sync::{
59 atomic::{AtomicBool, Ordering},
60 Arc,
61 },
62 };
63
64 use bevy::time::TimePlugin;
65
66 use crate::{lens::TransformPositionLens, test_utils::TestEnv, *};
67
68 #[test]
69 fn app() {
70 let mut app = App::default();
71 app.add_plugins((TimePlugin, TweeningPlugin));
72 app.finish();
73 app.update();
74 }
75
76 #[test]
77 fn custom_target_entity() {
78 let tween = Tween::new(
79 EaseMethod::EaseFunction(EaseFunction::Linear),
80 Duration::from_secs(1),
81 TransformPositionLens {
82 start: Vec3::ZERO,
83 end: Vec3::ONE,
84 },
85 )
86 .with_cycle_completed_event(true);
87 let mut env = TestEnv::<Transform>::new(tween);
88
89 env.step_all(Duration::ZERO);
90 let transform = env.component_mut();
91 assert!(transform.translation.abs_diff_eq(Vec3::ZERO, 1e-5));
92
93 env.step_all(Duration::from_millis(500));
94 let transform = env.component_mut();
95 assert!(transform.translation.abs_diff_eq(Vec3::splat(0.5), 1e-5));
96 }
97
98 #[test]
99 fn change_detect_component() {
100 let tween = Tween::new(
101 EaseMethod::default(),
102 Duration::from_secs(1),
103 TransformPositionLens {
104 start: Vec3::ZERO,
105 end: Vec3::ONE,
106 },
107 )
108 .with_cycle_completed_event(true);
109
110 let mut env = TestEnv::<Transform>::new(tween);
111
112 let transform = env.component_mut();
114 assert!(transform.is_changed());
115
116 env.step_all(Duration::ZERO);
117
118 let anim = env.anim().unwrap();
119 assert_eq!(anim.playback_state, PlaybackState::Playing);
120 assert_eq!(anim.tweenable.cycles_completed(), 0);
121 let transform = env.component_mut();
122 assert!(transform.is_changed());
123 assert!(transform.translation.abs_diff_eq(Vec3::ZERO, 1e-5));
124
125 env.step_all(Duration::from_millis(500));
126
127 assert_eq!(env.event_count::<CycleCompletedEvent>(), 0);
128 let anim = env.anim().unwrap();
129 assert_eq!(anim.playback_state, PlaybackState::Playing);
130 assert_eq!(anim.tweenable.cycles_completed(), 0);
131 let transform = env.component_mut();
132 assert!(transform.is_changed());
133 assert!(transform.translation.abs_diff_eq(Vec3::splat(0.5), 1e-5));
134
135 env.step_all(Duration::from_millis(500));
136
137 assert_eq!(env.event_count::<CycleCompletedEvent>(), 1);
142 let anim = env.anim();
143 assert!(anim.is_none()); let transform = env.component_mut();
145 assert!(transform.is_changed());
146 assert!(transform.translation.abs_diff_eq(Vec3::ONE, 1e-5));
147
148 env.step_all(Duration::from_millis(100));
150
151 assert_eq!(env.event_count::<CycleCompletedEvent>(), 0);
152 let anim = env.anim();
153 assert!(anim.is_none()); let transform = env.component_mut();
155 assert!(!transform.is_changed());
156 assert!(transform.translation.abs_diff_eq(Vec3::ONE, 1e-5));
157 }
158
159 #[derive(Debug, Default, Clone, Copy, Component)]
160 struct DummyComponent {
161 value: f32,
162 }
163
164 struct ConditionalDeferLens {
167 pub defer: Arc<AtomicBool>,
168 }
169
170 impl Lens<DummyComponent> for ConditionalDeferLens {
171 fn lerp(&mut self, mut target: Mut<DummyComponent>, ratio: f32) {
172 if self.defer.load(Ordering::SeqCst) {
173 target.deref_mut().value += ratio;
174 }
175 }
176 }
177
178 #[test]
179 fn change_detect_component_conditional() {
180 let defer = Arc::new(AtomicBool::new(false));
181 let tween = Tween::new(
182 EaseMethod::default(),
183 Duration::from_secs(1),
184 ConditionalDeferLens {
185 defer: Arc::clone(&defer),
186 },
187 )
188 .with_cycle_completed_event(true);
189
190 let mut env = TestEnv::<DummyComponent>::new(tween);
191
192 let component = env.component_mut();
194 assert!(component.is_changed());
195
196 assert!(!defer.load(Ordering::SeqCst));
197
198 env.step_all(Duration::ZERO);
200
201 let anim = env.anim().unwrap();
202 assert_eq!(anim.playback_state, PlaybackState::Playing);
203 assert_eq!(anim.tweenable.cycles_completed(), 0);
204 let component = env.component_mut();
205 assert!(!component.is_changed());
206 assert!(((*component).value - 0.).abs() <= 1e-5);
207
208 env.step_all(Duration::ZERO);
210
211 let anim = env.anim().unwrap();
212 assert_eq!(anim.playback_state, PlaybackState::Playing);
213 assert_eq!(anim.tweenable.cycles_completed(), 0);
214 let component = env.component_mut();
215 assert!(!component.is_changed());
216 assert!(((*component).value - 0.).abs() <= 1e-5);
217
218 env.step_all(Duration::from_millis(200));
220
221 let anim = env.anim().unwrap();
222 assert_eq!(anim.playback_state, PlaybackState::Playing);
223 assert_eq!(anim.tweenable.cycles_completed(), 0);
224 let component = env.component_mut();
225 assert!(!component.is_changed());
226 assert!(((*component).value - 0.).abs() <= 1e-5);
227
228 defer.store(true, Ordering::SeqCst);
230
231 env.step_all(Duration::ZERO);
235
236 let anim = env.anim().unwrap();
237 assert_eq!(anim.playback_state, PlaybackState::Playing);
238 assert_eq!(anim.tweenable.cycles_completed(), 0);
239 let component = env.component_mut();
240 assert!(component.is_changed());
241 assert!(((*component).value - 0.2).abs() <= 1e-5);
242
243 env.step_all(Duration::from_millis(300));
248
249 let anim = env.anim().unwrap();
250 assert_eq!(anim.playback_state, PlaybackState::Playing);
251 assert_eq!(anim.tweenable.cycles_completed(), 0);
252 let component = env.component_mut();
253 assert!(component.is_changed());
254 assert!(((*component).value - 0.7).abs() <= 1e-5);
255 }
256}