Skip to main content

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