bevy_tweening/
lib.rs

1#![deny(
2    warnings,
3    missing_copy_implementations,
4    trivial_casts,
5    trivial_numeric_casts,
6    unsafe_code,
7    unstable_features,
8    unused_import_braces,
9    unused_qualifications,
10    missing_docs
11)]
12
13//! Tweening animation plugin for the Bevy game engine.
14//!
15//! 🍃 Bevy Tweening provides interpolation-based animation between ("tweening")
16//! two values, to animate any field of any component, resource, or asset,
17//! including both built-in Bevy ones and custom user-defined ones. Each field
18//! of a component, resource, or asset, can be animated via a collection of
19//! predefined easing functions, or providing a custom animation curve. The
20//! library supports any number of animations queued in parallel, even on the
21//! same component, resource, or asset type, and allows runtime control over
22//! playback and animation speed.
23//!
24//! # Quick start
25//!
26//! Look at the documentation for:
27//! - [`Tween`] -- the description of a tweening animation and the explanation
28//!   of some core concepts
29//! - [`TweenAnim`] -- the component representing the runtime animation
30//! - [`AnimTarget`] -- the component defining the target that the animation
31//!   mutates
32//! - [`TweeningPlugin`] -- the plugin to add to your app
33//! - [`EntityCommandsTweeningExtensions`] -- the simplest way to spawn
34//!   animations
35//!
36//! # Example
37//!
38//! Add the [`TweeningPlugin`] to your app:
39//!
40//! ```no_run
41//! use bevy::prelude::*;
42//! use bevy_tweening::*;
43//!
44//! App::default()
45//!     .add_plugins(DefaultPlugins)
46//!     .add_plugins(TweeningPlugin)
47//!     .run();
48//! ```
49//!
50//! Animate the position ([`Transform::translation`]) of an [`Entity`]:
51//!
52//! ```
53//! # use bevy::prelude::*;
54//! # use bevy_tweening::{lens::*, *};
55//! # use std::time::Duration;
56//! # fn system(mut commands: Commands) {
57//! // Create a single animation (tween) to move an entity.
58//! let tween = Tween::new(
59//!     // Use a quadratic easing on both endpoints.
60//!     EaseFunction::QuadraticInOut,
61//!     // It takes 1 second to go from start to end points.
62//!     Duration::from_secs(1),
63//!     // The lens gives access to the Transform component of the Entity,
64//!     // for the TweenAnimator to animate it. It also contains the start and
65//!     // end values respectively associated with the progress ratios 0. and 1.
66//!     TransformPositionLens {
67//!         start: Vec3::ZERO,
68//!         end: Vec3::new(1., 2., -4.),
69//!     },
70//! );
71//!
72//! // Spawn an entity to animate the position of.
73//! commands.spawn((
74//!     Transform::default(),
75//!     // Create a tweenable animation targetting the current entity. Without AnimTarget,
76//!     // the target is implicitly a component on this same entity. The exact component
77//!     // type is derived from the type of the Lens used by the Tweenable itself.
78//!     TweenAnim::new(tween),
79//! ));
80//! # }
81//! ```
82//!
83//! If the target of the animation is not a component on the current entity,
84//! then an [`AnimTarget`] component is necessary to specify that target. Note
85//! that **[`AnimTarget`] is always mandatory for resource and asset
86//! animations**.
87//!
88//! ```
89//! # use bevy::prelude::*;
90//! # use bevy_tweening::{lens::*, *};
91//! # use std::time::Duration;
92//! # fn make_tween<R: Resource>() -> Tween { unimplemented!() }
93//! #[derive(Resource)]
94//! struct MyResource;
95//!
96//! # fn system(mut commands: Commands) {
97//! // Create a single animation (tween) to animate a resource.
98//! let tween = make_tween::<MyResource>();
99//!
100//! // Spawn an entity to own the resource animation.
101//! commands.spawn((
102//!     TweenAnim::new(tween),
103//!     // The AnimTarget is necessary here:
104//!     AnimTarget::resource::<MyResource>(),
105//! ));
106//! # }
107//! ```
108//!
109//! This example shows the general pattern to add animations for any component,
110//! resource, or asset. Since moving the position of an object is a very common
111//! task, 🍃 Bevy Tweening provides a shortcut for it. The above example can be
112//! rewritten more concicely as:
113//!
114//! ```
115//! # use bevy::prelude::*;
116//! # use bevy_tweening::{lens::*, *};
117//! # use std::time::Duration;
118//! # fn system(mut commands: Commands) {
119//! commands
120//!     // Spawn an entity to animate the position of.
121//!     .spawn((Transform::default(),))
122//!     // Create a new Transform::translation animation
123//!     .move_to(
124//!         Vec3::new(1., 2., -4.),
125//!         Duration::from_secs(1),
126//!         EaseFunction::QuadraticInOut,
127//!     );
128//! # }
129//! ```
130//!
131//! The [`move_to()`] extension is convenient helper for animations, which
132//! creates a [`Tween`] that animates the [`Transform::translation`]. It has the
133//! added benefit that the starting point is automatically read from the
134//! component itself; you only need to specify the end position. See the
135//! [`EntityCommandsTweeningExtensions`] extension trait defining helpers for
136//! other common animations.
137//!
138//! # Ready to animate
139//!
140//! Unlike previous versions of 🍃 Bevy Tweening, **you don't need any
141//! particular system setup** aside from adding the [`TweeningPlugin`] to your
142//! [`App`]. In particular, per-component-type and per-asset-type systems are
143//! gone. Instead, the plugin adds a _single_ system executing during the
144//! [`Update`] schedule, which calls [`TweenAnim::step_all()`]. Each
145//! [`TweenAnim`] acts as a controller for one animation, and mutates its
146//! target.
147//!
148//! # Tweenables
149//!
150//! 🍃 Bevy Tweening supports several types of _tweenables_, building blocks
151//! that can be combined to form complex animations. A tweenable is a type
152//! implementing the [`Tweenable`] trait.
153//!
154//! - [`Tween`] - A simple tween (easing) animation between two values.
155//! - [`Sequence`] - A series of tweenables executing in series, one after the
156//!   other.
157//! - [`Delay`] - A time delay. This doesn't animate anything.
158//!
159//! To execute multiple animations in parallel (like the `Tracks` tweenable used
160//! to do in older versions of 🍃 Bevy Tweening; it's now removed), simply
161//! enqueue each animation independently. This require careful selection of
162//! individual timings though if you want to synchronize those animations.
163//!
164//! ## Chaining animations
165//!
166//! Most tweenables can be chained with the `then()` operator to produce a
167//! [`Sequence`] tweenable:
168//!
169//! ```
170//! # use bevy::prelude::*;
171//! # use bevy_tweening::{lens::*, *};
172//! # use std::time::Duration;
173//! let tween1 = Tween::new(
174//!     // [...]
175//! #    EaseFunction::BounceOut,
176//! #    Duration::from_secs(2),
177//! #    TransformScaleLens {
178//! #        start: Vec3::ZERO,
179//! #        end: Vec3::ONE,
180//! #    },
181//! );
182//! let tween2 = Tween::new(
183//!     // [...]
184//! #    EaseFunction::QuadraticInOut,
185//! #    Duration::from_secs(1),
186//! #    TransformPositionLens {
187//! #        start: Vec3::ZERO,
188//! #        end: Vec3::new(3.5, 0., 0.),
189//! #    },
190//! );
191//! // Produce a Sequence executing first 'tween1' then 'tween2'
192//! let seq = tween1.then(tween2);
193//! ```
194//!
195//! Note that some tweenable animations can be of infinite duration; this is the
196//! case for example when using [`RepeatCount::Infinite`]. If you add such an
197//! infinite animation in a sequence, and append more tweenables after it,
198//! **those tweenables will never play** because playback will be stuck forever
199//! repeating the first animation. You're responsible for creating sequences
200//! that make sense. In general, only use infinite tweenable animations alone or
201//! as the last element of a sequence (for example, move to position and then
202//! rotate forever on self).
203//!
204//! # `TweenAnim`
205//!
206//! Bevy components, resources, and assets, are animated with the [`TweenAnim`]
207//! component. This component acts as a controller for a single animation. It
208//! determines the target component, resource, or asset, to animate, via an
209//! [`AnimTarget`], and accesses the field(s) of that target using a [`Lens`].
210//!
211//! - Components are animated via the [`AnimTargetKind::Component`], which
212//!   identifies a component instance on an entity via the [`Entity`] itself. If
213//!   that target entity is the same as the one owning the [`TweenAnim`], then
214//!   the [`AnimTarget`] can be omitted, for convenience.
215//! - Resources are animated via the [`AnimTargetKind::Resource`].
216//! - Assets are animated via the [`AnimTargetKind::Asset`] which identifies an
217//!   asset via the type of its [`Assets`] collection (and so indirectly the
218//!   type of asset itself) and the [`AssetId`] referencing that asset inside
219//!   that collection.
220//!
221//! Because assets are typically shared, and the animation applies to the asset
222//! itself, all users of the asset see the animation. For example, animating the
223//! color of a [`ColorMaterial`] will change the color of all the 2D meshes
224//! using that material. If you want to animate the color of a single mesh, you
225//! need to duplicate the asset and assign a unique copy to that mesh,
226//! then animate that copy alone.
227//!
228//! After that, you can use the [`TweenAnim`] component to control the animation
229//! playback:
230//!
231//! ```no_run
232//! # use bevy::prelude::Single;
233//! # use bevy_tweening::*;
234//! fn my_system(mut anim: Single<&mut TweenAnim>) {
235//!     anim.speed = 0.8; // 80% playback speed
236//! }
237//! ```
238//!
239//! ## Lenses
240//!
241//! The [`AnimTarget`] references the target (component, resource, or asset)
242//! being animated. However, only a part of that target is generally animated.
243//! To that end, the [`TweenAnim`] (or, more exactly, the [`Tweenable`] it uses)
244//! accesses the field(s) to animate via a _lens_, a type that implements the
245//! [`Lens`] trait and allows mapping a target to the actual value(s) animated.
246//!
247//! For example, the [`TransformPositionLens`] uses a [`Transform`] component as
248//! input, and animates its [`Transform::translation`] field only, leaving the
249//! rotation and scale unchanged.
250//!
251//! ```no_run
252//! # use bevy::{prelude::{Transform, Vec3}, ecs::change_detection::Mut};
253//! # use bevy_tweening::Lens;
254//! # struct TransformPositionLens { start: Vec3, end: Vec3 };
255//! impl Lens<Transform> for TransformPositionLens {
256//!     fn lerp(&mut self, mut target: Mut<Transform>, ratio: f32) {
257//!         target.translation = self.start.lerp(self.end, ratio);
258//!     }
259//! }
260//! ```
261//!
262//! Several built-in lenses are provided in the [`lens`] module for the most
263//! commonly animated fields, like the components of a [`Transform`]. Those are
264//! provided for convenience and mainly as examples. 🍃 Bevy Tweening expects
265//! you to write your own lenses by implementing the [`Lens`] trait, which as
266//! you can see above is very simple. This allows animating virtually any field
267//! of any component, resource, or asset, whether shipped with Bevy or defined
268//! by the user.
269//!
270//! # Tweening vs. keyframed animation
271//!
272//! 🍃 Bevy Tweening is a "tweening" animation library. It focuses on simple
273//! animations often used in applications and games to breathe life into a user
274//! interface or the objects of a game world. The API design favors simplicity,
275//! often for quick one-shot animations created from code. This type of
276//! animation is inherently simpler than a full-blown animation solution, like
277//! `bevy_animation`, which typically works with complex keyframe-based
278//! animation curves authored via Digital Content Creation (DCC) tools like 3D
279//! modellers and exported as assets, and whose most common usage is skeletal
280//! animation of characters. There's a grey area between those two approaches,
281//! and you can use both to achieve most animations, but 🍃 Bevy Tweening will
282//! shine for simpler animations while `bevy_animation` while offer a more
283//! extensive support for larger, more complex ones.
284//!
285//! [`Transform::translation`]: https://docs.rs/bevy/0.17/bevy/transform/components/struct.Transform.html#structfield.translation
286//! [`Entity`]: https://docs.rs/bevy/0.17/bevy/ecs/entity/struct.Entity.html
287//! [`ColorMaterial`]: https://docs.rs/bevy/0.17/bevy/sprite/struct.ColorMaterial.html
288//! [`Transform`]: https://docs.rs/bevy/0.17/bevy/transform/components/struct.Transform.html
289//! [`TransformPositionLens`]: crate::lens::TransformPositionLens
290//! [`move_to()`]: crate::EntityCommandsTweeningExtensions::move_to
291
292use std::{
293    any::TypeId,
294    ops::{Deref, DerefMut},
295    time::Duration,
296};
297
298use bevy::{
299    asset::UntypedAssetId,
300    ecs::{
301        change_detection::MutUntyped,
302        component::{ComponentId, Components, Mutable},
303    },
304    platform::collections::HashMap,
305    prelude::*,
306};
307pub use lens::Lens;
308use lens::{
309    TransformRotateAdditiveXLens, TransformRotateAdditiveYLens, TransformRotateAdditiveZLens,
310};
311pub use plugin::{AnimationSystem, TweeningPlugin};
312use thiserror::Error;
313pub use tweenable::{
314    BoxedTweenable, CycleCompletedEvent, Delay, IntoBoxedTweenable, Sequence, TotalDuration, Tween,
315    TweenState, Tweenable,
316};
317
318use crate::{
319    lens::{TransformPositionLens, TransformScaleLens},
320    tweenable::TweenConfig,
321};
322
323pub mod lens;
324mod plugin;
325mod tweenable;
326
327#[cfg(test)]
328mod test_utils;
329
330/// How many times to repeat a tweenable animation.
331///
332/// See also [`RepeatStrategy`].
333///
334/// Default: `Finite(1)`
335#[derive(Debug, Clone, Copy, PartialEq, Eq)]
336pub enum RepeatCount {
337    /// Run the animation an exact number of times.
338    ///
339    /// The total playback duration is the tweenable animation duration times
340    /// this number of iterations.
341    Finite(u32),
342    /// Run the animation for some duration.
343    ///
344    /// If this duration is not a multiple of the tweenable animation duration,
345    /// then the animation will get stopped midway through its playback,
346    /// possibly even before finishing a single loop.
347    For(Duration),
348    /// Loop the animation indefinitely.
349    Infinite,
350}
351
352impl Default for RepeatCount {
353    fn default() -> Self {
354        Self::Finite(1)
355    }
356}
357
358impl From<u32> for RepeatCount {
359    fn from(value: u32) -> Self {
360        Self::Finite(value)
361    }
362}
363
364impl From<Duration> for RepeatCount {
365    fn from(value: Duration) -> Self {
366        Self::For(value)
367    }
368}
369
370impl RepeatCount {
371    /// Calculate the total duration for this repeat count.
372    pub fn total_duration(&self, cycle_duration: Duration) -> TotalDuration {
373        match self {
374            RepeatCount::Finite(count) => TotalDuration::Finite(cycle_duration * *count),
375            RepeatCount::For(duration) => TotalDuration::Finite(*duration),
376            RepeatCount::Infinite => TotalDuration::Infinite,
377        }
378    }
379}
380
381/// Repeat strategy for animation cycles.
382///
383/// Only applicable when [`RepeatCount`] is greater than the total duration of
384/// the tweenable animation.
385///
386/// Default: `Repeat`.
387#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
388pub enum RepeatStrategy {
389    /// Reset the cycle back to its starting position.
390    ///
391    /// When playback reaches the end of the animation cycle, it jumps directly
392    /// back to the cycle start. This can create discontinuities if the
393    /// animation is not authored to be looping.
394    #[default]
395    Repeat,
396
397    /// Follow a ping-pong pattern, changing the cycle direction each time an
398    /// endpoint is reached.
399    ///
400    /// A complete loop start -> end -> start always counts as 2 cycles for the
401    /// various operations where cycle count matters. That is, an animation with
402    /// a 1-second cycle and a mirrored repeat strategy will take 2 seconds
403    /// to end up back in the state where it started.
404    ///
405    /// This strategy ensures that there's no discontinuity in the animation,
406    /// since there's no jump.
407    MirroredRepeat,
408}
409
410/// Playback state of a [`TweenAnim`].
411///
412/// Default: `Playing`.
413#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
414pub enum PlaybackState {
415    /// The animation is playing. This is the default state.
416    #[default]
417    Playing,
418    /// The animation is paused in its current state.
419    Paused,
420}
421
422impl std::ops::Not for PlaybackState {
423    type Output = Self;
424
425    fn not(self) -> Self::Output {
426        match self {
427            Self::Paused => Self::Playing,
428            Self::Playing => Self::Paused,
429        }
430    }
431}
432
433/// Describe how eased value should be computed.
434///
435/// This function is applied to the cycle fraction `t` representing the playback
436/// position over the cycle duration. The result is used to interpolate the
437/// animation target.
438///
439/// In general a [`Lens`] should perform a linear interpolation over its target,
440/// and the non-linear behavior (for example, bounciness, etc.) comes from this
441/// function. This ensures the same [`Lens`] can be reused in multiple contexts,
442/// while the "shape" of the animation is controlled independently.
443///
444/// Default: `EaseFunction::Linear`.
445#[derive(Debug, Clone, Copy)]
446pub enum EaseMethod {
447    /// Follow [`EaseFunction`].
448    EaseFunction(EaseFunction),
449    /// Discrete interpolation. The eased value will jump from start to end when
450    /// stepping over the discrete limit, which must be value between 0 and 1.
451    Discrete(f32),
452    /// Use a custom function to interpolate the value. The function is called
453    /// with the cycle ratio, in `[0:1]`, as parameter, and must return the
454    /// easing factor, typically also in `[0:1]`. Note that values outside this
455    /// unit range may not work well with some animations; for example if
456    /// animating a color, a negative red values have no meaning.
457    CustomFunction(fn(f32) -> f32),
458}
459
460impl EaseMethod {
461    #[must_use]
462    fn sample(self, x: f32) -> f32 {
463        match self {
464            Self::EaseFunction(function) => EasingCurve::new(0.0, 1.0, function).sample(x).unwrap(),
465            Self::Discrete(limit) => {
466                if x > limit {
467                    1.
468                } else {
469                    0.
470                }
471            }
472            Self::CustomFunction(function) => function(x),
473        }
474    }
475}
476
477impl Default for EaseMethod {
478    fn default() -> Self {
479        Self::EaseFunction(EaseFunction::Linear)
480    }
481}
482
483impl From<EaseFunction> for EaseMethod {
484    fn from(ease_function: EaseFunction) -> Self {
485        Self::EaseFunction(ease_function)
486    }
487}
488
489/// Direction a tweening animation is playing.
490///
491/// The playback direction determines if the delta animation time passed to
492/// [`Tweenable::step()`] is added or subtracted to the current time position on
493/// the animation's timeline.
494/// - In `Forward` direction, time passes forward from `t=0` to the total
495///   duration of the animation.
496/// - Conversely, in `Backward` direction, time passes backward from the total
497///   duration back to `t=0`.
498///
499/// Note that backward playback is supported for infinite animations (when the
500/// repeat count is [`RepeatCount::Infinite`]), but [`Tweenable::rewind()`] is
501/// not supported and will panic.
502///
503/// Default: `Forward`.
504#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
505pub enum PlaybackDirection {
506    /// Animation playing from start to end.
507    #[default]
508    Forward,
509    /// Animation playing from end to start, in reverse.
510    Backward,
511}
512
513impl PlaybackDirection {
514    /// Is the direction equal to [`PlaybackDirection::Forward`]?
515    #[must_use]
516    pub fn is_forward(&self) -> bool {
517        *self == Self::Forward
518    }
519
520    /// Is the direction equal to [`PlaybackDirection::Backward`]?
521    #[must_use]
522    pub fn is_backward(&self) -> bool {
523        *self == Self::Backward
524    }
525}
526
527impl std::ops::Not for PlaybackDirection {
528    type Output = Self;
529
530    fn not(self) -> Self::Output {
531        match self {
532            Self::Forward => Self::Backward,
533            Self::Backward => Self::Forward,
534        }
535    }
536}
537
538/// Extension trait for [`EntityCommands`], adding animation functionalities for
539/// commonly used tweening animations.
540///
541/// This trait extends [`EntityCommands`] to provide convenience helpers to
542/// common tweening animations like moving the position of an entity by
543/// animating its [`Transform::translation`].
544///
545/// One of the major source of convenience provided by these helpers is the fact
546/// that some of the data necessary to create the tween animation is
547/// automatically derived from the current value of the component at the time
548/// when the command is processed. For example, the [`move_to()`] helper only
549/// requires specifying the end position, and will automatically read the start
550/// position from the current [`Transform::translation`] value. This avoids
551/// having to explicitly access that component to read that value and manually
552/// store it into a [`Lens`].
553///
554/// [`move_to()`]: Self::move_to
555pub trait EntityCommandsTweeningExtensions<'a> {
556    /// Queue a new tween animation to move the current entity.
557    ///
558    /// The entity must have a [`Transform`] component. The tween animation will
559    /// be initialized with the current [`Transform::translation`] as its
560    /// starting point, and the given endpoint, duration, and ease method.
561    ///
562    /// Note that the starting point position is saved when the command is
563    /// applied, generally after the current system when [`apply_deferred()`]
564    /// runs. So any change to [`Transform::translation`] between this call and
565    /// [`apply_deferred()`] will be taken into account.
566    ///
567    /// This function is a fire-and-forget convenience helper, and doesn't give
568    /// access to the [`Entity`] created. To retrieve the entity and control
569    /// the animation playback, you should spawn a [`TweenAnim`] component
570    /// manually.
571    ///
572    /// # Example
573    ///
574    /// ```
575    /// # use bevy::{prelude::*, ecs::world::CommandQueue};
576    /// # use bevy_tweening::*;
577    /// # use std::time::Duration;
578    /// # let mut queue = CommandQueue::default();
579    /// # let mut world = World::default();
580    /// # let mut commands = Commands::new(&mut queue, &mut world);
581    /// commands.spawn(Transform::default()).move_to(
582    ///     Vec3::new(3.5, 0., 0.),
583    ///     Duration::from_secs(1),
584    ///     EaseFunction::QuadraticIn,
585    /// );
586    /// ```
587    ///
588    /// [`apply_deferred()`]: bevy::ecs::system::System::apply_deferred
589    fn move_to(
590        self,
591        end: Vec3,
592        duration: Duration,
593        ease_method: impl Into<EaseMethod>,
594    ) -> AnimatedEntityCommands<'a, impl TweenCommand>;
595
596    /// Queue a new tween animation to move the current entity.
597    ///
598    /// The entity must have a [`Transform`] component. The tween animation will
599    /// be initialized with the current [`Transform::translation`] as its
600    /// ending point, and the given starting point, duration, and ease method.
601    ///
602    /// Note that the ending point position is saved when the command is
603    /// applied, generally after the current system when [`apply_deferred()`]
604    /// runs. So any change to [`Transform::translation`] between this call and
605    /// [`apply_deferred()`] will be taken into account.
606    ///
607    /// This function is a fire-and-forget convenience helper, and doesn't give
608    /// access to the [`Entity`] created. To retrieve the entity and control
609    /// the animation playback, you should spawn a [`TweenAnim`] component
610    /// manually.
611    ///
612    /// # Example
613    ///
614    /// ```
615    /// # use bevy::{prelude::*, ecs::world::CommandQueue};
616    /// # use bevy_tweening::*;
617    /// # use std::time::Duration;
618    /// # let mut queue = CommandQueue::default();
619    /// # let mut world = World::default();
620    /// # let mut commands = Commands::new(&mut queue, &mut world);
621    /// commands.spawn(Transform::default()).move_from(
622    ///     Vec3::new(3.5, 0., 0.),
623    ///     Duration::from_secs(1),
624    ///     EaseFunction::QuadraticIn,
625    /// );
626    /// ```
627    ///
628    /// [`apply_deferred()`]: bevy::ecs::system::System::apply_deferred
629    fn move_from(
630        self,
631        start: Vec3,
632        duration: Duration,
633        ease_method: impl Into<EaseMethod>,
634    ) -> AnimatedEntityCommands<'a, impl TweenCommand>;
635
636    /// Queue a new tween animation to scale the current entity.
637    ///
638    /// The entity must have a [`Transform`] component. The tween animation will
639    /// be initialized with the current [`Transform::scale`] as its starting
640    /// point, and the given endpoint, duration, and ease method.
641    ///
642    /// Note that the starting point scale is saved when the command is applied,
643    /// generally after the current system when [`apply_deferred()`]
644    /// runs. So any change to [`Transform::scale`] between this call and
645    /// [`apply_deferred()`] will be taken into account.
646    ///
647    /// This function is a fire-and-forget convenience helper, and doesn't give
648    /// access to the [`Entity`] created. To retrieve the entity and control
649    /// the animation playback, you should spawn a [`TweenAnim`] component
650    /// manually.
651    ///
652    /// # Example
653    ///
654    /// ```
655    /// # use bevy::{prelude::*, ecs::world::CommandQueue};
656    /// # use bevy_tweening::*;
657    /// # use std::time::Duration;
658    /// # let mut queue = CommandQueue::default();
659    /// # let mut world = World::default();
660    /// # let mut commands = Commands::new(&mut queue, &mut world);
661    /// commands.spawn(Transform::default()).scale_to(
662    ///     Vec3::splat(2.), // 200% size
663    ///     Duration::from_secs(1),
664    ///     EaseFunction::QuadraticIn,
665    /// );
666    /// ```
667    ///
668    /// [`apply_deferred()`]: bevy::ecs::system::System::apply_deferred
669    fn scale_to(
670        self,
671        end: Vec3,
672        duration: Duration,
673        ease_method: impl Into<EaseMethod>,
674    ) -> AnimatedEntityCommands<'a, impl TweenCommand>;
675
676    /// Queue a new tween animation to scale the current entity.
677    ///
678    /// The entity must have a [`Transform`] component. The tween animation will
679    /// be initialized with the current [`Transform::scale`] as its ending
680    /// point, and the given start scale, duration, and ease method.
681    ///
682    /// Note that the ending point scale is saved when the command is applied,
683    /// generally after the current system when [`apply_deferred()`]
684    /// runs. So any change to [`Transform::scale`] between this call and
685    /// [`apply_deferred()`] will be taken into account.
686    ///
687    /// This function is a fire-and-forget convenience helper, and doesn't give
688    /// access to the [`Entity`] created. To retrieve the entity and control
689    /// the animation playback, you should spawn a [`TweenAnim`] component
690    /// manually.
691    ///
692    /// # Example
693    ///
694    /// ```
695    /// # use bevy::{prelude::*, ecs::world::CommandQueue};
696    /// # use bevy_tweening::*;
697    /// # use std::time::Duration;
698    /// # let mut queue = CommandQueue::default();
699    /// # let mut world = World::default();
700    /// # let mut commands = Commands::new(&mut queue, &mut world);
701    /// commands.spawn(Transform::default()).scale_from(
702    ///     Vec3::splat(0.8), // 80% size
703    ///     Duration::from_secs(1),
704    ///     EaseFunction::QuadraticIn,
705    /// );
706    /// ```
707    ///
708    /// [`apply_deferred()`]: bevy::ecs::system::System::apply_deferred
709    fn scale_from(
710        self,
711        start: Vec3,
712        duration: Duration,
713        ease_method: impl Into<EaseMethod>,
714    ) -> AnimatedEntityCommands<'a, impl TweenCommand>;
715
716    /// Queue a new tween animation to rotate the current entity around its X
717    /// axis continuously (repeats forever, linearly).
718    ///
719    /// The entity must have a [`Transform`] component.
720    ///
721    /// This function is a fire-and-forget convenience helper, and doesn't give
722    /// access to the [`Entity`] created. To retrieve the entity and control
723    /// the animation playback, you should spawn a [`TweenAnim`] component
724    /// manually.
725    ///
726    /// # Example
727    ///
728    /// ```
729    /// # use bevy::{prelude::*, ecs::world::CommandQueue};
730    /// # use bevy_tweening::*;
731    /// # use std::time::Duration;
732    /// # let mut queue = CommandQueue::default();
733    /// # let mut world = World::default();
734    /// # let mut commands = Commands::new(&mut queue, &mut world);
735    /// commands
736    ///     .spawn(Transform::default())
737    ///     .rotate_x(Duration::from_secs(1));
738    /// ```
739    fn rotate_x(self, cycle_duration: Duration) -> AnimatedEntityCommands<'a, impl TweenCommand>;
740
741    /// Queue a new tween animation to rotate the current entity around its Y
742    /// axis continuously (repeats forever, linearly).
743    ///
744    /// The entity must have a [`Transform`] component.
745    ///
746    /// This function is a fire-and-forget convenience helper, and doesn't give
747    /// access to the [`Entity`] created. To retrieve the entity and control
748    /// the animation playback, you should spawn a [`TweenAnim`] component
749    /// manually.
750    ///
751    /// # Example
752    ///
753    /// ```
754    /// # use bevy::{prelude::*, ecs::world::CommandQueue};
755    /// # use bevy_tweening::*;
756    /// # use std::time::Duration;
757    /// # let mut queue = CommandQueue::default();
758    /// # let mut world = World::default();
759    /// # let mut commands = Commands::new(&mut queue, &mut world);
760    /// commands
761    ///     .spawn(Transform::default())
762    ///     .rotate_y(Duration::from_secs(1));
763    /// ```
764    fn rotate_y(self, cycle_duration: Duration) -> AnimatedEntityCommands<'a, impl TweenCommand>;
765
766    /// Queue a new tween animation to rotate the current entity around its Z
767    /// axis continuously (repeats forever, linearly).
768    ///
769    /// The entity must have a [`Transform`] component.
770    ///
771    /// This function is a fire-and-forget convenience helper, and doesn't give
772    /// access to the [`Entity`] created. To retrieve the entity and control
773    /// the animation playback, you should spawn a [`TweenAnim`] component
774    /// manually.
775    ///
776    /// # Example
777    ///
778    /// ```
779    /// # use bevy::{prelude::*, ecs::world::CommandQueue};
780    /// # use bevy_tweening::*;
781    /// # use std::time::Duration;
782    /// # let mut queue = CommandQueue::default();
783    /// # let mut world = World::default();
784    /// # let mut commands = Commands::new(&mut queue, &mut world);
785    /// commands
786    ///     .spawn(Transform::default())
787    ///     .rotate_z(Duration::from_secs(1));
788    /// ```
789    fn rotate_z(self, cycle_duration: Duration) -> AnimatedEntityCommands<'a, impl TweenCommand>;
790
791    /// Queue a new tween animation to rotate the current entity around its X
792    /// axis by a given angle.
793    ///
794    /// The entity must have a [`Transform`] component. The animation applies a
795    /// rotation on top of the value of the [`Transform`] at the time the
796    /// animation is queued.
797    ///
798    /// This function is a fire-and-forget convenience helper, and doesn't give
799    /// access to the [`Entity`] created. To retrieve the entity and control
800    /// the animation playback, you should spawn a [`TweenAnim`] component
801    /// manually.
802    ///
803    /// # Example
804    ///
805    /// ```
806    /// # use bevy::{prelude::*, ecs::world::CommandQueue};
807    /// # use bevy_tweening::*;
808    /// # use std::time::Duration;
809    /// # let mut queue = CommandQueue::default();
810    /// # let mut world = World::default();
811    /// # let mut commands = Commands::new(&mut queue, &mut world);
812    /// commands.spawn(Transform::default()).rotate_x_by(
813    ///     std::f32::consts::FRAC_PI_4,
814    ///     Duration::from_secs(1),
815    ///     EaseFunction::QuadraticIn,
816    /// );
817    /// ```
818    fn rotate_x_by(
819        self,
820        angle: f32,
821        duration: Duration,
822        ease_method: impl Into<EaseMethod>,
823    ) -> AnimatedEntityCommands<'a, impl TweenCommand>;
824
825    /// Queue a new tween animation to rotate the current entity around its Y
826    /// axis by a given angle.
827    ///
828    /// The entity must have a [`Transform`] component. The animation applies a
829    /// rotation on top of the value of the [`Transform`] at the time the
830    /// animation is queued.
831    ///
832    /// This function is a fire-and-forget convenience helper, and doesn't give
833    /// access to the [`Entity`] created. To retrieve the entity and control
834    /// the animation playback, you should spawn a [`TweenAnim`] component
835    /// manually.
836    ///
837    /// # Example
838    ///
839    /// ```
840    /// # use bevy::{prelude::*, ecs::world::CommandQueue};
841    /// # use bevy_tweening::*;
842    /// # use std::time::Duration;
843    /// # let mut queue = CommandQueue::default();
844    /// # let mut world = World::default();
845    /// # let mut commands = Commands::new(&mut queue, &mut world);
846    /// commands.spawn(Transform::default()).rotate_y_by(
847    ///     std::f32::consts::FRAC_PI_4,
848    ///     Duration::from_secs(1),
849    ///     EaseFunction::QuadraticIn,
850    /// );
851    /// ```
852    fn rotate_y_by(
853        self,
854        angle: f32,
855        duration: Duration,
856        ease_method: impl Into<EaseMethod>,
857    ) -> AnimatedEntityCommands<'a, impl TweenCommand>;
858
859    /// Queue a new tween animation to rotate the current entity around its Z
860    /// axis by a given angle.
861    ///
862    /// The entity must have a [`Transform`] component. The animation applies a
863    /// rotation on top of the value of the [`Transform`] at the time the
864    /// animation is queued.
865    ///
866    /// This function is a fire-and-forget convenience helper, and doesn't give
867    /// access to the [`Entity`] created. To retrieve the entity and control
868    /// the animation playback, you should spawn a [`TweenAnim`] component
869    /// manually.
870    ///
871    /// # Example
872    ///
873    /// ```
874    /// # use bevy::{prelude::*, ecs::world::CommandQueue};
875    /// # use bevy_tweening::*;
876    /// # use std::time::Duration;
877    /// # let mut queue = CommandQueue::default();
878    /// # let mut world = World::default();
879    /// # let mut commands = Commands::new(&mut queue, &mut world);
880    /// commands.spawn(Transform::default()).rotate_z_by(
881    ///     std::f32::consts::FRAC_PI_4,
882    ///     Duration::from_secs(1),
883    ///     EaseFunction::QuadraticIn,
884    /// );
885    /// ```
886    fn rotate_z_by(
887        self,
888        angle: f32,
889        duration: Duration,
890        ease_method: impl Into<EaseMethod>,
891    ) -> AnimatedEntityCommands<'a, impl TweenCommand>;
892}
893
894/// Helper trait to abstract a tweening animation command.
895///
896/// This is mostly used internally by the [`AnimatedEntityCommands`] to tweak
897/// the current animation, while also abstracting the various commands used to
898/// implement the [`EntityCommandsTweeningExtensions`]. In general, you probably
899/// don't have any use for that trait.
900pub trait TweenCommand: EntityCommand {
901    /// Get read-only access to the tween configuration of the command.
902    #[allow(unused)]
903    fn config(&self) -> &TweenConfig;
904
905    /// Get mutable access to the tween configuration of the command.
906    fn config_mut(&mut self) -> &mut TweenConfig;
907}
908
909/// Animation command to move an entity to a target position.
910#[derive(Clone, Copy)]
911pub(crate) struct MoveToCommand {
912    end: Vec3,
913    config: TweenConfig,
914}
915
916impl EntityCommand for MoveToCommand {
917    fn apply(self, mut entity: EntityWorldMut) {
918        if let Some(start) = entity.get::<Transform>().map(|tr| tr.translation) {
919            let lens = TransformPositionLens {
920                start,
921                end: self.end,
922            };
923            let tween = Tween::from_config(self.config, lens);
924            let anim_target = AnimTarget::component::<Transform>(entity.id());
925            entity.world_scope(|world| {
926                world.spawn((TweenAnim::new(tween), anim_target));
927            });
928        }
929    }
930}
931
932impl TweenCommand for MoveToCommand {
933    #[inline]
934    fn config(&self) -> &TweenConfig {
935        &self.config
936    }
937
938    #[inline]
939    fn config_mut(&mut self) -> &mut TweenConfig {
940        &mut self.config
941    }
942}
943
944/// Animation command to move an entity from a source position.
945#[derive(Clone, Copy)]
946pub(crate) struct MoveFromCommand {
947    start: Vec3,
948    config: TweenConfig,
949}
950
951impl EntityCommand for MoveFromCommand {
952    fn apply(self, mut entity: EntityWorldMut) {
953        if let Some(end) = entity.get::<Transform>().map(|tr| tr.translation) {
954            let lens = TransformPositionLens {
955                start: self.start,
956                end,
957            };
958            let tween = Tween::from_config(self.config, lens);
959            let anim_target = AnimTarget::component::<Transform>(entity.id());
960            entity.world_scope(|world| {
961                world.spawn((TweenAnim::new(tween), anim_target));
962            });
963        }
964    }
965}
966
967impl TweenCommand for MoveFromCommand {
968    #[inline]
969    fn config(&self) -> &TweenConfig {
970        &self.config
971    }
972
973    #[inline]
974    fn config_mut(&mut self) -> &mut TweenConfig {
975        &mut self.config
976    }
977}
978
979/// Animation command to scale an entity to a target size.
980#[derive(Clone, Copy)]
981pub(crate) struct ScaleToCommand {
982    end: Vec3,
983    config: TweenConfig,
984}
985
986impl EntityCommand for ScaleToCommand {
987    fn apply(self, mut entity: EntityWorldMut) {
988        if let Some(start) = entity.get::<Transform>().map(|tr| tr.scale) {
989            let lens = TransformScaleLens {
990                start,
991                end: self.end,
992            };
993            let tween = Tween::from_config(self.config, lens);
994            let anim_target = AnimTarget::component::<Transform>(entity.id());
995            entity.world_scope(|world| {
996                world.spawn((TweenAnim::new(tween), anim_target));
997            });
998        }
999    }
1000}
1001
1002impl TweenCommand for ScaleToCommand {
1003    #[inline]
1004    fn config(&self) -> &TweenConfig {
1005        &self.config
1006    }
1007
1008    #[inline]
1009    fn config_mut(&mut self) -> &mut TweenConfig {
1010        &mut self.config
1011    }
1012}
1013
1014/// Animation command to scale an entity from a source size.
1015#[derive(Clone, Copy)]
1016pub(crate) struct ScaleFromCommand {
1017    start: Vec3,
1018    config: TweenConfig,
1019}
1020
1021impl EntityCommand for ScaleFromCommand {
1022    fn apply(self, mut entity: EntityWorldMut) {
1023        if let Some(end) = entity.get::<Transform>().map(|tr| tr.scale) {
1024            let lens = TransformScaleLens {
1025                start: self.start,
1026                end,
1027            };
1028            let tween = Tween::from_config(self.config, lens);
1029            let anim_target = AnimTarget::component::<Transform>(entity.id());
1030            entity.world_scope(|world| {
1031                world.spawn((TweenAnim::new(tween), anim_target));
1032            });
1033        }
1034    }
1035}
1036
1037impl TweenCommand for ScaleFromCommand {
1038    #[inline]
1039    fn config(&self) -> &TweenConfig {
1040        &self.config
1041    }
1042
1043    #[inline]
1044    fn config_mut(&mut self) -> &mut TweenConfig {
1045        &mut self.config
1046    }
1047}
1048
1049/// Animation command to rotate an entity around its X axis.
1050#[derive(Clone, Copy)]
1051pub(crate) struct RotateXCommand {
1052    config: TweenConfig,
1053}
1054
1055impl EntityCommand for RotateXCommand {
1056    fn apply(self, mut entity: EntityWorldMut) {
1057        if let Some(base_rotation) = entity.get::<Transform>().map(|tr| tr.rotation) {
1058            let lens = TransformRotateAdditiveXLens {
1059                base_rotation,
1060                start: 0.,
1061                end: std::f32::consts::TAU,
1062            };
1063            let tween = Tween::from_config(self.config, lens)
1064                .with_repeat(RepeatCount::Infinite, RepeatStrategy::Repeat);
1065            let anim_target = AnimTarget::component::<Transform>(entity.id());
1066            entity.world_scope(|world| {
1067                world.spawn((TweenAnim::new(tween), anim_target));
1068            });
1069        }
1070    }
1071}
1072
1073impl TweenCommand for RotateXCommand {
1074    #[inline]
1075    fn config(&self) -> &TweenConfig {
1076        &self.config
1077    }
1078
1079    #[inline]
1080    fn config_mut(&mut self) -> &mut TweenConfig {
1081        &mut self.config
1082    }
1083}
1084
1085/// Animation command to rotate an entity around its Y axis.
1086#[derive(Clone, Copy)]
1087pub(crate) struct RotateYCommand {
1088    config: TweenConfig,
1089}
1090
1091impl EntityCommand for RotateYCommand {
1092    fn apply(self, mut entity: EntityWorldMut) {
1093        if let Some(base_rotation) = entity.get::<Transform>().map(|tr| tr.rotation) {
1094            let lens = TransformRotateAdditiveYLens {
1095                base_rotation,
1096                start: 0.,
1097                end: std::f32::consts::TAU,
1098            };
1099            let tween = Tween::from_config(self.config, lens)
1100                .with_repeat(RepeatCount::Infinite, RepeatStrategy::Repeat);
1101            let anim_target = AnimTarget::component::<Transform>(entity.id());
1102            entity.world_scope(|world| {
1103                world.spawn((TweenAnim::new(tween), anim_target));
1104            });
1105        }
1106    }
1107}
1108
1109impl TweenCommand for RotateYCommand {
1110    #[inline]
1111    fn config(&self) -> &TweenConfig {
1112        &self.config
1113    }
1114
1115    #[inline]
1116    fn config_mut(&mut self) -> &mut TweenConfig {
1117        &mut self.config
1118    }
1119}
1120
1121/// Animation command to rotate an entity around its Z axis.
1122#[derive(Clone, Copy)]
1123pub(crate) struct RotateZCommand {
1124    config: TweenConfig,
1125}
1126
1127impl EntityCommand for RotateZCommand {
1128    fn apply(self, mut entity: EntityWorldMut) {
1129        if let Some(base_rotation) = entity.get::<Transform>().map(|tr| tr.rotation) {
1130            let lens = TransformRotateAdditiveZLens {
1131                base_rotation,
1132                start: 0.,
1133                end: std::f32::consts::TAU,
1134            };
1135            let tween = Tween::from_config(self.config, lens)
1136                .with_repeat(RepeatCount::Infinite, RepeatStrategy::Repeat);
1137            let anim_target = AnimTarget::component::<Transform>(entity.id());
1138            entity.world_scope(|world| {
1139                world.spawn((TweenAnim::new(tween), anim_target));
1140            });
1141        }
1142    }
1143}
1144
1145impl TweenCommand for RotateZCommand {
1146    #[inline]
1147    fn config(&self) -> &TweenConfig {
1148        &self.config
1149    }
1150
1151    #[inline]
1152    fn config_mut(&mut self) -> &mut TweenConfig {
1153        &mut self.config
1154    }
1155}
1156
1157/// Animation command to rotate an entity around its X axis by a given angle.
1158#[derive(Clone, Copy)]
1159pub(crate) struct RotateXByCommand {
1160    angle: f32,
1161    config: TweenConfig,
1162}
1163
1164impl EntityCommand for RotateXByCommand {
1165    fn apply(self, mut entity: EntityWorldMut) {
1166        if let Some(base_rotation) = entity.get::<Transform>().map(|tr| tr.rotation) {
1167            let lens = TransformRotateAdditiveXLens {
1168                base_rotation,
1169                start: 0.,
1170                end: self.angle,
1171            };
1172            let tween = Tween::from_config(self.config, lens);
1173            let anim_target = AnimTarget::component::<Transform>(entity.id());
1174            entity.world_scope(|world| {
1175                world.spawn((TweenAnim::new(tween), anim_target));
1176            });
1177        }
1178    }
1179}
1180
1181impl TweenCommand for RotateXByCommand {
1182    #[inline]
1183    fn config(&self) -> &TweenConfig {
1184        &self.config
1185    }
1186
1187    #[inline]
1188    fn config_mut(&mut self) -> &mut TweenConfig {
1189        &mut self.config
1190    }
1191}
1192
1193/// Animation command to rotate an entity around its Y axis by a given angle.
1194#[derive(Clone, Copy)]
1195pub(crate) struct RotateYByCommand {
1196    angle: f32,
1197    config: TweenConfig,
1198}
1199
1200impl EntityCommand for RotateYByCommand {
1201    fn apply(self, mut entity: EntityWorldMut) {
1202        if let Some(base_rotation) = entity.get::<Transform>().map(|tr| tr.rotation) {
1203            let lens = TransformRotateAdditiveYLens {
1204                base_rotation,
1205                start: 0.,
1206                end: self.angle,
1207            };
1208            let tween = Tween::from_config(self.config, lens);
1209            let anim_target = AnimTarget::component::<Transform>(entity.id());
1210            entity.world_scope(|world| {
1211                world.spawn((TweenAnim::new(tween), anim_target));
1212            });
1213        }
1214    }
1215}
1216
1217impl TweenCommand for RotateYByCommand {
1218    #[inline]
1219    fn config(&self) -> &TweenConfig {
1220        &self.config
1221    }
1222
1223    #[inline]
1224    fn config_mut(&mut self) -> &mut TweenConfig {
1225        &mut self.config
1226    }
1227}
1228
1229/// Animation command to rotate an entity around its Z axis by a given angle.
1230#[derive(Clone, Copy)]
1231pub(crate) struct RotateZByCommand {
1232    angle: f32,
1233    config: TweenConfig,
1234}
1235
1236impl EntityCommand for RotateZByCommand {
1237    fn apply(self, mut entity: EntityWorldMut) {
1238        if let Some(base_rotation) = entity.get::<Transform>().map(|tr| tr.rotation) {
1239            let lens = TransformRotateAdditiveZLens {
1240                base_rotation,
1241                start: 0.,
1242                end: self.angle,
1243            };
1244            let tween = Tween::from_config(self.config, lens);
1245            let anim_target = AnimTarget::component::<Transform>(entity.id());
1246            entity.world_scope(|world| {
1247                world.spawn((TweenAnim::new(tween), anim_target));
1248            });
1249        }
1250    }
1251}
1252
1253impl TweenCommand for RotateZByCommand {
1254    #[inline]
1255    fn config(&self) -> &TweenConfig {
1256        &self.config
1257    }
1258
1259    #[inline]
1260    fn config_mut(&mut self) -> &mut TweenConfig {
1261        &mut self.config
1262    }
1263}
1264
1265/// Wrapper over an [`EntityCommands`] which stores an animation command.
1266///
1267/// The wrapper acts as, and dereferences to, a regular [`EntityCommands`] as
1268/// _e.g._ returned by [`Commands::spawn()`]. In addition, it stores a pending
1269/// animation command, which can be further tweaked before being queued into the
1270/// entity commands queue. This deferred queuing allows fluent patterns like:
1271///
1272/// ```
1273/// # use std::time::Duration;
1274/// # use bevy::prelude::*;
1275/// # use bevy_tweening::*;
1276/// # fn my_system(mut commands: Commands) {
1277/// commands
1278///     .spawn(Transform::default())
1279///     // Consume the EntityCommands, and wrap it into an AnimatedEntityCommands,
1280///     // which stores an animation command to move an entity.
1281///     .move_to(
1282///         Vec3::ONE,
1283///         Duration::from_millis(400),
1284///         EaseFunction::QuadraticIn,
1285///     )
1286///     // Tweak the stored animation to set the repeat count of the Tween.
1287///     .with_repeat_count(2);
1288/// # }
1289/// ```
1290///
1291/// The animation commands always stores the last animation inserted. When the
1292/// commands is mutably dereferenced, it first flushes the pending animation
1293/// command, if any, by inserting it into the underlying [`EntityCommands`]
1294/// queue. It also flushes the animation when dropped, to ensure the last
1295/// animation is queued too.
1296///
1297/// To move from an [`AnimatedEntityCommands`] to its underlying
1298/// [`EntityCommands`], the former automatically dereferences to the latter.
1299/// Note however that once you're back on the base [`EntityCommands`], you can
1300/// only get a new [`AnimatedEntityCommands`] via functions consuming the
1301/// [`EntityCommands`] by value. In that case, you need to call [`reborrow()`]:
1302///
1303/// ```
1304/// # use std::time::Duration;
1305/// # use bevy::prelude::*;
1306/// # use bevy_tweening::*;
1307/// # fn my_system(mut commands: Commands) {
1308/// commands
1309///     .spawn(Transform::default())
1310///     .move_to(
1311///         Vec3::ONE,
1312///         Duration::from_millis(400),
1313///         EaseFunction::QuadraticIn,
1314///     )
1315///     // This call invokes std::ops::DerefMut, and returns a mutable ref
1316///     // to the underlying EntityCommands
1317///     .insert(Name::new("my_object"))
1318///     // Here we need to reborrow() to convert from `&mut EntityCommands`
1319///     // (by mutable ref) to `EntityCommands` (by value)
1320///     .reborrow()
1321///     // This call requires an `EntityCommands` (by value)
1322///     .scale_to(
1323///         Vec3::splat(1.1),
1324///         Duration::from_millis(400),
1325///         EaseFunction::Linear,
1326///     );
1327/// # }
1328/// ```
1329///
1330/// [`reborrow()`]: bevy::prelude::EntityCommands::reborrow
1331pub struct AnimatedEntityCommands<'a, C: TweenCommand> {
1332    commands: EntityCommands<'a>,
1333    cmd: Option<C>,
1334}
1335
1336impl<'a, C: TweenCommand> AnimatedEntityCommands<'a, C> {
1337    /// Wrap an [`EntityCommands`] into an animated one.
1338    pub fn new(commands: EntityCommands<'a>, cmd: C) -> Self {
1339        Self {
1340            commands,
1341            cmd: Some(cmd),
1342        }
1343    }
1344
1345    /// Set the repeat count of this animation.
1346    #[inline]
1347    pub fn with_repeat_count(mut self, repeat_count: impl Into<RepeatCount>) -> Self {
1348        if let Some(cmd) = self.cmd.as_mut() {
1349            cmd.config_mut().repeat_count = repeat_count.into();
1350        }
1351        self
1352    }
1353
1354    /// Set the repeat strategy of this animation.
1355    #[inline]
1356    pub fn with_repeat_strategy(mut self, repeat_strategy: RepeatStrategy) -> Self {
1357        if let Some(cmd) = self.cmd.as_mut() {
1358            cmd.config_mut().repeat_strategy = repeat_strategy;
1359        }
1360        self
1361    }
1362
1363    /// Configure the repeat parameters of this animation.
1364    ///
1365    /// This is a shortcut for:
1366    ///
1367    /// ```no_run
1368    /// # use bevy_tweening::*;
1369    /// # struct AnimatedEntityCommands {}
1370    /// # impl AnimatedEntityCommands {
1371    /// # fn with_repeat_count(self, r: RepeatCount) -> Self { unimplemented!() }
1372    /// # fn with_repeat_strategy(self, r: RepeatStrategy) -> Self { unimplemented!() }
1373    /// # fn xxx(self) -> Self {
1374    /// # let repeat_count = RepeatCount::Infinite;
1375    /// # let repeat_strategy = RepeatStrategy::Repeat;
1376    /// self.with_repeat_count(repeat_count)
1377    ///     .with_repeat_strategy(repeat_strategy)
1378    /// # }}
1379    /// ```
1380    #[inline]
1381    pub fn with_repeat(
1382        self,
1383        repeat_count: impl Into<RepeatCount>,
1384        repeat_strategy: RepeatStrategy,
1385    ) -> Self {
1386        self.with_repeat_count(repeat_count)
1387            .with_repeat_strategy(repeat_strategy)
1388    }
1389
1390    /// Consume self and return the inner [`EntityCommands`].
1391    ///
1392    /// The current animation is inserted into the commands queue, before that
1393    /// wrapped commands queue is returned.
1394    pub fn into_inner(mut self) -> EntityCommands<'a> {
1395        self.flush();
1396        // Since we already flushed above, we don't need Drop. And trying to keep would
1397        // allow it to access self.commands after it was stolen (even though we know the
1398        // implementation doesn't in practice). Still, it's safer to just short-circuit
1399        // Drop here.
1400        let this = std::mem::ManuallyDrop::new(self);
1401        // SAFETY: We have flushed self.cmd which is now None, and we're stealing
1402        // self.commands, after which the this object is forgotten and never
1403        // accessed again.
1404        #[allow(unsafe_code)]
1405        unsafe {
1406            std::ptr::read(&this.commands)
1407        }
1408    }
1409
1410    /// Flush the current animation, inserting it into the commands queue.
1411    ///
1412    /// This makes it impossible to further tweak the animation. This is
1413    /// automatically called when a new animation is created and when the
1414    /// commands queue is dropped with the last animation pending.
1415    fn flush(&mut self) {
1416        if let Some(cmd) = self.cmd.take() {
1417            self.queue(cmd);
1418        }
1419    }
1420}
1421
1422impl<'a, C: TweenCommand> EntityCommandsTweeningExtensions<'a> for AnimatedEntityCommands<'a, C> {
1423    #[inline]
1424    fn move_to(
1425        self,
1426        end: Vec3,
1427        duration: Duration,
1428        ease_method: impl Into<EaseMethod>,
1429    ) -> AnimatedEntityCommands<'a, impl TweenCommand> {
1430        self.into_inner().move_to(end, duration, ease_method)
1431    }
1432
1433    #[inline]
1434    fn move_from(
1435        self,
1436        start: Vec3,
1437        duration: Duration,
1438        ease_method: impl Into<EaseMethod>,
1439    ) -> AnimatedEntityCommands<'a, impl TweenCommand> {
1440        self.into_inner().move_from(start, duration, ease_method)
1441    }
1442
1443    #[inline]
1444    fn scale_to(
1445        self,
1446        end: Vec3,
1447        duration: Duration,
1448        ease_method: impl Into<EaseMethod>,
1449    ) -> AnimatedEntityCommands<'a, impl TweenCommand> {
1450        self.into_inner().scale_to(end, duration, ease_method)
1451    }
1452
1453    #[inline]
1454    fn scale_from(
1455        self,
1456        start: Vec3,
1457        duration: Duration,
1458        ease_method: impl Into<EaseMethod>,
1459    ) -> AnimatedEntityCommands<'a, impl TweenCommand> {
1460        self.into_inner().scale_from(start, duration, ease_method)
1461    }
1462
1463    #[inline]
1464    fn rotate_x(self, cycle_duration: Duration) -> AnimatedEntityCommands<'a, impl TweenCommand> {
1465        self.into_inner().rotate_x(cycle_duration)
1466    }
1467
1468    #[inline]
1469    fn rotate_y(self, cycle_duration: Duration) -> AnimatedEntityCommands<'a, impl TweenCommand> {
1470        self.into_inner().rotate_y(cycle_duration)
1471    }
1472
1473    #[inline]
1474    fn rotate_z(self, cycle_duration: Duration) -> AnimatedEntityCommands<'a, impl TweenCommand> {
1475        self.into_inner().rotate_z(cycle_duration)
1476    }
1477
1478    #[inline]
1479    fn rotate_x_by(
1480        self,
1481        angle: f32,
1482        duration: Duration,
1483        ease_method: impl Into<EaseMethod>,
1484    ) -> AnimatedEntityCommands<'a, impl TweenCommand> {
1485        self.into_inner().rotate_x_by(angle, duration, ease_method)
1486    }
1487
1488    #[inline]
1489    fn rotate_y_by(
1490        self,
1491        angle: f32,
1492        duration: Duration,
1493        ease_method: impl Into<EaseMethod>,
1494    ) -> AnimatedEntityCommands<'a, impl TweenCommand> {
1495        self.into_inner().rotate_y_by(angle, duration, ease_method)
1496    }
1497
1498    #[inline]
1499    fn rotate_z_by(
1500        self,
1501        angle: f32,
1502        duration: Duration,
1503        ease_method: impl Into<EaseMethod>,
1504    ) -> AnimatedEntityCommands<'a, impl TweenCommand> {
1505        self.into_inner().rotate_z_by(angle, duration, ease_method)
1506    }
1507}
1508
1509impl<'a, C: TweenCommand> Deref for AnimatedEntityCommands<'a, C> {
1510    type Target = EntityCommands<'a>;
1511
1512    fn deref(&self) -> &Self::Target {
1513        &self.commands
1514    }
1515}
1516
1517impl<C: TweenCommand> DerefMut for AnimatedEntityCommands<'_, C> {
1518    fn deref_mut(&mut self) -> &mut Self::Target {
1519        self.flush();
1520        &mut self.commands
1521    }
1522}
1523
1524impl<C: TweenCommand> Drop for AnimatedEntityCommands<'_, C> {
1525    fn drop(&mut self) {
1526        self.flush();
1527    }
1528}
1529
1530impl<'a> EntityCommandsTweeningExtensions<'a> for EntityCommands<'a> {
1531    #[inline]
1532    fn move_to(
1533        self,
1534        end: Vec3,
1535        duration: Duration,
1536        ease_method: impl Into<EaseMethod>,
1537    ) -> AnimatedEntityCommands<'a, impl TweenCommand> {
1538        AnimatedEntityCommands::new(
1539            self,
1540            MoveToCommand {
1541                end,
1542                config: TweenConfig {
1543                    ease_method: ease_method.into(),
1544                    cycle_duration: duration,
1545                    ..default()
1546                },
1547            },
1548        )
1549    }
1550
1551    #[inline]
1552    fn move_from(
1553        self,
1554        start: Vec3,
1555        duration: Duration,
1556        ease_method: impl Into<EaseMethod>,
1557    ) -> AnimatedEntityCommands<'a, impl TweenCommand> {
1558        AnimatedEntityCommands::new(
1559            self,
1560            MoveFromCommand {
1561                start,
1562                config: TweenConfig {
1563                    ease_method: ease_method.into(),
1564                    cycle_duration: duration,
1565                    ..default()
1566                },
1567            },
1568        )
1569    }
1570
1571    #[inline]
1572    fn scale_to(
1573        self,
1574        end: Vec3,
1575        duration: Duration,
1576        ease_method: impl Into<EaseMethod>,
1577    ) -> AnimatedEntityCommands<'a, impl TweenCommand> {
1578        AnimatedEntityCommands::new(
1579            self,
1580            ScaleToCommand {
1581                end,
1582                config: TweenConfig {
1583                    ease_method: ease_method.into(),
1584                    cycle_duration: duration,
1585                    ..default()
1586                },
1587            },
1588        )
1589    }
1590
1591    #[inline]
1592    fn scale_from(
1593        self,
1594        start: Vec3,
1595        duration: Duration,
1596        ease_method: impl Into<EaseMethod>,
1597    ) -> AnimatedEntityCommands<'a, impl TweenCommand> {
1598        AnimatedEntityCommands::new(
1599            self,
1600            ScaleFromCommand {
1601                start,
1602                config: TweenConfig {
1603                    ease_method: ease_method.into(),
1604                    cycle_duration: duration,
1605                    ..default()
1606                },
1607            },
1608        )
1609    }
1610
1611    #[inline]
1612    fn rotate_x(self, cycle_duration: Duration) -> AnimatedEntityCommands<'a, impl TweenCommand> {
1613        AnimatedEntityCommands::new(
1614            self,
1615            RotateXCommand {
1616                config: TweenConfig {
1617                    ease_method: EaseFunction::Linear.into(),
1618                    cycle_duration,
1619                    ..default()
1620                },
1621            },
1622        )
1623    }
1624
1625    #[inline]
1626    fn rotate_y(self, cycle_duration: Duration) -> AnimatedEntityCommands<'a, impl TweenCommand> {
1627        AnimatedEntityCommands::new(
1628            self,
1629            RotateYCommand {
1630                config: TweenConfig {
1631                    ease_method: EaseFunction::Linear.into(),
1632                    cycle_duration,
1633                    ..default()
1634                },
1635            },
1636        )
1637    }
1638
1639    #[inline]
1640    fn rotate_z(self, cycle_duration: Duration) -> AnimatedEntityCommands<'a, impl TweenCommand> {
1641        AnimatedEntityCommands::new(
1642            self,
1643            RotateZCommand {
1644                config: TweenConfig {
1645                    ease_method: EaseFunction::Linear.into(),
1646                    cycle_duration,
1647                    ..default()
1648                },
1649            },
1650        )
1651    }
1652
1653    #[inline]
1654    fn rotate_x_by(
1655        self,
1656        angle: f32,
1657        duration: Duration,
1658        ease_method: impl Into<EaseMethod>,
1659    ) -> AnimatedEntityCommands<'a, impl TweenCommand> {
1660        AnimatedEntityCommands::new(
1661            self,
1662            RotateXByCommand {
1663                angle,
1664                config: TweenConfig {
1665                    ease_method: ease_method.into(),
1666                    cycle_duration: duration,
1667                    ..default()
1668                },
1669            },
1670        )
1671    }
1672
1673    #[inline]
1674    fn rotate_y_by(
1675        self,
1676        angle: f32,
1677        duration: Duration,
1678        ease_method: impl Into<EaseMethod>,
1679    ) -> AnimatedEntityCommands<'a, impl TweenCommand> {
1680        AnimatedEntityCommands::new(
1681            self,
1682            RotateYByCommand {
1683                angle,
1684                config: TweenConfig {
1685                    ease_method: ease_method.into(),
1686                    cycle_duration: duration,
1687                    ..default()
1688                },
1689            },
1690        )
1691    }
1692
1693    #[inline]
1694    fn rotate_z_by(
1695        self,
1696        angle: f32,
1697        duration: Duration,
1698        ease_method: impl Into<EaseMethod>,
1699    ) -> AnimatedEntityCommands<'a, impl TweenCommand> {
1700        AnimatedEntityCommands::new(
1701            self,
1702            RotateZByCommand {
1703                angle,
1704                config: TweenConfig {
1705                    ease_method: ease_method.into(),
1706                    cycle_duration: duration,
1707                    ..default()
1708                },
1709            },
1710        )
1711    }
1712}
1713
1714/// Event raised when a [`TweenAnim`] completed.
1715#[derive(Debug, Clone, Copy, Event, Message)]
1716pub struct AnimCompletedEvent {
1717    /// The entity owning the [`TweenAnim`] which completed.
1718    ///
1719    /// Note that commonly the [`TweenAnim`] is despawned on completion, so
1720    /// can't be queried anymore with this entity. You can prevent a completed
1721    /// animation from being automatically destroyed by
1722    /// setting [`TweenAnim::destroy_on_completion`] to `false`.
1723    pub anim_entity: Entity,
1724    /// The animation target.
1725    ///
1726    /// This is provided both as a convenience for [`TweenAnim`]s not destroyed
1727    /// on completion, and because for those animations which are destroyed
1728    /// on completion the information is not available anymore when this
1729    /// event is received.
1730    pub target: AnimTargetKind,
1731}
1732
1733/// Errors returned by various animation functions.
1734#[derive(Debug, Error, Clone, Copy)]
1735pub enum TweeningError {
1736    /// The asset resolver for the given asset is not registered.
1737    #[error("Asset resolver for asset with resource ID {0:?} is not registered.")]
1738    AssetResolverNotRegistered(ComponentId),
1739    /// The entity was not found in the World.
1740    #[error("Entity {0:?} not found in the World.")]
1741    EntityNotFound(Entity),
1742    /// The entity should have had a TweenAnim but it was not found.
1743    #[error("Entity {0:?} doesn't have a TweenAnim.")]
1744    MissingTweenAnim(Entity),
1745    /// The component of the given type is not registered.
1746    #[error("Component of type {0:?} is not registered in the World.")]
1747    ComponentNotRegistered(TypeId),
1748    /// The resource of the given type is not registered.
1749    #[error("Resource of type {0:?} is not registered in the World.")]
1750    ResourceNotRegistered(TypeId),
1751    /// The asset container for the given asset type is not registered.
1752    #[error("Asset container Assets<A> for asset type A = {0:?} is not registered in the World.")]
1753    AssetNotRegistered(TypeId),
1754    /// The component of the given type is not registered.
1755    #[error("Component of type {0:?} is not present on entity {1:?}.")]
1756    MissingComponent(TypeId, Entity),
1757    /// The asset cannot be found.
1758    #[error("Asset ID {0:?} is invalid.")]
1759    InvalidAssetId(UntypedAssetId),
1760    /// The asset ID references a different type than expected.
1761    #[error("Expected type of asset ID to be {expected:?} but got {actual:?} instead.")]
1762    InvalidAssetIdType {
1763        /// The expected asset type.
1764        expected: TypeId,
1765        /// The actual type the asset ID references.
1766        actual: TypeId,
1767    },
1768    /// Expected [`Tweenable::target_type_id()`] to return a value, but it
1769    /// returned `None`.
1770    #[error("Expected a typed Tweenable.")]
1771    UntypedTweenable,
1772    /// Invalid [`Entity`].
1773    #[error("Invalid Entity {0:?}.")]
1774    InvalidTweenId(Entity),
1775    /// Cannot change target kind.
1776    #[error("Unexpected target kind: was component={0}, now component={1}")]
1777    MismatchingTargetKind(bool, bool),
1778    /// Cannot change component type.
1779    #[error("Cannot change component type: was component_id={0:?}, now component_id={1:?}")]
1780    MismatchingComponentId(ComponentId, ComponentId),
1781    /// Cannot change asset type.
1782    #[error("Cannot change asset type: was component_id={0:?}, now component_id={1:?}")]
1783    MismatchingAssetResourceId(ComponentId, ComponentId),
1784}
1785
1786type RegisterAction = dyn Fn(&Components, &mut TweenResolver) + Send + Sync + 'static;
1787
1788/// Enumeration of the types of animation targets.
1789///
1790/// This type holds the minimum amount of data to reference ananimation target,
1791/// aside from the actual type of the target.
1792#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
1793pub enum AnimTargetKind {
1794    /// Component animation target.
1795    Component {
1796        /// The entity owning the component instance.
1797        entity: Entity,
1798    },
1799    /// Resource animation target.
1800    Resource,
1801    /// Asset animation target.
1802    Asset {
1803        /// The asset ID inside the [`Assets`] collection.
1804        asset_id: UntypedAssetId,
1805        /// Type ID of the [`Assets`] collection itself.
1806        assets_type_id: TypeId,
1807    },
1808}
1809
1810/// Component defining the target of an animation.
1811///
1812/// References an object used as the target of the animation stored in the
1813/// [`TweenAnim`] component on the same entity.
1814#[derive(Component)]
1815pub struct AnimTarget {
1816    /// Target kind and additional data to identify it.
1817    pub kind: AnimTargetKind,
1818
1819    /// Self-registering action for assets and resources.
1820    pub(crate) register_action: Option<Box<RegisterAction>>,
1821}
1822
1823impl AnimTarget {
1824    /// Create a target mutating a component on the given entity.
1825    pub fn component<C: Component<Mutability = Mutable>>(entity: Entity) -> Self {
1826        Self {
1827            kind: AnimTargetKind::Component { entity },
1828            // Components have a complete typeless API, don't need any extra registration for type
1829            // erasure.
1830            register_action: None,
1831        }
1832    }
1833
1834    /// Create a target mutating the given resource.
1835    pub fn resource<R: Resource>() -> Self {
1836        let register_action = |components: &Components, resolver: &mut TweenResolver| {
1837            resolver.register_resource_resolver_for::<R>(components);
1838        };
1839        Self {
1840            kind: AnimTargetKind::Resource,
1841            register_action: Some(Box::new(register_action)),
1842        }
1843    }
1844
1845    /// Create a target mutating the given asset.
1846    ///
1847    /// The asset is identified by its type, and its [`AssetId`].
1848    pub fn asset<A: Asset>(asset_id: impl Into<AssetId<A>>) -> Self {
1849        let register_action = |components: &Components, resolver: &mut TweenResolver| {
1850            resolver.register_asset_resolver_for::<A>(components);
1851        };
1852        Self {
1853            kind: AnimTargetKind::Asset {
1854                asset_id: asset_id.into().untyped(),
1855                assets_type_id: TypeId::of::<Assets<A>>(),
1856            },
1857            register_action: Some(Box::new(register_action)),
1858        }
1859    }
1860
1861    /// Register any resolver for this target.
1862    pub(crate) fn register(&self, components: &Components, resolver: &mut TweenResolver) {
1863        if let Some(register_action) = self.register_action.as_ref() {
1864            register_action(components, resolver);
1865        }
1866    }
1867}
1868
1869/// Animation controller instance.
1870///
1871/// The [`TweenAnim`] represents a single animation instance for a single
1872/// target (component or resource or asset). Each instance is independent, even
1873/// if it mutates the same target as another instance. Spawning this component
1874/// adds an active animation, and destroying it stops that animation. The
1875/// component can also be used to control the animation playback at runtime,
1876/// like the playback speed.
1877///
1878/// The target is described by the [`AnimTarget`] component. If that component
1879/// is absent, then the animation implicitly targets a component on the current
1880/// Entity. The type of the component is derived from the type that the [`Lens`]
1881/// animates.
1882///
1883/// _If you're looking for the basic tweenable animation description, see
1884/// [`Tween`] instead._
1885///
1886/// # Example
1887///
1888/// ```
1889/// # use bevy::prelude::*;
1890/// # use bevy_tweening::*;
1891/// # fn make_tweenable<T>() -> Tween { unimplemented!() }
1892/// fn my_system(mut commands: Commands) {
1893///     let tweenable = make_tweenable::<Transform>();
1894///     let id1 = commands
1895///         .spawn((
1896///             Transform::default(),
1897///             // Implicitly targets the current entity's Transform
1898///             TweenAnim::new(tweenable),
1899///         ))
1900///         .id();
1901///
1902///     let tweenable2 = make_tweenable::<Transform>();
1903///     commands.spawn((
1904///         TweenAnim::new(tweenable2),
1905///         // Explicitly targets the Transform component of entity 'id1'
1906///         AnimTarget::component::<Transform>(id1),
1907///     ));
1908/// }
1909/// ```
1910#[derive(Component)]
1911pub struct TweenAnim {
1912    /// The animation itself. Note that the tweenable is stateful, so can't be
1913    /// shared with another [`TweenAnim`] instance.
1914    tweenable: BoxedTweenable,
1915    /// Control if the animation is played or not. Defaults to
1916    /// [`PlaybackState::Playing`].
1917    ///
1918    /// Pausing an animation with [`PlaybackState::Paused`] is functionaly
1919    /// equivalent to setting its [`speed`] to zero. The two fields remain
1920    /// independent though, for convenience.
1921    ///
1922    /// [`speed`]: Self::speed
1923    pub playback_state: PlaybackState,
1924    /// Relative playback speed. Defaults to `1.` (normal speed; 100%).
1925    ///
1926    /// Setting a negative or zero speed value effectively pauses the animation
1927    /// (although the [`playback_state`] remains unchanged). Negative values may
1928    /// be clamped to 0. when the animation is stepped, but positive or zero
1929    /// values are never modified by the library.
1930    ///
1931    /// # Time precision
1932    ///
1933    /// _This note is an implementation detail which can usually be ignored._
1934    ///
1935    /// Despite the use of `f64`, setting a playback speed different from `1.`
1936    /// (100% speed) may produce small inaccuracies in durations, especially
1937    /// for longer animations. However those are often negligible.
1938    /// This is due to the very large precision of `Duration` (typically 96
1939    /// bits or more), even compared to `f64` (64 bits), and the fact this speed
1940    /// factor is a multiplier whereas most other time quantities are added or
1941    /// subtracted.
1942    ///
1943    /// [`playback_state`]: Self::playback_state
1944    pub speed: f64,
1945    /// Destroy the animation once completed. This defaults to `true`, and makes
1946    /// the stepping functions like [`TweenAnim::step_all()`] destroy this
1947    /// animation once it completed. To keep the animation queued, and allow
1948    /// access after it completed, set this to `false`. Note however that
1949    /// you should avoid leaving all animations queued if they're unused, as
1950    /// this wastes memory and may degrade performances if too many
1951    /// completed animations are kept around for no good reason.
1952    pub destroy_on_completion: bool,
1953    /// Current tweening completion state.
1954    tween_state: TweenState,
1955}
1956
1957impl TweenAnim {
1958    /// Create a new tween animation.
1959    ///
1960    /// This component represents the runtime animation being played to mutate a
1961    /// specific target.
1962    ///
1963    /// # Panics
1964    ///
1965    /// Panics if the tweenable is "typeless", that is
1966    /// [`Tweenable::target_type_id()`] returns `None`. Animations must
1967    /// target a concrete component or asset type. This means in particular
1968    /// that you can't use a single [`Delay`] alone. You can however use a
1969    /// [`Delay`] or other typeless tweenables as part of a [`Sequence`],
1970    /// provided there's at least one other typed tweenable in the sequence
1971    /// to make it typed too.
1972    #[inline]
1973    pub fn new(tweenable: impl IntoBoxedTweenable) -> Self {
1974        let tweenable = tweenable.into_boxed();
1975        assert!(
1976            tweenable.target_type_id().is_some(),
1977            "The top-level Tweenable of a TweenAnim must be typed (Tweenable::target_type_id() returns Some)."
1978        );
1979        Self {
1980            tweenable,
1981            playback_state: PlaybackState::Playing,
1982            speed: 1.,
1983            destroy_on_completion: true,
1984            tween_state: TweenState::Active,
1985        }
1986    }
1987
1988    /// Configure the playback speed.
1989    pub fn with_speed(mut self, speed: f64) -> Self {
1990        self.speed = speed;
1991        self
1992    }
1993
1994    /// Enable or disable destroying this component on animation completion.
1995    ///
1996    /// If enabled, the component is automatically removed from its `Entity`
1997    /// when the animation completed.
1998    pub fn with_destroy_on_completed(mut self, destroy_on_completed: bool) -> Self {
1999        self.destroy_on_completion = destroy_on_completed;
2000        self
2001    }
2002
2003    /// Step a single animation.
2004    ///
2005    /// _The [`step_all()`] function is called automatically by the animation
2006    /// system registered by the [`TweeningPlugin`], you generally don't
2007    /// need to call this one._
2008    ///
2009    /// This is a shortcut for `step_many(world, delta_time, [entity])`, with
2010    /// the added benefit that it returns some error if the entity is not valid.
2011    /// See [`step_many()`] for details.
2012    ///
2013    /// # Example
2014    ///
2015    /// ```
2016    /// # use std::time::Duration;
2017    /// # use bevy::prelude::*;
2018    /// # use bevy_tweening::*;
2019    /// # fn make_tweenable() -> Tween { unimplemented!() }
2020    /// #[derive(Component)]
2021    /// struct MyMarker;
2022    ///
2023    /// fn my_system(world: &mut World) -> Result<()> {
2024    ///     let mut q_anims = world.query_filtered::<Entity, (With<TweenAnim>, With<MyMarker>)>();
2025    ///     let entity = q_anims.single(world)?;
2026    ///     let delta_time = Duration::from_millis(200);
2027    ///     TweenAnim::step_one(world, delta_time, entity);
2028    ///     Ok(())
2029    /// }
2030    /// ```
2031    ///
2032    /// # Returns
2033    ///
2034    /// This returns an error if the entity is not found or doesn't own a
2035    /// [`TweenAnim`] component.
2036    ///
2037    /// [`step_all()`]: Self::step_all
2038    /// [`step_many()`]: Self::step_many
2039    #[inline]
2040    pub fn step_one(
2041        world: &mut World,
2042        delta_time: Duration,
2043        entity: Entity,
2044    ) -> Result<(), TweeningError> {
2045        let num = Self::step_many(world, delta_time, &[entity]);
2046        if num > 0 {
2047            Ok(())
2048        } else {
2049            Err(TweeningError::EntityNotFound(entity))
2050        }
2051    }
2052
2053    /// Step some animation(s).
2054    ///
2055    /// _The [`step_all()`] function is called automatically by the animation
2056    /// system registered by the [`TweeningPlugin`], you generally don't
2057    /// need to call this one._
2058    ///
2059    /// Step the given animation(s) by a given `delta_time`, which may be
2060    /// [`Duration::ZERO`]. Passing a zero delta time may be useful to force the
2061    /// current animation state to be applied to a target, in case you made
2062    /// change which do not automatically do so (for example, retargeting an
2063    /// animation). The `anims` are the entities which own a [`TweenAnim`]
2064    /// component to step; any entity without a [`TweenAnim`] component is
2065    /// silently ignored.
2066    ///
2067    /// The function doesn't check that all input entities are unique. If an
2068    /// entity is duplicated in `anims`, the behavior is undefined, including
2069    /// (but not guaranteed) stepping the animation multiple times. You're
2070    /// responsible for ensuring the input entity slice contains distinct
2071    /// entities.
2072    ///
2073    /// # Example
2074    ///
2075    /// ```
2076    /// # use std::time::Duration;
2077    /// # use bevy::prelude::*;
2078    /// # use bevy_tweening::*;
2079    /// # fn make_tweenable() -> Tween { unimplemented!() }
2080    /// #[derive(Component)]
2081    /// struct MyMarker;
2082    ///
2083    /// fn my_system(world: &mut World) -> Result<()> {
2084    ///     let mut q_anims = world.query_filtered::<Entity, (With<TweenAnim>, With<MyMarker>)>();
2085    ///     let entities = q_anims.iter(world).collect::<Vec<Entity>>();
2086    ///     let delta_time = Duration::from_millis(200);
2087    ///     TweenAnim::step_many(world, delta_time, &entities[..]);
2088    ///     Ok(())
2089    /// }
2090    /// ```
2091    ///
2092    /// # Returns
2093    ///
2094    /// Returns the number of [`TweenAnim`] component found and stepped, which
2095    /// is always less than or equal to the input `anims` slice length.
2096    ///
2097    /// [`step_all()`]: Self::step_all
2098    pub fn step_many(world: &mut World, delta_time: Duration, anims: &[Entity]) -> usize {
2099        let mut targets = vec![];
2100        world.resource_scope(|world, mut resolver: Mut<TweenResolver>| {
2101            let mut q_anims = world.query::<(Entity, &TweenAnim, Option<&AnimTarget>)>();
2102            targets.reserve(anims.len());
2103            for entity in anims {
2104                if let Ok((entity, anim, maybe_target)) = q_anims.get(world, *entity) {
2105                    // Lazy registration with resolver if needed
2106                    if let Some(anim_target) = maybe_target {
2107                        anim_target.register(world.components(), &mut resolver);
2108                    }
2109
2110                    // Actually step the tweenable and update the target
2111                    if let Ok((target_type_id, component_id, target, is_retargetable)) =
2112                        Self::resolve_target(
2113                            world.components(),
2114                            maybe_target,
2115                            entity,
2116                            anim.tweenable(),
2117                        )
2118                    {
2119                        targets.push((
2120                            entity,
2121                            target_type_id,
2122                            component_id,
2123                            target,
2124                            is_retargetable,
2125                        ));
2126                    }
2127                }
2128            }
2129        });
2130        Self::step_impl(world, delta_time, &targets[..]);
2131        targets.len()
2132    }
2133
2134    /// Step all animations on the given world.
2135    ///
2136    /// _This function is called automatically by the animation system
2137    /// registered by the [`TweeningPlugin`], you generally don't need to call
2138    /// it._
2139    ///
2140    /// Step all the [`TweenAnim`] components of the input world by a given
2141    /// `delta_time`, which may be [`Duration::ZERO`]. Passing a zero delta
2142    /// time may be useful to force the current animation state to be
2143    /// applied to a target, in case you made change which do not
2144    /// automatically do so (for example, retargeting an animation).
2145    pub fn step_all(world: &mut World, delta_time: Duration) {
2146        let targets = world.resource_scope(|world, mut resolver: Mut<TweenResolver>| {
2147            let mut q_anims = world.query::<(Entity, &TweenAnim, Option<&AnimTarget>)>();
2148            q_anims
2149                .iter(world)
2150                .filter_map(|(entity, anim, maybe_target)| {
2151                    // Lazy registration with resolver if needed
2152                    if let Some(anim_target) = maybe_target {
2153                        anim_target.register(world.components(), &mut resolver);
2154                    }
2155
2156                    // Actually step the tweenable and update the target
2157                    match Self::resolve_target(
2158                        world.components(),
2159                        maybe_target,
2160                        entity,
2161                        anim.tweenable(),
2162                    ) {
2163                        Ok((target_type_id, component_id, target, is_retargetable)) => Some((
2164                            entity,
2165                            target_type_id,
2166                            component_id,
2167                            target,
2168                            is_retargetable,
2169                        )),
2170                        Err(err) => {
2171                            bevy::log::error!(
2172                                "Error while stepping TweenAnim on entity {:?}: {:?}",
2173                                entity,
2174                                err
2175                            );
2176                            None
2177                        }
2178                    }
2179                })
2180                .collect::<Vec<_>>()
2181        });
2182        Self::step_impl(world, delta_time, &targets[..]);
2183    }
2184
2185    fn resolve_target(
2186        components: &Components,
2187        maybe_target: Option<&AnimTarget>,
2188        anim_entity: Entity,
2189        tweenable: &dyn Tweenable,
2190    ) -> Result<(TypeId, ComponentId, AnimTargetKind, bool), TweeningError> {
2191        let type_id = tweenable
2192            .target_type_id()
2193            .ok_or(TweeningError::UntypedTweenable)?;
2194        if let Some(target) = maybe_target {
2195            // Target explicitly specified with AnimTarget component
2196            let component_id = match &target.kind {
2197                AnimTargetKind::Component { .. } => components
2198                    .get_id(type_id)
2199                    .ok_or(TweeningError::ComponentNotRegistered(type_id))?,
2200                AnimTargetKind::Resource => components
2201                    .get_resource_id(type_id)
2202                    .ok_or(TweeningError::ResourceNotRegistered(type_id))?,
2203                AnimTargetKind::Asset { assets_type_id, .. } => components
2204                    .get_resource_id(*assets_type_id)
2205                    .ok_or(TweeningError::AssetNotRegistered(type_id))?,
2206            };
2207            let is_retargetable = false; // explicit target
2208            Ok((type_id, component_id, target.kind, is_retargetable))
2209        } else {
2210            // Target implicitly self; this can only be a component target
2211            let is_retargetable = true;
2212            if let Some(component_id) = components.get_id(type_id) {
2213                Ok((
2214                    type_id,
2215                    component_id,
2216                    AnimTargetKind::Component {
2217                        entity: anim_entity,
2218                    },
2219                    is_retargetable,
2220                ))
2221            } else {
2222                // We can't implicitly target an asset without its AssetId
2223                Err(TweeningError::ComponentNotRegistered(type_id))
2224            }
2225        }
2226    }
2227
2228    fn step_impl(
2229        world: &mut World,
2230        delta_time: Duration,
2231        anims: &[(Entity, TypeId, ComponentId, AnimTargetKind, bool)],
2232    ) {
2233        let mut to_remove = Vec::with_capacity(anims.len());
2234        world.resource_scope(|world, resolver: Mut<TweenResolver>| {
2235            world.resource_scope(
2236                |world, mut cycle_events: Mut<Messages<CycleCompletedEvent>>| {
2237                    world.resource_scope(
2238                        |world, mut anim_events: Mut<Messages<AnimCompletedEvent>>| {
2239                            let anim_comp_id = world.component_id::<TweenAnim>().unwrap();
2240                            for (
2241                                anim_entity,
2242                                target_type_id,
2243                                component_id,
2244                                anim_target,
2245                                is_retargetable,
2246                            ) in anims
2247                            {
2248                                let retain = match anim_target {
2249                                    AnimTargetKind::Component {
2250                                        entity: comp_entity,
2251                                    } => {
2252                                        let (mut entities, commands) =
2253                                            world.entities_and_commands();
2254                                        let ret = if *anim_entity == *comp_entity {
2255                                            // The TweenAnim animates another component on the same
2256                                            // entity
2257                                            let Ok([mut ent]) = entities.get_mut([*anim_entity])
2258                                            else {
2259                                                continue;
2260                                            };
2261                                            let Ok([anim, target]) =
2262                                                ent.get_mut_by_id([anim_comp_id, *component_id])
2263                                            else {
2264                                                continue;
2265                                            };
2266                                            // SAFETY: We fetched the EntityMut from the component
2267                                            // ID of
2268                                            // TweenAnim
2269                                            #[allow(unsafe_code)]
2270                                            let mut anim = unsafe { anim.with_type::<TweenAnim>() };
2271                                            anim.step_self(
2272                                                commands,
2273                                                *anim_entity,
2274                                                delta_time,
2275                                                anim_target,
2276                                                target,
2277                                                target_type_id,
2278                                                cycle_events.reborrow(),
2279                                                anim_events.reborrow(),
2280                                            )
2281                                        } else {
2282                                            // The TweenAnim animates a component on a different
2283                                            // entity
2284                                            let Ok([mut anim, mut target]) =
2285                                                entities.get_mut([*anim_entity, *comp_entity])
2286                                            else {
2287                                                continue;
2288                                            };
2289                                            let Some(mut anim) = anim.get_mut::<TweenAnim>() else {
2290                                                continue;
2291                                            };
2292                                            let Ok(target) = target.get_mut_by_id(*component_id)
2293                                            else {
2294                                                continue;
2295                                            };
2296                                            anim.step_self(
2297                                                commands,
2298                                                *anim_entity,
2299                                                delta_time,
2300                                                anim_target,
2301                                                target,
2302                                                target_type_id,
2303                                                cycle_events.reborrow(),
2304                                                anim_events.reborrow(),
2305                                            )
2306                                        };
2307                                        match ret {
2308                                            Ok(res) => {
2309                                                if res.needs_retarget {
2310                                                    assert!(res.retain);
2311                                                    if *is_retargetable {
2312                                                        //to_retarget.push(anim_entity);
2313                                                        //true
2314                                                        bevy::log::warn!("TODO: Multi-target tweenable sequence is not yet supported. Ensure the animation of the TweenAnim component on entity {:?} targets a single component type.", *anim_entity);
2315                                                        false
2316                                                    } else {
2317                                                        bevy::log::warn!("Multi-target tweenable sequence cannot be used with an explicit single target. Remove the AnimTarget component from entity {:?}, or ensure all tweenables in the sequence target the same component.", *anim_entity);
2318                                                        false
2319                                                    }
2320                                                } else {
2321                                                    res.retain
2322                                                }
2323                                            }
2324                                            Err(_) => false,
2325                                        }
2326                                    }
2327                                    AnimTargetKind::Resource => resolver
2328                                        .resolve_resource(
2329                                            world,
2330                                            target_type_id,
2331                                            *component_id,
2332                                            *anim_entity,
2333                                            delta_time,
2334                                            cycle_events.reborrow(),
2335                                            anim_events.reborrow(),
2336                                        )
2337                                        .unwrap_or_else(|err| {
2338                                            bevy::log::error!(
2339                                                "Deleting resource animation due to error: {err:?}"
2340                                            );
2341                                            false
2342                                        }),
2343                                    AnimTargetKind::Asset { asset_id, .. } => resolver
2344                                        .resolve_asset(
2345                                            world,
2346                                            target_type_id,
2347                                            *component_id,
2348                                            *asset_id,
2349                                            *anim_entity,
2350                                            delta_time,
2351                                            cycle_events.reborrow(),
2352                                            anim_events.reborrow(),
2353                                        )
2354                                        .unwrap_or_else(|err| {
2355                                            bevy::log::error!(
2356                                                "Deleting asset animation due to error: {err:?}"
2357                                            );
2358                                            false
2359                                        }),
2360                                };
2361
2362                                if !retain {
2363                                    to_remove.push(*anim_entity);
2364                                }
2365                            }
2366                        },
2367                    );
2368                },
2369            );
2370        });
2371
2372        for entity in to_remove.drain(..) {
2373            world.entity_mut(entity).remove::<TweenAnim>();
2374        }
2375
2376        world.flush();
2377    }
2378
2379    #[allow(clippy::too_many_arguments)]
2380    fn step_self(
2381        &mut self,
2382        mut commands: Commands,
2383        anim_entity: Entity,
2384        delta_time: Duration,
2385        target_kind: &AnimTargetKind,
2386        mut mut_untyped: MutUntyped,
2387        target_type_id: &TypeId,
2388        mut cycle_events: Mut<Messages<CycleCompletedEvent>>,
2389        mut anim_events: Mut<Messages<AnimCompletedEvent>>,
2390    ) -> Result<StepResult, TweeningError> {
2391        let mut completed_events = Vec::with_capacity(8);
2392
2393        // Sanity checks on fields which can be freely modified by the user
2394        self.speed = self.speed.max(0.);
2395
2396        // Retain completed animations only if requested
2397        if self.tween_state == TweenState::Completed {
2398            let ret = StepResult {
2399                retain: !self.destroy_on_completion,
2400                needs_retarget: false,
2401            };
2402            return Ok(ret);
2403        }
2404
2405        // Skip paused animations (but retain them)
2406        if self.playback_state == PlaybackState::Paused || self.speed <= 0. {
2407            let ret = StepResult {
2408                retain: true,
2409                needs_retarget: false,
2410            };
2411            return Ok(ret);
2412        }
2413
2414        // Scale delta time by this animation's speed. Reject negative speeds; use
2415        // backward playback to play in reverse direction.
2416        // Note: must use f64 for precision; f32 produces visible roundings.
2417        let delta_time = delta_time.mul_f64(self.speed);
2418
2419        // Step the tweenable animation
2420        let mut notify_completed = || {
2421            completed_events.push(CycleCompletedEvent {
2422                anim_entity,
2423                target: *target_kind,
2424            });
2425        };
2426        let (state, needs_retarget) = self.tweenable.step(
2427            anim_entity,
2428            delta_time,
2429            mut_untyped.reborrow(),
2430            target_type_id,
2431            &mut notify_completed,
2432        );
2433        self.tween_state = state;
2434
2435        // Send tween completed events once we reclaimed mut access to world and can get
2436        // a Commands.
2437        if !completed_events.is_empty() {
2438            for event in completed_events.drain(..) {
2439                // Send buffered event
2440                cycle_events.write(event);
2441
2442                // Trigger all entity-scoped observers
2443                commands.trigger(CycleCompletedEvent {
2444                    anim_entity,
2445                    ..event
2446                });
2447            }
2448        }
2449
2450        // Raise animation completed event
2451        if state == TweenState::Completed {
2452            let event: AnimCompletedEvent = AnimCompletedEvent {
2453                anim_entity,
2454                target: *target_kind,
2455            };
2456
2457            // Send buffered event
2458            anim_events.write(event);
2459
2460            // Trigger all entity-scoped observers
2461            commands.trigger(event);
2462        }
2463
2464        let ret = StepResult {
2465            retain: state == TweenState::Active || !self.destroy_on_completion,
2466            needs_retarget,
2467        };
2468        Ok(ret)
2469    }
2470
2471    /// Stop animation playback and rewind the animation.
2472    ///
2473    /// This changes the animator state to [`PlaybackState::Paused`] and rewinds
2474    /// its tweenable.
2475    ///
2476    /// # Panics
2477    ///
2478    /// Like [`Tweenable::rewind()`], this panics if the current playback
2479    /// direction is [`PlaybackDirection::Backward`] and the animation is
2480    /// infinitely repeating.
2481    pub fn stop(&mut self) {
2482        self.playback_state = PlaybackState::Paused;
2483        self.tweenable.rewind();
2484        self.tween_state = TweenState::Active;
2485    }
2486
2487    /// Get the tweenable describing this animation.
2488    ///
2489    /// To change the tweenable, use [`TweenAnim::set_tweenable()`].
2490    #[inline]
2491    pub fn tweenable(&self) -> &dyn Tweenable {
2492        self.tweenable.as_ref()
2493    }
2494
2495    /// Set a new animation description.
2496    ///
2497    /// Attempt to change the tweenable of an animation already spawned.
2498    ///
2499    /// If the tweenable is successfully swapped, this resets the
2500    /// [`tween_state()`] to [`TweenState::Active`], even if the tweenable would
2501    /// otherwise be completed _e.g._ because its current elapsed time is past
2502    /// its total duration. Conversely, this doesn't update the target
2503    /// component or asset, as this function doesn't have mutable access to
2504    /// it. To force applying the new state to the target without stepping the
2505    /// animation forward or backward, call one of the stepping functions like
2506    /// [`TweenAnim::step_one()`] passing a delta time of [`Duration::ZERO`].
2507    ///
2508    /// To ensure the old and new animations have the same elapsed time (for
2509    /// example if they need to be synchronized, if they're variants of each
2510    /// other), call [`set_elapsed()`] first on the input `tweenable`, with
2511    /// the duration value of the old tweenable returned by [`elapsed()`].
2512    ///
2513    /// ```
2514    /// # use std::time::Duration;
2515    /// # use bevy::prelude::*;
2516    /// # use bevy_tweening::*;
2517    /// # fn make_tweenable() -> Tween { unimplemented!() }
2518    /// fn my_system(mut anim: Single<&mut TweenAnim>) {
2519    ///     let mut tweenable = make_tweenable();
2520    ///     let elapsed = anim.tweenable().elapsed();
2521    ///     tweenable.set_elapsed(elapsed);
2522    ///     anim.set_tweenable(tweenable);
2523    /// }
2524    /// ```
2525    ///
2526    /// # Returns
2527    ///
2528    /// On success, returns the previous tweenable which has been swapped out.
2529    ///
2530    /// [`tween_state()`]: Self::tween_state
2531    /// [`set_elapsed()`]: crate::Tweenable::set_elapsed
2532    /// [`elapsed()`]: crate::Tweenable::elapsed
2533    /// [`step_one()`]: Self::step_one
2534    pub fn set_tweenable<T>(&mut self, tweenable: T) -> Result<BoxedTweenable, TweeningError>
2535    where
2536        T: Tweenable + 'static,
2537    {
2538        let mut old_tweenable: BoxedTweenable = Box::new(tweenable);
2539        std::mem::swap(&mut self.tweenable, &mut old_tweenable);
2540        // Reset tweening state, the new tweenable is at t=0
2541        self.tween_state = TweenState::Active;
2542        Ok(old_tweenable)
2543    }
2544
2545    /// Get the tweening completion state.
2546    ///
2547    /// In general this is [`TweenState::Active`], unless the animation
2548    /// completed and [`destroy_on_completion`] is `false`.
2549    ///
2550    /// [`destroy_on_completion`]: Self::destroy_on_completion
2551    #[inline]
2552    pub fn tween_state(&self) -> TweenState {
2553        self.tween_state
2554    }
2555}
2556
2557type ResourceResolver = Box<
2558    dyn for<'w> Fn(
2559            &mut World,
2560            Entity,
2561            &TypeId,
2562            Duration,
2563            Mut<Messages<CycleCompletedEvent>>,
2564            Mut<Messages<AnimCompletedEvent>>,
2565        ) -> Result<bool, TweeningError>
2566        + Send
2567        + Sync
2568        + 'static,
2569>;
2570
2571type AssetResolver = Box<
2572    dyn for<'w> Fn(
2573            &mut World,
2574            UntypedAssetId,
2575            Entity,
2576            &TypeId,
2577            Duration,
2578            Mut<Messages<CycleCompletedEvent>>,
2579            Mut<Messages<AnimCompletedEvent>>,
2580        ) -> Result<bool, TweeningError>
2581        + Send
2582        + Sync
2583        + 'static,
2584>;
2585
2586/// Resolver for resources and assets.
2587///
2588/// _This resource is largely an implementation detail. You can safely ignore
2589/// it._
2590///
2591/// Bevy doesn't provide a suitable untyped API to access resources and assets
2592/// at runtime without knowing their compile-time type.
2593/// - For resources, most of the API is in place, but unfortunately there's no
2594///   `World::resource_scope_untyped()` to temporarily extract a resource by ID
2595///   to allow concurrent mutability of the resource with other parts of the
2596///   [`World`], in particular the animation target.
2597/// - For assets, there's simply no untyped API. [`Assets`] doesn't allow
2598///   untyped asset access.
2599///
2600/// To work around those limitations, this resolver resource contains
2601/// type-erased closures allowing to resolve an animation target definition into
2602/// a mutable pointer [`MutUntyped`] to that instance, to allow the animation
2603/// engine to apply the animation on it.
2604#[derive(Default, Resource)]
2605pub struct TweenResolver {
2606    /// Resource resolver allowing to call `World::resource_scope()` to extract
2607    /// that resource type form the `World` while in parallel accessing mutably
2608    /// the animation entity itself.
2609    resource_resolver: HashMap<ComponentId, ResourceResolver>,
2610    /// Asset resolver allowing to convert a pair of { untyped pointer to
2611    /// `Assets<A>`, untyped `AssetId` } into an untyped pointer to the asset A
2612    /// itself. This is necessary because there's no UntypedAssets interface in
2613    /// Bevy. The TypeId key must be the type of the `Assets<A>` type itself.
2614    /// The resolver is allowed to fail (return `None`), for example when the
2615    /// asset ID doesn't reference a valid asset.
2616    asset_resolver: HashMap<ComponentId, AssetResolver>,
2617}
2618
2619impl TweenResolver {
2620    /// Register a resolver for the given resource type.
2621    pub(crate) fn register_resource_resolver_for<R: Resource>(&mut self, components: &Components) {
2622        let resource_id = components.resource_id::<R>().unwrap();
2623        let resolver = |world: &mut World,
2624                        entity: Entity,
2625                        target_type_id: &TypeId,
2626                        delta_time: Duration,
2627                        mut cycle_events: Mut<Messages<CycleCompletedEvent>>,
2628                        mut anim_events: Mut<Messages<AnimCompletedEvent>>|
2629         -> Result<bool, TweeningError> {
2630            // First, remove the resource R from the world so we can access it mutably in
2631            // parallel of the TweenAnim
2632            world.resource_scope(|world, resource: Mut<R>| {
2633                let target = AnimTargetKind::Resource;
2634
2635                let (mut entities, commands) = world.entities_and_commands();
2636
2637                // Resolve the TweenAnim component
2638                let Ok([mut ent]) = entities.get_mut([entity]) else {
2639                    return Err(TweeningError::EntityNotFound(entity));
2640                };
2641                let Some(mut anim) = ent.get_mut::<TweenAnim>() else {
2642                    return Err(TweeningError::MissingTweenAnim(ent.id()));
2643                };
2644
2645                // Finally, step the TweenAnim and mutate the target
2646                let ret = anim.step_self(
2647                    commands,
2648                    entity,
2649                    delta_time,
2650                    &target,
2651                    resource.into(),
2652                    target_type_id,
2653                    cycle_events.reborrow(),
2654                    anim_events.reborrow(),
2655                );
2656                ret.map(|result| {
2657                    assert!(!result.needs_retarget, "Cannot use a multi-target sequence of tweenable animations with a resource target.");
2658                    result.retain
2659                })
2660            })
2661        };
2662        self.resource_resolver
2663            .entry(resource_id)
2664            .or_insert(Box::new(resolver));
2665    }
2666
2667    /// Register a resolver for the given asset type.
2668    pub(crate) fn register_asset_resolver_for<A: Asset>(&mut self, components: &Components) {
2669        let resource_id = components.resource_id::<Assets<A>>().unwrap();
2670        let resolver = |world: &mut World,
2671                        asset_id: UntypedAssetId,
2672                        entity: Entity,
2673                        target_type_id: &TypeId,
2674                        delta_time: Duration,
2675                        mut cycle_events: Mut<Messages<CycleCompletedEvent>>,
2676                        mut anim_events: Mut<Messages<AnimCompletedEvent>>|
2677         -> Result<bool, TweeningError> {
2678            let asset_id = asset_id.typed::<A>();
2679            // First, remove the Assets<A> from the world so we can access it mutably in
2680            // parallel of the TweenAnim
2681            world.resource_scope(|world, assets: Mut<Assets<A>>| {
2682                // Next, fetch the asset A itself from its Assets<A> based on its asset ID
2683                let Some(asset) = assets.filter_map_unchanged(|assets| assets.get_mut(asset_id))
2684                else {
2685                    return Err(TweeningError::InvalidAssetId(asset_id.into()));
2686                };
2687
2688                let target = AnimTargetKind::Asset {
2689                    asset_id: asset_id.untyped(),
2690                    assets_type_id: TypeId::of::<Assets<A>>(),
2691                };
2692
2693                let (mut entities, commands) = world.entities_and_commands();
2694
2695                // Resolve the TweenAnim component
2696                let Ok([mut ent]) = entities.get_mut([entity]) else {
2697                    return Err(TweeningError::EntityNotFound(entity));
2698                };
2699                let Some(mut anim) = ent.get_mut::<TweenAnim>() else {
2700                    return Err(TweeningError::MissingTweenAnim(ent.id()));
2701                };
2702
2703                // Finally, step the TweenAnim and mutate the target
2704                let ret = anim.step_self(
2705                    commands,
2706                    entity,
2707                    delta_time,
2708                    &target,
2709                    asset.into(),
2710                    target_type_id,
2711                    cycle_events.reborrow(),
2712                    anim_events.reborrow(),
2713                );
2714                ret.map(|result| {
2715                    assert!(!result.needs_retarget, "Cannot use a multi-target sequence of tweenable animations with an asset target.");
2716                    result.retain
2717                })
2718            })
2719        };
2720        self.asset_resolver
2721            .entry(resource_id)
2722            .or_insert(Box::new(resolver));
2723    }
2724
2725    #[allow(clippy::too_many_arguments)]
2726    #[inline]
2727    pub(crate) fn resolve_resource(
2728        &self,
2729        world: &mut World,
2730        target_type_id: &TypeId,
2731        resource_id: ComponentId,
2732        entity: Entity,
2733        delta_time: Duration,
2734        cycle_events: Mut<Messages<CycleCompletedEvent>>,
2735        anim_events: Mut<Messages<AnimCompletedEvent>>,
2736    ) -> Result<bool, TweeningError> {
2737        let Some(resolver) = self.resource_resolver.get(&resource_id) else {
2738            println!("ERROR: resource not registered {:?}", resource_id);
2739            return Err(TweeningError::AssetResolverNotRegistered(resource_id));
2740        };
2741        resolver(
2742            world,
2743            entity,
2744            target_type_id,
2745            delta_time,
2746            cycle_events,
2747            anim_events,
2748        )
2749    }
2750
2751    #[allow(clippy::too_many_arguments)]
2752    #[inline]
2753    pub(crate) fn resolve_asset(
2754        &self,
2755        world: &mut World,
2756        target_type_id: &TypeId,
2757        resource_id: ComponentId,
2758        untyped_asset_id: UntypedAssetId,
2759        entity: Entity,
2760        delta_time: Duration,
2761        cycle_events: Mut<Messages<CycleCompletedEvent>>,
2762        anim_events: Mut<Messages<AnimCompletedEvent>>,
2763    ) -> Result<bool, TweeningError> {
2764        let Some(resolver) = self.asset_resolver.get(&resource_id) else {
2765            println!("ERROR: asset not registered {:?}", resource_id);
2766            return Err(TweeningError::AssetResolverNotRegistered(resource_id));
2767        };
2768        resolver(
2769            world,
2770            untyped_asset_id,
2771            entity,
2772            target_type_id,
2773            delta_time,
2774            cycle_events,
2775            anim_events,
2776        )
2777    }
2778}
2779
2780pub(crate) struct StepResult {
2781    /// Whether to retain the current [`TweenAnim`]? If `false`, the
2782    /// [`TweenAnim`] is destroyed unless [`TweenAnim::destroy_on_completion`]
2783    /// is `false`.
2784    pub retain: bool,
2785    /// Whether to recompute the new animation target and step again. This is
2786    /// used by sequences when the animation target changes type in a sequence.
2787    pub needs_retarget: bool,
2788}
2789
2790#[cfg(test)]
2791mod tests {
2792    use std::{
2793        f32::consts::{FRAC_PI_2, TAU},
2794        marker::PhantomData,
2795    };
2796
2797    use bevy::ecs::{change_detection::MaybeLocation, component::Tick};
2798
2799    use super::*;
2800    use crate::test_utils::*;
2801
2802    struct DummyLens {
2803        start: f32,
2804        end: f32,
2805    }
2806
2807    struct DummyLens2 {
2808        start: i32,
2809        end: i32,
2810    }
2811
2812    #[derive(Debug, Default, Clone, Copy, Component)]
2813    struct DummyComponent {
2814        value: f32,
2815    }
2816
2817    #[derive(Debug, Default, Clone, Copy, Component)]
2818    struct DummyComponent2 {
2819        value: i32,
2820    }
2821
2822    #[derive(Debug, Default, Clone, Copy, Resource)]
2823    struct DummyResource {
2824        value: f32,
2825    }
2826
2827    #[derive(Asset, Debug, Default, Reflect)]
2828    struct DummyAsset {
2829        value: f32,
2830    }
2831
2832    impl Lens<DummyComponent> for DummyLens {
2833        fn lerp(&mut self, mut target: Mut<DummyComponent>, ratio: f32) {
2834            target.value = self.start.lerp(self.end, ratio);
2835        }
2836    }
2837
2838    impl Lens<DummyComponent2> for DummyLens2 {
2839        fn lerp(&mut self, mut target: Mut<DummyComponent2>, ratio: f32) {
2840            target.value = ((self.start as f32) * (1. - ratio) + (self.end as f32) * ratio) as i32;
2841        }
2842    }
2843
2844    #[test]
2845    fn dummy_lens_component() {
2846        let mut c = DummyComponent::default();
2847        let mut l = DummyLens { start: 0., end: 1. };
2848        for r in [0_f32, 0.01, 0.3, 0.5, 0.9, 0.999, 1.] {
2849            {
2850                let mut added = Tick::new(0);
2851                let mut last_changed = Tick::new(0);
2852                let mut caller = MaybeLocation::caller();
2853                let mut target = Mut::new(
2854                    &mut c,
2855                    &mut added,
2856                    &mut last_changed,
2857                    Tick::new(0),
2858                    Tick::new(1),
2859                    caller.as_mut(),
2860                );
2861
2862                l.lerp(target.reborrow(), r);
2863
2864                assert!(target.is_changed());
2865            }
2866            assert_approx_eq!(c.value, r);
2867        }
2868    }
2869
2870    impl Lens<DummyResource> for DummyLens {
2871        fn lerp(&mut self, mut target: Mut<DummyResource>, ratio: f32) {
2872            target.value = self.start.lerp(self.end, ratio);
2873        }
2874    }
2875
2876    #[test]
2877    fn dummy_lens_resource() {
2878        let mut res = DummyResource::default();
2879        let mut l = DummyLens { start: 0., end: 1. };
2880        for r in [0_f32, 0.01, 0.3, 0.5, 0.9, 0.999, 1.] {
2881            {
2882                let mut added = Tick::new(0);
2883                let mut last_changed = Tick::new(0);
2884                let mut caller = MaybeLocation::caller();
2885                let mut target = Mut::new(
2886                    &mut res,
2887                    &mut added,
2888                    &mut last_changed,
2889                    Tick::new(0),
2890                    Tick::new(0),
2891                    caller.as_mut(),
2892                );
2893                l.lerp(target.reborrow(), r);
2894            }
2895            assert_approx_eq!(res.value, r);
2896        }
2897    }
2898
2899    impl Lens<DummyAsset> for DummyLens {
2900        fn lerp(&mut self, mut target: Mut<DummyAsset>, ratio: f32) {
2901            target.value = self.start.lerp(self.end, ratio);
2902        }
2903    }
2904
2905    #[test]
2906    fn dummy_lens_asset() {
2907        let mut assets = Assets::<DummyAsset>::default();
2908        let handle = assets.add(DummyAsset::default());
2909
2910        let mut l = DummyLens { start: 0., end: 1. };
2911        for r in [0_f32, 0.01, 0.3, 0.5, 0.9, 0.999, 1.] {
2912            {
2913                let mut added = Tick::new(0);
2914                let mut last_changed = Tick::new(0);
2915                let mut caller = MaybeLocation::caller();
2916                let asset = assets.get_mut(handle.id()).unwrap();
2917                let target = Mut::new(
2918                    asset,
2919                    &mut added,
2920                    &mut last_changed,
2921                    Tick::new(0),
2922                    Tick::new(0),
2923                    caller.as_mut(),
2924                );
2925                l.lerp(target, r);
2926            }
2927            assert_approx_eq!(assets.get(handle.id()).unwrap().value, r);
2928        }
2929    }
2930
2931    #[test]
2932    fn repeat_count() {
2933        let cycle_duration = Duration::from_millis(100);
2934
2935        let repeat = RepeatCount::default();
2936        assert_eq!(repeat, RepeatCount::Finite(1));
2937        assert_eq!(
2938            repeat.total_duration(cycle_duration),
2939            TotalDuration::Finite(cycle_duration)
2940        );
2941
2942        let repeat: RepeatCount = 3u32.into();
2943        assert_eq!(repeat, RepeatCount::Finite(3));
2944        assert_eq!(
2945            repeat.total_duration(cycle_duration),
2946            TotalDuration::Finite(cycle_duration * 3)
2947        );
2948
2949        let duration = Duration::from_secs(5);
2950        let repeat: RepeatCount = duration.into();
2951        assert_eq!(repeat, RepeatCount::For(duration));
2952        assert_eq!(
2953            repeat.total_duration(cycle_duration),
2954            TotalDuration::Finite(duration)
2955        );
2956
2957        let repeat = RepeatCount::Infinite;
2958        assert_eq!(
2959            repeat.total_duration(cycle_duration),
2960            TotalDuration::Infinite
2961        );
2962    }
2963
2964    #[test]
2965    fn repeat_strategy() {
2966        let strategy = RepeatStrategy::default();
2967        assert_eq!(strategy, RepeatStrategy::Repeat);
2968    }
2969
2970    #[test]
2971    fn playback_direction() {
2972        let tweening_direction = PlaybackDirection::default();
2973        assert_eq!(tweening_direction, PlaybackDirection::Forward);
2974    }
2975
2976    #[test]
2977    fn playback_state() {
2978        let mut state = PlaybackState::default();
2979        assert_eq!(state, PlaybackState::Playing);
2980        state = !state;
2981        assert_eq!(state, PlaybackState::Paused);
2982        state = !state;
2983        assert_eq!(state, PlaybackState::Playing);
2984    }
2985
2986    #[test]
2987    fn ease_method() {
2988        let ease = EaseMethod::default();
2989        assert!(matches!(
2990            ease,
2991            EaseMethod::EaseFunction(EaseFunction::Linear)
2992        ));
2993
2994        let ease = EaseMethod::EaseFunction(EaseFunction::QuadraticIn);
2995        assert_eq!(0., ease.sample(0.));
2996        assert_eq!(0.25, ease.sample(0.5));
2997        assert_eq!(1., ease.sample(1.));
2998
2999        let ease = EaseMethod::EaseFunction(EaseFunction::Linear);
3000        assert_eq!(0., ease.sample(0.));
3001        assert_eq!(0.5, ease.sample(0.5));
3002        assert_eq!(1., ease.sample(1.));
3003
3004        let ease = EaseMethod::Discrete(0.3);
3005        assert_eq!(0., ease.sample(0.));
3006        assert_eq!(1., ease.sample(0.5));
3007        assert_eq!(1., ease.sample(1.));
3008
3009        let ease = EaseMethod::CustomFunction(|f| 1. - f);
3010        assert_eq!(0., ease.sample(1.));
3011        assert_eq!(0.5, ease.sample(0.5));
3012        assert_eq!(1., ease.sample(0.));
3013    }
3014
3015    // TweenAnim::playback_state is entirely user-controlled; stepping animations
3016    // won't change it.
3017    #[test]
3018    fn animation_playback_state() {
3019        for state in [PlaybackState::Playing, PlaybackState::Paused] {
3020            let tween = Tween::new::<DummyComponent, DummyLens>(
3021                EaseFunction::QuadraticInOut,
3022                Duration::from_secs(1),
3023                DummyLens { start: 0., end: 1. },
3024            );
3025            let mut env = TestEnv::<DummyComponent>::new(tween);
3026            let mut anim = env.anim_mut().unwrap();
3027            anim.playback_state = state;
3028            anim.destroy_on_completion = false;
3029
3030            // Tick once
3031            let dt = Duration::from_millis(100);
3032            env.step_all(dt);
3033            assert_eq!(env.anim().unwrap().tween_state(), TweenState::Active);
3034            assert_eq!(env.anim().unwrap().playback_state, state);
3035
3036            // Check elapsed
3037            let elapsed = match state {
3038                PlaybackState::Playing => dt,
3039                PlaybackState::Paused => Duration::ZERO,
3040            };
3041            assert_eq!(env.anim().unwrap().tweenable.elapsed(), elapsed);
3042
3043            // Force playback, otherwise we can't complete
3044            env.anim_mut().unwrap().playback_state = PlaybackState::Playing;
3045
3046            // Even after completion, the playback state is untouched
3047            env.step_all(Duration::from_secs(10) - elapsed);
3048            assert_eq!(env.anim().unwrap().tween_state(), TweenState::Completed);
3049            assert_eq!(env.anim().unwrap().playback_state, PlaybackState::Playing);
3050        }
3051    }
3052
3053    #[test]
3054    fn animation_events() {
3055        let tween = Tween::new::<DummyComponent, DummyLens>(
3056            EaseFunction::QuadraticInOut,
3057            Duration::from_secs(1),
3058            DummyLens { start: 0., end: 1. },
3059        )
3060        .with_repeat_count(2)
3061        .with_cycle_completed_event(true);
3062        let mut env = TestEnv::<DummyComponent>::new(tween);
3063
3064        // Tick until one cycle is completed, but not the entire animation
3065        let dt = Duration::from_millis(1200);
3066        env.step_all(dt);
3067        assert_eq!(env.anim().unwrap().tween_state(), TweenState::Active);
3068
3069        // Check events
3070        assert_eq!(env.event_count::<CycleCompletedEvent>(), 1);
3071        assert_eq!(env.event_count::<AnimCompletedEvent>(), 0);
3072
3073        // Tick until completion
3074        let dt = Duration::from_millis(1000);
3075        env.step_all(dt);
3076        assert!(env.anim().is_none());
3077
3078        // Check events (note that we didn't clear previous events, so that's a
3079        // cumulative count).
3080        assert_eq!(env.event_count::<CycleCompletedEvent>(), 1);
3081        assert_eq!(env.event_count::<AnimCompletedEvent>(), 1);
3082    }
3083
3084    #[derive(Debug, Resource)]
3085    struct Count<E: Event, T = ()> {
3086        pub count: i32,
3087        pub phantom: PhantomData<E>,
3088        pub phantom2: PhantomData<T>,
3089    }
3090
3091    impl<E: Event, T> Default for Count<E, T> {
3092        fn default() -> Self {
3093            Self {
3094                count: 0,
3095                phantom: PhantomData,
3096                phantom2: PhantomData,
3097            }
3098        }
3099    }
3100
3101    struct GlobalMarker;
3102
3103    #[test]
3104    fn animation_observe() {
3105        let tween = Tween::new::<DummyComponent, DummyLens>(
3106            EaseFunction::QuadraticInOut,
3107            Duration::from_secs(1),
3108            DummyLens { start: 0., end: 1. },
3109        )
3110        .with_repeat_count(2)
3111        .with_cycle_completed_event(true);
3112        let mut env = TestEnv::<DummyComponent>::new(tween);
3113
3114        env.world.init_resource::<Count<CycleCompletedEvent>>();
3115        assert_eq!(env.world.resource::<Count<CycleCompletedEvent>>().count, 0);
3116        env.world
3117            .init_resource::<Count<CycleCompletedEvent, GlobalMarker>>();
3118        assert_eq!(
3119            env.world
3120                .resource::<Count<CycleCompletedEvent, GlobalMarker>>()
3121                .count,
3122            0
3123        );
3124
3125        fn observe_global(
3126            _trigger: On<CycleCompletedEvent>,
3127            mut count: ResMut<Count<CycleCompletedEvent, GlobalMarker>>,
3128        ) {
3129            count.count += 1;
3130        }
3131        env.world.add_observer(observe_global);
3132
3133        fn observe_entity(
3134            _trigger: On<CycleCompletedEvent>,
3135            mut count: ResMut<Count<CycleCompletedEvent>>,
3136        ) {
3137            count.count += 1;
3138        }
3139        env.world.entity_mut(env.entity).observe(observe_entity);
3140
3141        // Tick until one cycle is completed, but not the entire animation
3142        let dt = Duration::from_millis(1200);
3143        env.step_all(dt);
3144        assert_eq!(env.anim().unwrap().tween_state(), TweenState::Active);
3145
3146        // Check observer system ran
3147        assert_eq!(env.world.resource::<Count<CycleCompletedEvent>>().count, 1);
3148        assert_eq!(
3149            env.world
3150                .resource::<Count<CycleCompletedEvent, GlobalMarker>>()
3151                .count,
3152            1
3153        );
3154
3155        // Tick until completion
3156        let dt = Duration::from_millis(1000);
3157        env.step_all(dt);
3158        assert!(env.anim().is_none());
3159
3160        // Check observer system ran (note that we didn't clear previous events, so
3161        // that's a cumulative count).
3162        assert_eq!(env.world.resource::<Count<CycleCompletedEvent>>().count, 2);
3163        assert_eq!(
3164            env.world
3165                .resource::<Count<CycleCompletedEvent, GlobalMarker>>()
3166                .count,
3167            2
3168        );
3169    }
3170
3171    // #[test]
3172    // fn animator_controls() {
3173    //     let tween = Tween::<DummyComponent>::new(
3174    //         EaseFunction::QuadraticInOut,
3175    //         Duration::from_secs(1),
3176    //         DummyLens { start: 0., end: 1. },
3177    //     );
3178    //     let mut animator = Animator::new(tween);
3179    //     assert_eq!(animator.state, AnimatorState::Playing);
3180    //     assert_approx_eq!(animator.tweenable().progress(), 0.);
3181
3182    //     animator.stop();
3183    //     assert_eq!(animator.state, AnimatorState::Paused);
3184    //     assert_approx_eq!(animator.tweenable().progress(), 0.);
3185
3186    //     animator.tweenable_mut().set_progress(0.5);
3187    //     assert_eq!(animator.state, AnimatorState::Paused);
3188    //     assert_approx_eq!(animator.tweenable().progress(), 0.5);
3189
3190    //     animator.tweenable_mut().rewind();
3191    //     assert_eq!(animator.state, AnimatorState::Paused);
3192    //     assert_approx_eq!(animator.tweenable().progress(), 0.);
3193
3194    //     animator.tweenable_mut().set_progress(0.5);
3195    //     animator.state = AnimatorState::Playing;
3196    //     assert_eq!(animator.state, AnimatorState::Playing);
3197    //     assert_approx_eq!(animator.tweenable().progress(), 0.5);
3198
3199    //     animator.tweenable_mut().rewind();
3200    //     assert_eq!(animator.state, AnimatorState::Playing);
3201    //     assert_approx_eq!(animator.tweenable().progress(), 0.);
3202
3203    //     animator.stop();
3204    //     assert_eq!(animator.state, AnimatorState::Paused);
3205    //     assert_approx_eq!(animator.tweenable().progress(), 0.);
3206    // }
3207
3208    #[test]
3209    fn animation_speed() {
3210        let tween = Tween::new::<DummyComponent, DummyLens>(
3211            EaseFunction::QuadraticInOut,
3212            Duration::from_secs(1),
3213            DummyLens { start: 0., end: 1. },
3214        );
3215
3216        let mut env = TestEnv::<DummyComponent>::new(tween);
3217
3218        assert_approx_eq!(env.anim().unwrap().speed, 1.); // default speed
3219
3220        env.anim_mut().unwrap().speed = 2.4;
3221        assert_approx_eq!(env.anim().unwrap().speed, 2.4);
3222
3223        env.step_all(Duration::from_millis(100));
3224        // Here we have enough precision for exact equality, but that may not always be
3225        // the case for larger durations or speed values.
3226        assert_eq!(
3227            env.anim().unwrap().tweenable.elapsed(),
3228            Duration::from_millis(240)
3229        );
3230
3231        env.anim_mut().unwrap().speed = -1.;
3232        env.step_all(Duration::from_millis(100));
3233        // Safety: invalid negative speed clamped to 0.
3234        assert_eq!(env.anim().unwrap().speed, 0.);
3235        // At zero speed, step is a no-op so elapse() didn't change
3236        assert_eq!(
3237            env.anim().unwrap().tweenable.elapsed(),
3238            Duration::from_millis(240)
3239        );
3240    }
3241
3242    #[test]
3243    fn animator_set_tweenable() {
3244        let tween = Tween::new::<DummyComponent, DummyLens>(
3245            EaseFunction::QuadraticInOut,
3246            Duration::from_secs(1),
3247            DummyLens { start: 0., end: 1. },
3248        );
3249        let tween2 = Tween::new::<DummyComponent, DummyLens>(
3250            EaseFunction::SmoothStep,
3251            Duration::from_secs(2),
3252            DummyLens { start: 2., end: 3. },
3253        );
3254
3255        let mut env = TestEnv::<DummyComponent>::new(tween);
3256        env.anim_mut().unwrap().destroy_on_completion = false;
3257
3258        let dt = Duration::from_millis(1500);
3259
3260        env.step_all(dt);
3261        assert_eq!(env.component().value, 1.);
3262        assert_eq!(env.anim().unwrap().tween_state(), TweenState::Completed);
3263
3264        // Swap tweens
3265        let old_tweenable = env.anim_mut().unwrap().set_tweenable(tween2).unwrap();
3266
3267        assert_eq!(env.anim().unwrap().tween_state(), TweenState::Active);
3268        // The elapsed is stored inside the tweenable
3269        assert_eq!(old_tweenable.elapsed(), Duration::from_secs(1)); // capped at total_duration()
3270        assert_eq!(env.anim().unwrap().tweenable.elapsed(), Duration::ZERO);
3271
3272        env.step_all(dt);
3273        assert!(env.component().value >= 2. && env.component().value <= 3.);
3274    }
3275
3276    // Currently multi-target sequences are not implemented. This _could_ work with
3277    // implicit targets (so, multiple components on the same entity), but is a bit
3278    // complex to implement with the current code. So leave that out for now, and
3279    // test we assert if the user attempts it. The workaround is to create separate
3280    // animations for each comopnent/target. Anyway multi-target sequence can't work
3281    // with other target types, since they need an explicit TweenAnim, and we
3282    // can't have more than one per entity.
3283    #[test]
3284    #[should_panic(
3285        expected = "TODO: Cannot use tweenable animations with different targets inside the same Sequence. Create separate animations for each target."
3286    )]
3287    fn seq_multi_target() {
3288        let tween = Tween::new::<DummyComponent, DummyLens>(
3289            EaseFunction::QuadraticInOut,
3290            Duration::from_secs(1),
3291            DummyLens { start: 0., end: 1. },
3292        )
3293        .then(Tween::new::<DummyComponent2, DummyLens2>(
3294            EaseFunction::SmoothStep,
3295            Duration::from_secs(1),
3296            DummyLens2 { start: -5, end: 5 },
3297        ));
3298        let mut env = TestEnv::<DummyComponent>::new(tween);
3299        let entity = env.entity;
3300        env.world
3301            .entity_mut(entity)
3302            .insert(DummyComponent2 { value: -42 });
3303        TweenAnim::step_one(&mut env.world, Duration::from_millis(1100), entity).unwrap();
3304    }
3305
3306    // #[test]
3307    // fn animator_set_target() {
3308    //     let tween = Tween::new::<DummyComponent, DummyLens>(
3309    //         EaseFunction::QuadraticInOut,
3310    //         Duration::from_secs(1),
3311    //         DummyLens { start: 0., end: 1. },
3312    //     );
3313    //     let mut env = TestEnv::<DummyComponent>::new(tween);
3314
3315    //     // Register our custom asset type
3316    //     env.world.init_resource::<Assets<DummyAsset>>();
3317
3318    //     // Invalid ID
3319    //     {
3320    //         let entity = env.entity;
3321    //         let target =
3322    //
3323    // ComponentAnimTarget::new::<DummyComponent>(env.world.components(),
3324    // entity).unwrap();         let err = env
3325    //             .animator_mut()
3326    //             .set_target(Entity::PLACEHOLDER, target.into())
3327    //             .err()
3328    //             .unwrap();
3329    //         let TweeningError::InvalidTweenId(err_id) = err else {
3330    //             panic!();
3331    //         };
3332    //         assert_eq!(err_id, Entity::PLACEHOLDER);
3333    //     }
3334
3335    //     // Spawn a second entity without any animation
3336    //     let entity1 = env.entity;
3337    //     let entity2 = env.world_mut().spawn(DummyComponent { value: 0.
3338    // }).id();     assert_ne!(entity1, entity2);
3339    //     assert_eq!(env.component().value, 0.);
3340
3341    //     // Step the current target
3342    //     let dt = Duration::from_millis(100);
3343    //     env.step_all(dt);
3344    //     assert!(env.component().value > 0.);
3345    //     assert_eq!(
3346    //         env.world
3347    //             .entity(entity2)
3348    //             .get_components::<&DummyComponent>()
3349    //             .unwrap()
3350    //             .value,
3351    //         0.
3352    //     );
3353
3354    //     // Now retarget
3355    //     let id = env.entity;
3356    //     let target2 =
3357    //         ComponentAnimTarget::new::<DummyComponent>(env.world.
3358    // components(), entity2).unwrap();     let target1 =
3359    // env.animator_mut().set_target(id, target2.into()).unwrap();
3360    //     assert!(target1.is_component());
3361    //     let comp1 = target1.as_component().unwrap();
3362    //     assert_eq!(comp1.entity, entity1);
3363    //     assert_eq!(
3364    //         comp1.component_id,
3365    //         env.world.component_id::<DummyComponent>().unwrap()
3366    //     );
3367
3368    //     // Step the new target
3369    //     env.step_all(dt);
3370    //     assert!(env.component().value > 0.);
3371    //     assert!(
3372    //         env.world
3373    //             .entity(entity1)
3374    //             .get_components::<&DummyComponent>()
3375    //             .unwrap()
3376    //             .value
3377    //             > 0.
3378    //     );
3379
3380    //     // Invalid target
3381    //     {
3382    //         let target3 =
3383    //             AssetAnimTarget::new(env.world.components(),
3384    // Handle::<DummyAsset>::default().id())                 .unwrap();
3385    //         let err3 = env.animator_mut().set_target(id, target3.into());
3386    //         assert!(err3.is_err());
3387    //         let err3 = err3.err().unwrap();
3388    //         let TweeningError::MismatchingTargetKind(oc, nc) = err3 else {
3389    //             panic!();
3390    //         };
3391    //         assert_eq!(oc, true);
3392    //         assert_eq!(nc, false);
3393    //     }
3394    // }
3395
3396    #[test]
3397    fn anim_target_component() {
3398        let mut env = TestEnv::<Transform>::empty();
3399        let entity = env.world.spawn(Transform::default()).id();
3400        let tween = Tween::new::<Transform, TransformPositionLens>(
3401            EaseFunction::Linear,
3402            Duration::from_secs(1),
3403            TransformPositionLens {
3404                start: Vec3::ZERO,
3405                end: Vec3::ONE,
3406            },
3407        );
3408        let target = AnimTarget::component::<Transform>(entity);
3409        let anim_entity = env
3410            .world
3411            .spawn((
3412                TweenAnim::new(tween)
3413                    .with_speed(2.)
3414                    .with_destroy_on_completed(true),
3415                target,
3416            ))
3417            .id();
3418
3419        // Step
3420        assert!(
3421            TweenAnim::step_one(&mut env.world, Duration::from_millis(100), anim_entity).is_ok()
3422        );
3423        let tr = env.world.entity(entity).get::<Transform>().unwrap();
3424        assert_eq!(tr.translation, Vec3::ONE * 0.2);
3425
3426        // Complete
3427        assert_eq!(
3428            TweenAnim::step_many(&mut env.world, Duration::from_millis(400), &[anim_entity]),
3429            1
3430        );
3431
3432        // Destroyed on completion
3433        assert!(env.world.entity(anim_entity).get::<TweenAnim>().is_none());
3434    }
3435
3436    #[test]
3437    fn anim_target_resource() {
3438        let mut env = TestEnv::<Transform>::empty();
3439        env.world.init_resource::<DummyResource>();
3440        let tween = Tween::new::<DummyResource, DummyLens>(
3441            EaseFunction::Linear,
3442            Duration::from_secs(1),
3443            DummyLens { start: 0., end: 1. },
3444        );
3445        let target = AnimTarget::resource::<DummyResource>();
3446        let anim_entity = env
3447            .world
3448            .spawn((
3449                TweenAnim::new(tween)
3450                    .with_speed(2.)
3451                    .with_destroy_on_completed(true),
3452                target,
3453            ))
3454            .id();
3455
3456        // Step
3457        assert!(
3458            TweenAnim::step_one(&mut env.world, Duration::from_millis(100), anim_entity).is_ok()
3459        );
3460        let res = env.world.resource::<DummyResource>();
3461        assert_eq!(res.value, 0.2);
3462
3463        // Complete
3464        assert_eq!(
3465            TweenAnim::step_many(&mut env.world, Duration::from_millis(400), &[anim_entity]),
3466            1
3467        );
3468
3469        // Destroyed on completion
3470        assert!(env.world.entity(anim_entity).get::<TweenAnim>().is_none());
3471    }
3472
3473    #[test]
3474    fn anim_target_asset() {
3475        let mut env = TestEnv::<Transform>::empty();
3476        let mut assets = Assets::<DummyAsset>::default();
3477        let handle = assets.add(DummyAsset::default());
3478        env.world.insert_resource(assets);
3479        let tween = Tween::new::<DummyAsset, DummyLens>(
3480            EaseFunction::Linear,
3481            Duration::from_secs(1),
3482            DummyLens { start: 0., end: 1. },
3483        );
3484        let target = AnimTarget::asset::<DummyAsset>(&handle);
3485        let anim_entity = env
3486            .world
3487            .spawn((
3488                TweenAnim::new(tween)
3489                    .with_speed(2.)
3490                    .with_destroy_on_completed(true),
3491                target,
3492            ))
3493            .id();
3494
3495        // Step
3496        assert!(
3497            TweenAnim::step_one(&mut env.world, Duration::from_millis(100), anim_entity).is_ok()
3498        );
3499        let assets = env.world.resource::<Assets<DummyAsset>>();
3500        let asset = assets.get(&handle).unwrap();
3501        assert_eq!(asset.value, 0.2);
3502
3503        // Complete
3504        assert_eq!(
3505            TweenAnim::step_many(&mut env.world, Duration::from_millis(400), &[anim_entity]),
3506            1
3507        );
3508
3509        // Destroyed on completion
3510        assert!(env.world.entity(anim_entity).get::<TweenAnim>().is_none());
3511    }
3512
3513    #[test]
3514    fn animated_entity_commands_common() {
3515        let dummy_tween = Tween::new::<DummyComponent, DummyLens>(
3516            EaseFunction::QuadraticInOut,
3517            Duration::from_secs(1),
3518            DummyLens { start: 0., end: 1. },
3519        );
3520        let mut env = TestEnv::<DummyComponent>::new(dummy_tween);
3521
3522        let entity = env
3523            .world
3524            .commands()
3525            .spawn(Transform::default())
3526            .move_to(Vec3::ONE, Duration::from_secs(1), EaseFunction::Linear)
3527            .with_repeat_count(4)
3528            .with_repeat_strategy(RepeatStrategy::MirroredRepeat)
3529            .id();
3530        let entity2 = env
3531            .world
3532            .commands()
3533            .spawn(Transform::default())
3534            .move_to(Vec3::ONE, Duration::from_secs(1), EaseFunction::Linear)
3535            .with_repeat(4, RepeatStrategy::MirroredRepeat)
3536            .into_inner()
3537            .id();
3538        env.world.flush();
3539
3540        env.step_all(Duration::from_millis(3300));
3541
3542        let tr = env.world.entity(entity).get::<Transform>().unwrap();
3543        assert_eq!(tr.translation, Vec3::ONE * 0.7);
3544        let tr = env.world.entity(entity2).get::<Transform>().unwrap();
3545        assert_eq!(tr.translation, Vec3::ONE * 0.7);
3546    }
3547
3548    #[test]
3549    fn animated_entity_commands_move_to() {
3550        let dummy_tween = Tween::new::<DummyComponent, DummyLens>(
3551            EaseFunction::QuadraticInOut,
3552            Duration::from_secs(1),
3553            DummyLens { start: 0., end: 1. },
3554        );
3555        let mut env = TestEnv::<DummyComponent>::new(dummy_tween);
3556
3557        let entity = env
3558            .world
3559            .commands()
3560            .spawn(Transform::default())
3561            .move_to(Vec3::ONE, Duration::from_secs(1), EaseFunction::Linear)
3562            .id();
3563        env.world.flush();
3564
3565        env.step_all(Duration::from_millis(300));
3566
3567        let tr = env.world.entity(entity).get::<Transform>().unwrap();
3568        assert_eq!(tr.translation, Vec3::ONE * 0.3);
3569    }
3570
3571    #[test]
3572    fn animated_entity_commands_move_from() {
3573        let dummy_tween = Tween::new::<DummyComponent, DummyLens>(
3574            EaseFunction::QuadraticInOut,
3575            Duration::from_secs(1),
3576            DummyLens { start: 0., end: 1. },
3577        );
3578        let mut env = TestEnv::<DummyComponent>::new(dummy_tween);
3579
3580        let entity = env
3581            .world
3582            .commands()
3583            .spawn(Transform::default())
3584            .move_from(Vec3::ONE, Duration::from_secs(1), EaseFunction::Linear)
3585            .id();
3586        env.world.flush();
3587
3588        env.step_all(Duration::from_millis(300));
3589
3590        let tr = env.world.entity(entity).get::<Transform>().unwrap();
3591        assert_eq!(tr.translation, Vec3::ONE * 0.7);
3592    }
3593
3594    #[test]
3595    fn animated_entity_commands_scale_to() {
3596        let dummy_tween = Tween::new::<DummyComponent, DummyLens>(
3597            EaseFunction::QuadraticInOut,
3598            Duration::from_secs(1),
3599            DummyLens { start: 0., end: 1. },
3600        );
3601        let mut env = TestEnv::<DummyComponent>::new(dummy_tween);
3602
3603        let entity = env
3604            .world
3605            .commands()
3606            .spawn(Transform::default())
3607            .scale_to(Vec3::ONE * 2., Duration::from_secs(1), EaseFunction::Linear)
3608            .id();
3609        env.world.flush();
3610
3611        env.step_all(Duration::from_millis(300));
3612
3613        let tr = env.world.entity(entity).get::<Transform>().unwrap();
3614        assert_eq!(tr.scale, Vec3::ONE * 1.3);
3615    }
3616
3617    #[test]
3618    fn animated_entity_commands_scale_from() {
3619        let dummy_tween = Tween::new::<DummyComponent, DummyLens>(
3620            EaseFunction::QuadraticInOut,
3621            Duration::from_secs(1),
3622            DummyLens { start: 0., end: 1. },
3623        );
3624        let mut env = TestEnv::<DummyComponent>::new(dummy_tween);
3625
3626        let entity = env
3627            .world
3628            .commands()
3629            .spawn(Transform::default())
3630            .scale_from(Vec3::ONE * 2., Duration::from_secs(1), EaseFunction::Linear)
3631            .id();
3632        env.world.flush();
3633
3634        env.step_all(Duration::from_millis(300));
3635
3636        let tr = env.world.entity(entity).get::<Transform>().unwrap();
3637        assert_eq!(tr.scale, Vec3::ONE * 1.7);
3638    }
3639
3640    #[test]
3641    fn animated_entity_commands_rotate_x() {
3642        let dummy_tween = Tween::new::<DummyComponent, DummyLens>(
3643            EaseFunction::QuadraticInOut,
3644            Duration::from_secs(1),
3645            DummyLens { start: 0., end: 1. },
3646        );
3647        let mut env = TestEnv::<DummyComponent>::new(dummy_tween);
3648
3649        let entity = env
3650            .world
3651            .commands()
3652            .spawn(Transform::default())
3653            .rotate_x(Duration::from_secs(1))
3654            .id();
3655        env.world.flush();
3656
3657        env.step_all(Duration::from_millis(1300));
3658
3659        let tr = env.world.entity(entity).get::<Transform>().unwrap();
3660        assert_eq!(tr.rotation, Quat::from_rotation_x(TAU * 0.3));
3661    }
3662
3663    #[test]
3664    fn animated_entity_commands_rotate_y() {
3665        let dummy_tween = Tween::new::<DummyComponent, DummyLens>(
3666            EaseFunction::QuadraticInOut,
3667            Duration::from_secs(1),
3668            DummyLens { start: 0., end: 1. },
3669        );
3670        let mut env = TestEnv::<DummyComponent>::new(dummy_tween);
3671
3672        let entity = env
3673            .world
3674            .commands()
3675            .spawn(Transform::default())
3676            .rotate_y(Duration::from_secs(1))
3677            .id();
3678        env.world.flush();
3679
3680        env.step_all(Duration::from_millis(1300));
3681
3682        let tr = env.world.entity(entity).get::<Transform>().unwrap();
3683        assert_eq!(tr.rotation, Quat::from_rotation_y(TAU * 0.3));
3684    }
3685
3686    #[test]
3687    fn animated_entity_commands_rotate_z() {
3688        let dummy_tween = Tween::new::<DummyComponent, DummyLens>(
3689            EaseFunction::QuadraticInOut,
3690            Duration::from_secs(1),
3691            DummyLens { start: 0., end: 1. },
3692        );
3693        let mut env = TestEnv::<DummyComponent>::new(dummy_tween);
3694
3695        let entity = env
3696            .world
3697            .commands()
3698            .spawn(Transform::default())
3699            .rotate_z(Duration::from_secs(1))
3700            .id();
3701        env.world.flush();
3702
3703        env.step_all(Duration::from_millis(1300));
3704
3705        let tr = env.world.entity(entity).get::<Transform>().unwrap();
3706        assert_eq!(tr.rotation, Quat::from_rotation_z(TAU * 0.3));
3707    }
3708
3709    #[test]
3710    fn animated_entity_commands_rotate_x_by() {
3711        let dummy_tween = Tween::new::<DummyComponent, DummyLens>(
3712            EaseFunction::QuadraticInOut,
3713            Duration::from_secs(1),
3714            DummyLens { start: 0., end: 1. },
3715        );
3716        let mut env = TestEnv::<DummyComponent>::new(dummy_tween);
3717
3718        let entity = env
3719            .world
3720            .commands()
3721            .spawn(Transform::default())
3722            .rotate_x_by(FRAC_PI_2, Duration::from_secs(1), EaseFunction::Linear)
3723            .id();
3724        env.world.flush();
3725
3726        env.step_all(Duration::from_millis(1300)); // 130%
3727
3728        let tr = env.world.entity(entity).get::<Transform>().unwrap();
3729        assert_eq!(tr.rotation, Quat::from_rotation_x(FRAC_PI_2)); // 100%
3730    }
3731
3732    #[test]
3733    fn animated_entity_commands_rotate_y_by() {
3734        let dummy_tween = Tween::new::<DummyComponent, DummyLens>(
3735            EaseFunction::QuadraticInOut,
3736            Duration::from_secs(1),
3737            DummyLens { start: 0., end: 1. },
3738        );
3739        let mut env = TestEnv::<DummyComponent>::new(dummy_tween);
3740
3741        let entity = env
3742            .world
3743            .commands()
3744            .spawn(Transform::default())
3745            .rotate_y_by(FRAC_PI_2, Duration::from_secs(1), EaseFunction::Linear)
3746            .id();
3747        env.world.flush();
3748
3749        env.step_all(Duration::from_millis(1300)); // 130%
3750
3751        let tr = env.world.entity(entity).get::<Transform>().unwrap();
3752        assert_eq!(tr.rotation, Quat::from_rotation_y(FRAC_PI_2)); // 100%
3753    }
3754
3755    #[test]
3756    fn animated_entity_commands_rotate_z_by() {
3757        let dummy_tween = Tween::new::<DummyComponent, DummyLens>(
3758            EaseFunction::QuadraticInOut,
3759            Duration::from_secs(1),
3760            DummyLens { start: 0., end: 1. },
3761        );
3762        let mut env = TestEnv::<DummyComponent>::new(dummy_tween);
3763
3764        let entity = env
3765            .world
3766            .commands()
3767            .spawn(Transform::default())
3768            .rotate_z_by(FRAC_PI_2, Duration::from_secs(1), EaseFunction::Linear)
3769            .id();
3770        env.world.flush();
3771
3772        env.step_all(Duration::from_millis(1300)); // 130%
3773
3774        let tr = env.world.entity(entity).get::<Transform>().unwrap();
3775        assert_eq!(tr.rotation, Quat::from_rotation_z(FRAC_PI_2)); // 100%
3776    }
3777
3778    #[test]
3779    fn resolver_resource() {
3780        let dummy_tween = Tween::new::<DummyComponent, DummyLens>(
3781            EaseFunction::QuadraticInOut,
3782            Duration::from_secs(1),
3783            DummyLens { start: 0., end: 1. },
3784        );
3785        let mut env = TestEnv::<DummyComponent>::new(dummy_tween);
3786
3787        // Register the resource and create a TweenAnim for it
3788        env.world.init_resource::<DummyResource>();
3789        let tween = Tween::new::<DummyResource, DummyLens>(
3790            EaseFunction::QuadraticInOut,
3791            Duration::from_secs(1),
3792            DummyLens { start: 0., end: 1. },
3793        );
3794        let entity = env.world.commands().spawn(TweenAnim::new(tween)).id();
3795
3796        // Ensure all commands are applied before starting the test
3797        env.world.flush();
3798
3799        let delta_time = Duration::from_millis(200);
3800        let resource_id = env.world.resource_id::<DummyResource>().unwrap();
3801
3802        // Resource resolver not registered; fails
3803        env.world
3804            .resource_scope(|world, resolver: Mut<TweenResolver>| {
3805                world.resource_scope(
3806                    |world, mut cycle_events: Mut<Messages<CycleCompletedEvent>>| {
3807                        world.resource_scope(
3808                            |world, mut anim_events: Mut<Messages<AnimCompletedEvent>>| {
3809                                assert!(resolver
3810                                    .resolve_resource(
3811                                        world,
3812                                        &TypeId::of::<DummyResource>(),
3813                                        resource_id,
3814                                        entity,
3815                                        delta_time,
3816                                        cycle_events.reborrow(),
3817                                        anim_events.reborrow(),
3818                                    )
3819                                    .is_err());
3820                            },
3821                        );
3822                    },
3823                );
3824            });
3825
3826        // Register the resource resolver
3827        env.world
3828            .resource_scope(|world, mut resolver: Mut<TweenResolver>| {
3829                resolver.register_resource_resolver_for::<DummyResource>(world.components());
3830            });
3831
3832        // Resource resolver registered; succeeds
3833        env.world
3834            .resource_scope(|world, resolver: Mut<TweenResolver>| {
3835                world.resource_scope(
3836                    |world, mut cycle_events: Mut<Messages<CycleCompletedEvent>>| {
3837                        world.resource_scope(
3838                            |world, mut anim_events: Mut<Messages<AnimCompletedEvent>>| {
3839                                assert!(resolver
3840                                    .resolve_resource(
3841                                        world,
3842                                        &TypeId::of::<DummyResource>(),
3843                                        resource_id,
3844                                        entity,
3845                                        delta_time,
3846                                        cycle_events.reborrow(),
3847                                        anim_events.reborrow(),
3848                                    )
3849                                    .unwrap());
3850                            },
3851                        );
3852                    },
3853                );
3854            });
3855    }
3856
3857    #[test]
3858    fn resolver_asset() {
3859        let dummy_tween = Tween::new::<DummyComponent, DummyLens>(
3860            EaseFunction::QuadraticInOut,
3861            Duration::from_secs(1),
3862            DummyLens { start: 0., end: 1. },
3863        );
3864        let mut env = TestEnv::<DummyComponent>::new(dummy_tween);
3865
3866        // Register the asset and create a TweenAnim for it
3867        let mut assets = Assets::<DummyAsset>::default();
3868        let handle = assets.add(DummyAsset::default());
3869        let untyped_asset_id = handle.id().untyped();
3870        env.world.insert_resource(assets);
3871        let tween = Tween::new::<DummyAsset, DummyLens>(
3872            EaseFunction::QuadraticInOut,
3873            Duration::from_secs(1),
3874            DummyLens { start: 0., end: 1. },
3875        );
3876        let entity = env.world.commands().spawn(TweenAnim::new(tween)).id();
3877
3878        // Ensure all commands are applied before starting the test
3879        env.world.flush();
3880
3881        let delta_time = Duration::from_millis(200);
3882        let resource_id = env.world.resource_id::<Assets<DummyAsset>>().unwrap();
3883
3884        // Asset resolver not registered; fails
3885        env.world
3886            .resource_scope(|world, resolver: Mut<TweenResolver>| {
3887                world.resource_scope(
3888                    |world, mut cycle_events: Mut<Messages<CycleCompletedEvent>>| {
3889                        world.resource_scope(
3890                            |world, mut anim_events: Mut<Messages<AnimCompletedEvent>>| {
3891                                assert!(resolver
3892                                    .resolve_asset(
3893                                        world,
3894                                        &TypeId::of::<DummyAsset>(),
3895                                        resource_id,
3896                                        untyped_asset_id,
3897                                        entity,
3898                                        delta_time,
3899                                        cycle_events.reborrow(),
3900                                        anim_events.reborrow(),
3901                                    )
3902                                    .is_err());
3903                            },
3904                        );
3905                    },
3906                );
3907            });
3908
3909        // Register the asset resolver
3910        env.world
3911            .resource_scope(|world, mut resolver: Mut<TweenResolver>| {
3912                resolver.register_asset_resolver_for::<DummyAsset>(world.components());
3913            });
3914
3915        // Asset resolver registered; succeeds
3916        env.world
3917            .resource_scope(|world, resolver: Mut<TweenResolver>| {
3918                world.resource_scope(
3919                    |world, mut cycle_events: Mut<Messages<CycleCompletedEvent>>| {
3920                        world.resource_scope(
3921                            |world, mut anim_events: Mut<Messages<AnimCompletedEvent>>| {
3922                                assert!(resolver
3923                                    .resolve_asset(
3924                                        world,
3925                                        &TypeId::of::<DummyAsset>(),
3926                                        resource_id,
3927                                        untyped_asset_id,
3928                                        entity,
3929                                        delta_time,
3930                                        cycle_events.reborrow(),
3931                                        anim_events.reborrow(),
3932                                    )
3933                                    .unwrap());
3934                            },
3935                        );
3936                    },
3937                );
3938            });
3939    }
3940}