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.19/bevy/transform/components/struct.Transform.html#structfield.translation
287//! [`Entity`]: https://docs.rs/bevy/0.19/bevy/ecs/entity/struct.Entity.html
288//! [`ColorMaterial`]: https://docs.rs/bevy/0.19/bevy/sprite/struct.ColorMaterial.html
289//! [`Transform`]: https://docs.rs/bevy/0.19/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::{MaybeLocation, MutUntyped, Tick},
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    type Out = ();
919    fn apply(self, mut entity: EntityWorldMut) {
920        if let Some(start) = entity.get::<Transform>().map(|tr| tr.translation) {
921            let lens = TransformPositionLens {
922                start,
923                end: self.end,
924            };
925            let tween = Tween::from_config(self.config, lens);
926            let anim_target = AnimTarget::component::<Transform>(entity.id());
927            entity.world_scope(|world| {
928                world.spawn((TweenAnim::new(tween), anim_target));
929            });
930        }
931    }
932}
933
934impl TweenCommand for MoveToCommand {
935    #[inline]
936    fn config(&self) -> &TweenConfig {
937        &self.config
938    }
939
940    #[inline]
941    fn config_mut(&mut self) -> &mut TweenConfig {
942        &mut self.config
943    }
944}
945
946/// Animation command to move an entity from a source position.
947#[derive(Clone, Copy)]
948pub(crate) struct MoveFromCommand {
949    start: Vec3,
950    config: TweenConfig,
951}
952
953impl EntityCommand for MoveFromCommand {
954    type Out = ();
955    fn apply(self, mut entity: EntityWorldMut) {
956        if let Some(end) = entity.get::<Transform>().map(|tr| tr.translation) {
957            let lens = TransformPositionLens {
958                start: self.start,
959                end,
960            };
961            let tween = Tween::from_config(self.config, lens);
962            let anim_target = AnimTarget::component::<Transform>(entity.id());
963            entity.world_scope(|world| {
964                world.spawn((TweenAnim::new(tween), anim_target));
965            });
966        }
967    }
968}
969
970impl TweenCommand for MoveFromCommand {
971    #[inline]
972    fn config(&self) -> &TweenConfig {
973        &self.config
974    }
975
976    #[inline]
977    fn config_mut(&mut self) -> &mut TweenConfig {
978        &mut self.config
979    }
980}
981
982/// Animation command to scale an entity to a target size.
983#[derive(Clone, Copy)]
984pub(crate) struct ScaleToCommand {
985    end: Vec3,
986    config: TweenConfig,
987}
988
989impl EntityCommand for ScaleToCommand {
990    type Out = ();
991    fn apply(self, mut entity: EntityWorldMut) {
992        if let Some(start) = entity.get::<Transform>().map(|tr| tr.scale) {
993            let lens = TransformScaleLens {
994                start,
995                end: self.end,
996            };
997            let tween = Tween::from_config(self.config, lens);
998            let anim_target = AnimTarget::component::<Transform>(entity.id());
999            entity.world_scope(|world| {
1000                world.spawn((TweenAnim::new(tween), anim_target));
1001            });
1002        }
1003    }
1004}
1005
1006impl TweenCommand for ScaleToCommand {
1007    #[inline]
1008    fn config(&self) -> &TweenConfig {
1009        &self.config
1010    }
1011
1012    #[inline]
1013    fn config_mut(&mut self) -> &mut TweenConfig {
1014        &mut self.config
1015    }
1016}
1017
1018/// Animation command to scale an entity from a source size.
1019#[derive(Clone, Copy)]
1020pub(crate) struct ScaleFromCommand {
1021    start: Vec3,
1022    config: TweenConfig,
1023}
1024
1025impl EntityCommand for ScaleFromCommand {
1026    type Out = ();
1027    fn apply(self, mut entity: EntityWorldMut) {
1028        if let Some(end) = entity.get::<Transform>().map(|tr| tr.scale) {
1029            let lens = TransformScaleLens {
1030                start: self.start,
1031                end,
1032            };
1033            let tween = Tween::from_config(self.config, lens);
1034            let anim_target = AnimTarget::component::<Transform>(entity.id());
1035            entity.world_scope(|world| {
1036                world.spawn((TweenAnim::new(tween), anim_target));
1037            });
1038        }
1039    }
1040}
1041
1042impl TweenCommand for ScaleFromCommand {
1043    #[inline]
1044    fn config(&self) -> &TweenConfig {
1045        &self.config
1046    }
1047
1048    #[inline]
1049    fn config_mut(&mut self) -> &mut TweenConfig {
1050        &mut self.config
1051    }
1052}
1053
1054/// Animation command to rotate an entity around its X axis.
1055#[derive(Clone, Copy)]
1056pub(crate) struct RotateXCommand {
1057    config: TweenConfig,
1058}
1059
1060impl EntityCommand for RotateXCommand {
1061    type Out = ();
1062    fn apply(self, mut entity: EntityWorldMut) {
1063        if let Some(base_rotation) = entity.get::<Transform>().map(|tr| tr.rotation) {
1064            let lens = TransformRotateAdditiveXLens {
1065                base_rotation,
1066                start: 0.,
1067                end: std::f32::consts::TAU,
1068            };
1069            let tween = Tween::from_config(self.config, lens)
1070                .with_repeat(RepeatCount::Infinite, RepeatStrategy::Repeat);
1071            let anim_target = AnimTarget::component::<Transform>(entity.id());
1072            entity.world_scope(|world| {
1073                world.spawn((TweenAnim::new(tween), anim_target));
1074            });
1075        }
1076    }
1077}
1078
1079impl TweenCommand for RotateXCommand {
1080    #[inline]
1081    fn config(&self) -> &TweenConfig {
1082        &self.config
1083    }
1084
1085    #[inline]
1086    fn config_mut(&mut self) -> &mut TweenConfig {
1087        &mut self.config
1088    }
1089}
1090
1091/// Animation command to rotate an entity around its Y axis.
1092#[derive(Clone, Copy)]
1093pub(crate) struct RotateYCommand {
1094    config: TweenConfig,
1095}
1096
1097impl EntityCommand for RotateYCommand {
1098    type Out = ();
1099    fn apply(self, mut entity: EntityWorldMut) {
1100        if let Some(base_rotation) = entity.get::<Transform>().map(|tr| tr.rotation) {
1101            let lens = TransformRotateAdditiveYLens {
1102                base_rotation,
1103                start: 0.,
1104                end: std::f32::consts::TAU,
1105            };
1106            let tween = Tween::from_config(self.config, lens)
1107                .with_repeat(RepeatCount::Infinite, RepeatStrategy::Repeat);
1108            let anim_target = AnimTarget::component::<Transform>(entity.id());
1109            entity.world_scope(|world| {
1110                world.spawn((TweenAnim::new(tween), anim_target));
1111            });
1112        }
1113    }
1114}
1115
1116impl TweenCommand for RotateYCommand {
1117    #[inline]
1118    fn config(&self) -> &TweenConfig {
1119        &self.config
1120    }
1121
1122    #[inline]
1123    fn config_mut(&mut self) -> &mut TweenConfig {
1124        &mut self.config
1125    }
1126}
1127
1128/// Animation command to rotate an entity around its Z axis.
1129#[derive(Clone, Copy)]
1130pub(crate) struct RotateZCommand {
1131    config: TweenConfig,
1132}
1133
1134impl EntityCommand for RotateZCommand {
1135    type Out = ();
1136    fn apply(self, mut entity: EntityWorldMut) {
1137        if let Some(base_rotation) = entity.get::<Transform>().map(|tr| tr.rotation) {
1138            let lens = TransformRotateAdditiveZLens {
1139                base_rotation,
1140                start: 0.,
1141                end: std::f32::consts::TAU,
1142            };
1143            let tween = Tween::from_config(self.config, lens)
1144                .with_repeat(RepeatCount::Infinite, RepeatStrategy::Repeat);
1145            let anim_target = AnimTarget::component::<Transform>(entity.id());
1146            entity.world_scope(|world| {
1147                world.spawn((TweenAnim::new(tween), anim_target));
1148            });
1149        }
1150    }
1151}
1152
1153impl TweenCommand for RotateZCommand {
1154    #[inline]
1155    fn config(&self) -> &TweenConfig {
1156        &self.config
1157    }
1158
1159    #[inline]
1160    fn config_mut(&mut self) -> &mut TweenConfig {
1161        &mut self.config
1162    }
1163}
1164
1165/// Animation command to rotate an entity around its X axis by a given angle.
1166#[derive(Clone, Copy)]
1167pub(crate) struct RotateXByCommand {
1168    angle: f32,
1169    config: TweenConfig,
1170}
1171
1172impl EntityCommand for RotateXByCommand {
1173    type Out = ();
1174    fn apply(self, mut entity: EntityWorldMut) {
1175        if let Some(base_rotation) = entity.get::<Transform>().map(|tr| tr.rotation) {
1176            let lens = TransformRotateAdditiveXLens {
1177                base_rotation,
1178                start: 0.,
1179                end: self.angle,
1180            };
1181            let tween = Tween::from_config(self.config, lens);
1182            let anim_target = AnimTarget::component::<Transform>(entity.id());
1183            entity.world_scope(|world| {
1184                world.spawn((TweenAnim::new(tween), anim_target));
1185            });
1186        }
1187    }
1188}
1189
1190impl TweenCommand for RotateXByCommand {
1191    #[inline]
1192    fn config(&self) -> &TweenConfig {
1193        &self.config
1194    }
1195
1196    #[inline]
1197    fn config_mut(&mut self) -> &mut TweenConfig {
1198        &mut self.config
1199    }
1200}
1201
1202/// Animation command to rotate an entity around its Y axis by a given angle.
1203#[derive(Clone, Copy)]
1204pub(crate) struct RotateYByCommand {
1205    angle: f32,
1206    config: TweenConfig,
1207}
1208
1209impl EntityCommand for RotateYByCommand {
1210    type Out = ();
1211    fn apply(self, mut entity: EntityWorldMut) {
1212        if let Some(base_rotation) = entity.get::<Transform>().map(|tr| tr.rotation) {
1213            let lens = TransformRotateAdditiveYLens {
1214                base_rotation,
1215                start: 0.,
1216                end: self.angle,
1217            };
1218            let tween = Tween::from_config(self.config, lens);
1219            let anim_target = AnimTarget::component::<Transform>(entity.id());
1220            entity.world_scope(|world| {
1221                world.spawn((TweenAnim::new(tween), anim_target));
1222            });
1223        }
1224    }
1225}
1226
1227impl TweenCommand for RotateYByCommand {
1228    #[inline]
1229    fn config(&self) -> &TweenConfig {
1230        &self.config
1231    }
1232
1233    #[inline]
1234    fn config_mut(&mut self) -> &mut TweenConfig {
1235        &mut self.config
1236    }
1237}
1238
1239/// Animation command to rotate an entity around its Z axis by a given angle.
1240#[derive(Clone, Copy)]
1241pub(crate) struct RotateZByCommand {
1242    angle: f32,
1243    config: TweenConfig,
1244}
1245
1246impl EntityCommand for RotateZByCommand {
1247    type Out = ();
1248    fn apply(self, mut entity: EntityWorldMut) {
1249        if let Some(base_rotation) = entity.get::<Transform>().map(|tr| tr.rotation) {
1250            let lens = TransformRotateAdditiveZLens {
1251                base_rotation,
1252                start: 0.,
1253                end: self.angle,
1254            };
1255            let tween = Tween::from_config(self.config, lens);
1256            let anim_target = AnimTarget::component::<Transform>(entity.id());
1257            entity.world_scope(|world| {
1258                world.spawn((TweenAnim::new(tween), anim_target));
1259            });
1260        }
1261    }
1262}
1263
1264impl TweenCommand for RotateZByCommand {
1265    #[inline]
1266    fn config(&self) -> &TweenConfig {
1267        &self.config
1268    }
1269
1270    #[inline]
1271    fn config_mut(&mut self) -> &mut TweenConfig {
1272        &mut self.config
1273    }
1274}
1275
1276/// Wrapper over an [`EntityCommands`] which stores an animation command.
1277///
1278/// The wrapper acts as, and dereferences to, a regular [`EntityCommands`] as
1279/// _e.g._ returned by [`Commands::spawn()`]. In addition, it stores a pending
1280/// animation command, which can be further tweaked before being queued into the
1281/// entity commands queue. This deferred queuing allows fluent patterns like:
1282///
1283/// ```
1284/// # use std::time::Duration;
1285/// # use bevy::prelude::*;
1286/// # use bevy_tweening::*;
1287/// # fn my_system(mut commands: Commands) {
1288/// commands
1289///     .spawn(Transform::default())
1290///     // Consume the EntityCommands, and wrap it into an AnimatedEntityCommands,
1291///     // which stores an animation command to move an entity.
1292///     .move_to(
1293///         Vec3::ONE,
1294///         Duration::from_millis(400),
1295///         EaseFunction::QuadraticIn,
1296///     )
1297///     // Tweak the stored animation to set the repeat count of the Tween.
1298///     .with_repeat_count(2);
1299/// # }
1300/// ```
1301///
1302/// The animation commands always stores the last animation inserted. When the
1303/// commands is mutably dereferenced, it first flushes the pending animation
1304/// command, if any, by inserting it into the underlying [`EntityCommands`]
1305/// queue. It also flushes the animation when dropped, to ensure the last
1306/// animation is queued too.
1307///
1308/// To move from an [`AnimatedEntityCommands`] to its underlying
1309/// [`EntityCommands`], the former automatically dereferences to the latter.
1310/// Note however that once you're back on the base [`EntityCommands`], you can
1311/// only get a new [`AnimatedEntityCommands`] via functions consuming the
1312/// [`EntityCommands`] by value. In that case, you need to call [`reborrow()`]:
1313///
1314/// ```
1315/// # use std::time::Duration;
1316/// # use bevy::prelude::*;
1317/// # use bevy_tweening::*;
1318/// # fn my_system(mut commands: Commands) {
1319/// commands
1320///     .spawn(Transform::default())
1321///     .move_to(
1322///         Vec3::ONE,
1323///         Duration::from_millis(400),
1324///         EaseFunction::QuadraticIn,
1325///     )
1326///     // This call invokes std::ops::DerefMut, and returns a mutable ref
1327///     // to the underlying EntityCommands
1328///     .insert(Name::new("my_object"))
1329///     // Here we need to reborrow() to convert from `&mut EntityCommands`
1330///     // (by mutable ref) to `EntityCommands` (by value)
1331///     .reborrow()
1332///     // This call requires an `EntityCommands` (by value)
1333///     .scale_to(
1334///         Vec3::splat(1.1),
1335///         Duration::from_millis(400),
1336///         EaseFunction::Linear,
1337///     );
1338/// # }
1339/// ```
1340///
1341/// [`reborrow()`]: bevy::prelude::EntityCommands::reborrow
1342pub struct AnimatedEntityCommands<'a, C: TweenCommand> {
1343    commands: EntityCommands<'a>,
1344    cmd: Option<C>,
1345}
1346
1347impl<'a, C: TweenCommand> AnimatedEntityCommands<'a, C> {
1348    /// Wrap an [`EntityCommands`] into an animated one.
1349    pub fn new(commands: EntityCommands<'a>, cmd: C) -> Self {
1350        Self {
1351            commands,
1352            cmd: Some(cmd),
1353        }
1354    }
1355
1356    /// Set the repeat count of this animation.
1357    #[inline]
1358    pub fn with_repeat_count(mut self, repeat_count: impl Into<RepeatCount>) -> Self {
1359        if let Some(cmd) = self.cmd.as_mut() {
1360            cmd.config_mut().repeat_count = repeat_count.into();
1361        }
1362        self
1363    }
1364
1365    /// Set the repeat strategy of this animation.
1366    #[inline]
1367    pub fn with_repeat_strategy(mut self, repeat_strategy: RepeatStrategy) -> Self {
1368        if let Some(cmd) = self.cmd.as_mut() {
1369            cmd.config_mut().repeat_strategy = repeat_strategy;
1370        }
1371        self
1372    }
1373
1374    /// Configure the repeat parameters of this animation.
1375    ///
1376    /// This is a shortcut for:
1377    ///
1378    /// ```no_run
1379    /// # use bevy_tweening::*;
1380    /// # struct AnimatedEntityCommands {}
1381    /// # impl AnimatedEntityCommands {
1382    /// # fn with_repeat_count(self, r: RepeatCount) -> Self { unimplemented!() }
1383    /// # fn with_repeat_strategy(self, r: RepeatStrategy) -> Self { unimplemented!() }
1384    /// # fn xxx(self) -> Self {
1385    /// # let repeat_count = RepeatCount::Infinite;
1386    /// # let repeat_strategy = RepeatStrategy::Repeat;
1387    /// self.with_repeat_count(repeat_count)
1388    ///     .with_repeat_strategy(repeat_strategy)
1389    /// # }}
1390    /// ```
1391    #[inline]
1392    pub fn with_repeat(
1393        self,
1394        repeat_count: impl Into<RepeatCount>,
1395        repeat_strategy: RepeatStrategy,
1396    ) -> Self {
1397        self.with_repeat_count(repeat_count)
1398            .with_repeat_strategy(repeat_strategy)
1399    }
1400
1401    /// Consume self and return the inner [`EntityCommands`].
1402    ///
1403    /// The current animation is inserted into the commands queue, before that
1404    /// wrapped commands queue is returned.
1405    pub fn into_inner(mut self) -> EntityCommands<'a> {
1406        self.flush();
1407        // Since we already flushed above, we don't need Drop. And trying to keep would
1408        // allow it to access self.commands after it was stolen (even though we know the
1409        // implementation doesn't in practice). Still, it's safer to just short-circuit
1410        // Drop here.
1411        let this = std::mem::ManuallyDrop::new(self);
1412        // SAFETY: We have flushed self.cmd which is now None, and we're stealing
1413        // self.commands, after which the this object is forgotten and never
1414        // accessed again.
1415        #[allow(unsafe_code)]
1416        unsafe {
1417            std::ptr::read(&this.commands)
1418        }
1419    }
1420
1421    /// Flush the current animation, inserting it into the commands queue.
1422    ///
1423    /// This makes it impossible to further tweak the animation. This is
1424    /// automatically called when a new animation is created and when the
1425    /// commands queue is dropped with the last animation pending.
1426    fn flush(&mut self) {
1427        if let Some(cmd) = self.cmd.take() {
1428            self.queue(cmd);
1429        }
1430    }
1431}
1432
1433impl<'a, C: TweenCommand> EntityCommandsTweeningExtensions<'a> for AnimatedEntityCommands<'a, C> {
1434    #[inline]
1435    fn move_to(
1436        self,
1437        end: Vec3,
1438        duration: Duration,
1439        ease_method: impl Into<EaseMethod>,
1440    ) -> AnimatedEntityCommands<'a, impl TweenCommand> {
1441        self.into_inner().move_to(end, duration, ease_method)
1442    }
1443
1444    #[inline]
1445    fn move_from(
1446        self,
1447        start: Vec3,
1448        duration: Duration,
1449        ease_method: impl Into<EaseMethod>,
1450    ) -> AnimatedEntityCommands<'a, impl TweenCommand> {
1451        self.into_inner().move_from(start, duration, ease_method)
1452    }
1453
1454    #[inline]
1455    fn scale_to(
1456        self,
1457        end: Vec3,
1458        duration: Duration,
1459        ease_method: impl Into<EaseMethod>,
1460    ) -> AnimatedEntityCommands<'a, impl TweenCommand> {
1461        self.into_inner().scale_to(end, duration, ease_method)
1462    }
1463
1464    #[inline]
1465    fn scale_from(
1466        self,
1467        start: Vec3,
1468        duration: Duration,
1469        ease_method: impl Into<EaseMethod>,
1470    ) -> AnimatedEntityCommands<'a, impl TweenCommand> {
1471        self.into_inner().scale_from(start, duration, ease_method)
1472    }
1473
1474    #[inline]
1475    fn rotate_x(self, cycle_duration: Duration) -> AnimatedEntityCommands<'a, impl TweenCommand> {
1476        self.into_inner().rotate_x(cycle_duration)
1477    }
1478
1479    #[inline]
1480    fn rotate_y(self, cycle_duration: Duration) -> AnimatedEntityCommands<'a, impl TweenCommand> {
1481        self.into_inner().rotate_y(cycle_duration)
1482    }
1483
1484    #[inline]
1485    fn rotate_z(self, cycle_duration: Duration) -> AnimatedEntityCommands<'a, impl TweenCommand> {
1486        self.into_inner().rotate_z(cycle_duration)
1487    }
1488
1489    #[inline]
1490    fn rotate_x_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_x_by(angle, duration, ease_method)
1497    }
1498
1499    #[inline]
1500    fn rotate_y_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_y_by(angle, duration, ease_method)
1507    }
1508
1509    #[inline]
1510    fn rotate_z_by(
1511        self,
1512        angle: f32,
1513        duration: Duration,
1514        ease_method: impl Into<EaseMethod>,
1515    ) -> AnimatedEntityCommands<'a, impl TweenCommand> {
1516        self.into_inner().rotate_z_by(angle, duration, ease_method)
1517    }
1518}
1519
1520impl<'a, C: TweenCommand> Deref for AnimatedEntityCommands<'a, C> {
1521    type Target = EntityCommands<'a>;
1522
1523    fn deref(&self) -> &Self::Target {
1524        &self.commands
1525    }
1526}
1527
1528impl<C: TweenCommand> DerefMut for AnimatedEntityCommands<'_, C> {
1529    fn deref_mut(&mut self) -> &mut Self::Target {
1530        self.flush();
1531        &mut self.commands
1532    }
1533}
1534
1535impl<C: TweenCommand> Drop for AnimatedEntityCommands<'_, C> {
1536    fn drop(&mut self) {
1537        self.flush();
1538    }
1539}
1540
1541impl<'a> EntityCommandsTweeningExtensions<'a> for EntityCommands<'a> {
1542    #[inline]
1543    fn move_to(
1544        self,
1545        end: Vec3,
1546        duration: Duration,
1547        ease_method: impl Into<EaseMethod>,
1548    ) -> AnimatedEntityCommands<'a, impl TweenCommand> {
1549        AnimatedEntityCommands::new(
1550            self,
1551            MoveToCommand {
1552                end,
1553                config: TweenConfig {
1554                    ease_method: ease_method.into(),
1555                    cycle_duration: duration,
1556                    ..default()
1557                },
1558            },
1559        )
1560    }
1561
1562    #[inline]
1563    fn move_from(
1564        self,
1565        start: Vec3,
1566        duration: Duration,
1567        ease_method: impl Into<EaseMethod>,
1568    ) -> AnimatedEntityCommands<'a, impl TweenCommand> {
1569        AnimatedEntityCommands::new(
1570            self,
1571            MoveFromCommand {
1572                start,
1573                config: TweenConfig {
1574                    ease_method: ease_method.into(),
1575                    cycle_duration: duration,
1576                    ..default()
1577                },
1578            },
1579        )
1580    }
1581
1582    #[inline]
1583    fn scale_to(
1584        self,
1585        end: Vec3,
1586        duration: Duration,
1587        ease_method: impl Into<EaseMethod>,
1588    ) -> AnimatedEntityCommands<'a, impl TweenCommand> {
1589        AnimatedEntityCommands::new(
1590            self,
1591            ScaleToCommand {
1592                end,
1593                config: TweenConfig {
1594                    ease_method: ease_method.into(),
1595                    cycle_duration: duration,
1596                    ..default()
1597                },
1598            },
1599        )
1600    }
1601
1602    #[inline]
1603    fn scale_from(
1604        self,
1605        start: Vec3,
1606        duration: Duration,
1607        ease_method: impl Into<EaseMethod>,
1608    ) -> AnimatedEntityCommands<'a, impl TweenCommand> {
1609        AnimatedEntityCommands::new(
1610            self,
1611            ScaleFromCommand {
1612                start,
1613                config: TweenConfig {
1614                    ease_method: ease_method.into(),
1615                    cycle_duration: duration,
1616                    ..default()
1617                },
1618            },
1619        )
1620    }
1621
1622    #[inline]
1623    fn rotate_x(self, cycle_duration: Duration) -> AnimatedEntityCommands<'a, impl TweenCommand> {
1624        AnimatedEntityCommands::new(
1625            self,
1626            RotateXCommand {
1627                config: TweenConfig {
1628                    ease_method: EaseFunction::Linear.into(),
1629                    cycle_duration,
1630                    ..default()
1631                },
1632            },
1633        )
1634    }
1635
1636    #[inline]
1637    fn rotate_y(self, cycle_duration: Duration) -> AnimatedEntityCommands<'a, impl TweenCommand> {
1638        AnimatedEntityCommands::new(
1639            self,
1640            RotateYCommand {
1641                config: TweenConfig {
1642                    ease_method: EaseFunction::Linear.into(),
1643                    cycle_duration,
1644                    ..default()
1645                },
1646            },
1647        )
1648    }
1649
1650    #[inline]
1651    fn rotate_z(self, cycle_duration: Duration) -> AnimatedEntityCommands<'a, impl TweenCommand> {
1652        AnimatedEntityCommands::new(
1653            self,
1654            RotateZCommand {
1655                config: TweenConfig {
1656                    ease_method: EaseFunction::Linear.into(),
1657                    cycle_duration,
1658                    ..default()
1659                },
1660            },
1661        )
1662    }
1663
1664    #[inline]
1665    fn rotate_x_by(
1666        self,
1667        angle: f32,
1668        duration: Duration,
1669        ease_method: impl Into<EaseMethod>,
1670    ) -> AnimatedEntityCommands<'a, impl TweenCommand> {
1671        AnimatedEntityCommands::new(
1672            self,
1673            RotateXByCommand {
1674                angle,
1675                config: TweenConfig {
1676                    ease_method: ease_method.into(),
1677                    cycle_duration: duration,
1678                    ..default()
1679                },
1680            },
1681        )
1682    }
1683
1684    #[inline]
1685    fn rotate_y_by(
1686        self,
1687        angle: f32,
1688        duration: Duration,
1689        ease_method: impl Into<EaseMethod>,
1690    ) -> AnimatedEntityCommands<'a, impl TweenCommand> {
1691        AnimatedEntityCommands::new(
1692            self,
1693            RotateYByCommand {
1694                angle,
1695                config: TweenConfig {
1696                    ease_method: ease_method.into(),
1697                    cycle_duration: duration,
1698                    ..default()
1699                },
1700            },
1701        )
1702    }
1703
1704    #[inline]
1705    fn rotate_z_by(
1706        self,
1707        angle: f32,
1708        duration: Duration,
1709        ease_method: impl Into<EaseMethod>,
1710    ) -> AnimatedEntityCommands<'a, impl TweenCommand> {
1711        AnimatedEntityCommands::new(
1712            self,
1713            RotateZByCommand {
1714                angle,
1715                config: TweenConfig {
1716                    ease_method: ease_method.into(),
1717                    cycle_duration: duration,
1718                    ..default()
1719                },
1720            },
1721        )
1722    }
1723}
1724
1725/// Event raised when a [`TweenAnim`] completed.
1726#[derive(Debug, Clone, Copy, EntityEvent, Message)]
1727pub struct AnimCompletedEvent {
1728    /// The entity owning the [`TweenAnim`] which completed.
1729    ///
1730    /// Note that commonly the [`TweenAnim`] is despawned on completion, so
1731    /// can't be queried anymore with this entity. You can prevent a completed
1732    /// animation from being automatically destroyed by
1733    /// setting [`TweenAnim::destroy_on_completion`] to `false`.
1734    #[event_target]
1735    pub anim_entity: Entity,
1736    /// The animation target.
1737    ///
1738    /// This is provided both as a convenience for [`TweenAnim`]s not destroyed
1739    /// on completion, and because for those animations which are destroyed
1740    /// on completion the information is not available anymore when this
1741    /// event is received.
1742    pub target: AnimTargetKind,
1743}
1744
1745/// Errors returned by various animation functions.
1746#[derive(Debug, Error, Clone, Copy)]
1747pub enum TweeningError {
1748    /// The asset resolver for the given asset is not registered.
1749    #[error("Asset resolver for asset with resource ID {0:?} is not registered.")]
1750    AssetResolverNotRegistered(ComponentId),
1751    /// The entity was not found in the World.
1752    #[error("Entity {0:?} not found in the World.")]
1753    EntityNotFound(Entity),
1754    /// The entity should have had a TweenAnim but it was not found.
1755    #[error("Entity {0:?} doesn't have a TweenAnim.")]
1756    MissingTweenAnim(Entity),
1757    /// The component of the given type is not registered.
1758    #[error("Component of type {0:?} is not registered in the World.")]
1759    ComponentNotRegistered(TypeId),
1760    /// The resource of the given type is not registered.
1761    #[error("Resource of type {0:?} is not registered in the World.")]
1762    ResourceNotRegistered(TypeId),
1763    /// The asset container for the given asset type is not registered.
1764    #[error("Asset container Assets<A> for asset type A = {0:?} is not registered in the World.")]
1765    AssetNotRegistered(TypeId),
1766    /// The component of the given type is not registered.
1767    #[error("Component of type {0:?} is not present on entity {1:?}.")]
1768    MissingComponent(TypeId, Entity),
1769    /// The asset cannot be found.
1770    #[error("Asset ID {0:?} is invalid.")]
1771    InvalidAssetId(UntypedAssetId),
1772    /// The asset ID references a different type than expected.
1773    #[error("Expected type of asset ID to be {expected:?} but got {actual:?} instead.")]
1774    InvalidAssetIdType {
1775        /// The expected asset type.
1776        expected: TypeId,
1777        /// The actual type the asset ID references.
1778        actual: TypeId,
1779    },
1780    /// Expected [`Tweenable::target_type_id()`] to return a value, but it
1781    /// returned `None`.
1782    #[error("Expected a typed Tweenable.")]
1783    UntypedTweenable,
1784    /// Invalid [`Entity`].
1785    #[error("Invalid Entity {0:?}.")]
1786    InvalidTweenId(Entity),
1787    /// Cannot change target kind.
1788    #[error("Unexpected target kind: was component={0}, now component={1}")]
1789    MismatchingTargetKind(bool, bool),
1790    /// Cannot change component type.
1791    #[error("Cannot change component type: was component_id={0:?}, now component_id={1:?}")]
1792    MismatchingComponentId(ComponentId, ComponentId),
1793    /// Cannot change asset type.
1794    #[error("Cannot change asset type: was component_id={0:?}, now component_id={1:?}")]
1795    MismatchingAssetResourceId(ComponentId, ComponentId),
1796}
1797
1798type RegisterAction = dyn Fn(&Components, &mut TweenResolver) + Send + Sync + 'static;
1799
1800/// Enumeration of the types of animation targets.
1801///
1802/// This type holds the minimum amount of data to reference ananimation target,
1803/// aside from the actual type of the target.
1804#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
1805pub enum AnimTargetKind {
1806    /// Component animation target.
1807    Component {
1808        /// The entity owning the component instance.
1809        entity: Entity,
1810    },
1811    /// Resource animation target.
1812    Resource,
1813    /// Asset animation target.
1814    Asset {
1815        /// The asset ID inside the [`Assets`] collection.
1816        asset_id: UntypedAssetId,
1817        /// Type ID of the [`Assets`] collection itself.
1818        assets_type_id: TypeId,
1819    },
1820}
1821
1822/// Component defining the target of an animation.
1823///
1824/// References an object used as the target of the animation stored in the
1825/// [`TweenAnim`] component on the same entity.
1826#[derive(Component)]
1827pub struct AnimTarget {
1828    /// Target kind and additional data to identify it.
1829    pub kind: AnimTargetKind,
1830
1831    /// Self-registering action for assets and resources.
1832    pub(crate) register_action: Option<Box<RegisterAction>>,
1833}
1834
1835impl AnimTarget {
1836    /// Create a target mutating a component on the given entity.
1837    pub fn component<C: Component<Mutability = Mutable>>(entity: Entity) -> Self {
1838        Self {
1839            kind: AnimTargetKind::Component { entity },
1840            // Components have a complete typeless API, don't need any extra registration for type
1841            // erasure.
1842            register_action: None,
1843        }
1844    }
1845
1846    /// Create a target mutating the given resource.
1847    pub fn resource<R: Resource>() -> Self {
1848        let register_action = |components: &Components, resolver: &mut TweenResolver| {
1849            resolver.register_resource_resolver_for::<R>(components);
1850        };
1851        Self {
1852            kind: AnimTargetKind::Resource,
1853            register_action: Some(Box::new(register_action)),
1854        }
1855    }
1856
1857    /// Create a target mutating the given asset.
1858    ///
1859    /// The asset is identified by its type, and its [`AssetId`].
1860    pub fn asset<A: Asset>(asset_id: impl Into<AssetId<A>>) -> Self {
1861        let register_action = |components: &Components, resolver: &mut TweenResolver| {
1862            resolver.register_asset_resolver_for::<A>(components);
1863        };
1864        Self {
1865            kind: AnimTargetKind::Asset {
1866                asset_id: asset_id.into().untyped(),
1867                assets_type_id: TypeId::of::<Assets<A>>(),
1868            },
1869            register_action: Some(Box::new(register_action)),
1870        }
1871    }
1872
1873    /// Register any resolver for this target.
1874    pub(crate) fn register(&self, components: &Components, resolver: &mut TweenResolver) {
1875        if let Some(register_action) = self.register_action.as_ref() {
1876            register_action(components, resolver);
1877        }
1878    }
1879}
1880
1881/// Animation controller instance.
1882///
1883/// The [`TweenAnim`] represents a single animation instance for a single
1884/// target (component or resource or asset). Each instance is independent, even
1885/// if it mutates the same target as another instance. Spawning this component
1886/// adds an active animation, and destroying it stops that animation. The
1887/// component can also be used to control the animation playback at runtime,
1888/// like the playback speed.
1889///
1890/// The target is described by the [`AnimTarget`] component. If that component
1891/// is absent, then the animation implicitly targets a component on the current
1892/// Entity. The type of the component is derived from the type that the [`Lens`]
1893/// animates.
1894///
1895/// _If you're looking for the basic tweenable animation description, see
1896/// [`Tween`] instead._
1897///
1898/// # Example
1899///
1900/// ```
1901/// # use bevy::prelude::*;
1902/// # use bevy_tweening::*;
1903/// # fn make_tweenable<T>() -> Tween { unimplemented!() }
1904/// fn my_system(mut commands: Commands) {
1905///     let tweenable = make_tweenable::<Transform>();
1906///     let id1 = commands
1907///         .spawn((
1908///             Transform::default(),
1909///             // Implicitly targets the current entity's Transform
1910///             TweenAnim::new(tweenable),
1911///         ))
1912///         .id();
1913///
1914///     let tweenable2 = make_tweenable::<Transform>();
1915///     commands.spawn((
1916///         TweenAnim::new(tweenable2),
1917///         // Explicitly targets the Transform component of entity 'id1'
1918///         AnimTarget::component::<Transform>(id1),
1919///     ));
1920/// }
1921/// ```
1922#[derive(Component)]
1923pub struct TweenAnim {
1924    /// The animation itself. Note that the tweenable is stateful, so can't be
1925    /// shared with another [`TweenAnim`] instance.
1926    tweenable: BoxedTweenable,
1927    /// Control if the animation is played or not. Defaults to
1928    /// [`PlaybackState::Playing`].
1929    ///
1930    /// Pausing an animation with [`PlaybackState::Paused`] is functionaly
1931    /// equivalent to setting its [`speed`] to zero. The two fields remain
1932    /// independent though, for convenience.
1933    ///
1934    /// [`speed`]: Self::speed
1935    pub playback_state: PlaybackState,
1936    /// Relative playback speed. Defaults to `1.` (normal speed; 100%).
1937    ///
1938    /// Setting a negative or zero speed value effectively pauses the animation
1939    /// (although the [`playback_state`] remains unchanged). Negative values may
1940    /// be clamped to 0. when the animation is stepped, but positive or zero
1941    /// values are never modified by the library.
1942    ///
1943    /// # Time precision
1944    ///
1945    /// _This note is an implementation detail which can usually be ignored._
1946    ///
1947    /// Despite the use of `f64`, setting a playback speed different from `1.`
1948    /// (100% speed) may produce small inaccuracies in durations, especially
1949    /// for longer animations. However those are often negligible.
1950    /// This is due to the very large precision of `Duration` (typically 96
1951    /// bits or more), even compared to `f64` (64 bits), and the fact this speed
1952    /// factor is a multiplier whereas most other time quantities are added or
1953    /// subtracted.
1954    ///
1955    /// [`playback_state`]: Self::playback_state
1956    pub speed: f64,
1957    /// Destroy the animation once completed. This defaults to `true`, and makes
1958    /// the stepping functions like [`TweenAnim::step_all()`] destroy this
1959    /// animation once it completed. To keep the animation queued, and allow
1960    /// access after it completed, set this to `false`. Note however that
1961    /// you should avoid leaving all animations queued if they're unused, as
1962    /// this wastes memory and may degrade performances if too many
1963    /// completed animations are kept around for no good reason.
1964    pub destroy_on_completion: bool,
1965    /// Current tweening completion state.
1966    tween_state: TweenState,
1967}
1968
1969impl TweenAnim {
1970    /// Create a new tween animation.
1971    ///
1972    /// This component represents the runtime animation being played to mutate a
1973    /// specific target.
1974    ///
1975    /// # Panics
1976    ///
1977    /// Panics if the tweenable is "typeless", that is
1978    /// [`Tweenable::target_type_id()`] returns `None`. Animations must
1979    /// target a concrete component or asset type. This means in particular
1980    /// that you can't use a single [`Delay`] alone. You can however use a
1981    /// [`Delay`] or other typeless tweenables as part of a [`Sequence`],
1982    /// provided there's at least one other typed tweenable in the sequence
1983    /// to make it typed too.
1984    #[inline]
1985    pub fn new(tweenable: impl IntoBoxedTweenable) -> Self {
1986        let tweenable = tweenable.into_boxed();
1987        assert!(
1988            tweenable.target_type_id().is_some(),
1989            "The top-level Tweenable of a TweenAnim must be typed (Tweenable::target_type_id() returns Some)."
1990        );
1991        Self {
1992            tweenable,
1993            playback_state: PlaybackState::Playing,
1994            speed: 1.,
1995            destroy_on_completion: true,
1996            tween_state: TweenState::Active,
1997        }
1998    }
1999
2000    /// Configure the playback speed.
2001    pub fn with_speed(mut self, speed: f64) -> Self {
2002        self.speed = speed;
2003        self
2004    }
2005
2006    /// Enable or disable destroying this component on animation completion.
2007    ///
2008    /// If enabled, the component is automatically removed from its `Entity`
2009    /// when the animation completed.
2010    pub fn with_destroy_on_completed(mut self, destroy_on_completed: bool) -> Self {
2011        self.destroy_on_completion = destroy_on_completed;
2012        self
2013    }
2014
2015    /// Step a single animation.
2016    ///
2017    /// _The [`step_all()`] function is called automatically by the animation
2018    /// system registered by the [`TweeningPlugin`], you generally don't
2019    /// need to call this one._
2020    ///
2021    /// This is a shortcut for `step_many(world, delta_time, [entity])`, with
2022    /// the added benefit that it returns some error if the entity is not valid.
2023    /// See [`step_many()`] for details.
2024    ///
2025    /// # Example
2026    ///
2027    /// ```
2028    /// # use std::time::Duration;
2029    /// # use bevy::prelude::*;
2030    /// # use bevy_tweening::*;
2031    /// # fn make_tweenable() -> Tween { unimplemented!() }
2032    /// #[derive(Component)]
2033    /// struct MyMarker;
2034    ///
2035    /// fn my_system(world: &mut World) -> Result<()> {
2036    ///     let mut q_anims = world.query_filtered::<Entity, (With<TweenAnim>, With<MyMarker>)>();
2037    ///     let entity = q_anims.single(world)?;
2038    ///     let delta_time = Duration::from_millis(200);
2039    ///     TweenAnim::step_one(world, delta_time, entity);
2040    ///     Ok(())
2041    /// }
2042    /// ```
2043    ///
2044    /// # Returns
2045    ///
2046    /// This returns an error if the entity is not found or doesn't own a
2047    /// [`TweenAnim`] component.
2048    ///
2049    /// [`step_all()`]: Self::step_all
2050    /// [`step_many()`]: Self::step_many
2051    #[inline]
2052    pub fn step_one(
2053        world: &mut World,
2054        delta_time: Duration,
2055        entity: Entity,
2056    ) -> Result<(), TweeningError> {
2057        let num = Self::step_many(world, delta_time, &[entity]);
2058        if num > 0 {
2059            Ok(())
2060        } else {
2061            Err(TweeningError::EntityNotFound(entity))
2062        }
2063    }
2064
2065    /// Step some animation(s).
2066    ///
2067    /// _The [`step_all()`] function is called automatically by the animation
2068    /// system registered by the [`TweeningPlugin`], you generally don't
2069    /// need to call this one._
2070    ///
2071    /// Step the given animation(s) by a given `delta_time`, which may be
2072    /// [`Duration::ZERO`]. Passing a zero delta time may be useful to force the
2073    /// current animation state to be applied to a target, in case you made
2074    /// change which do not automatically do so (for example, retargeting an
2075    /// animation). The `anims` are the entities which own a [`TweenAnim`]
2076    /// component to step; any entity without a [`TweenAnim`] component is
2077    /// silently ignored.
2078    ///
2079    /// The function doesn't check that all input entities are unique. If an
2080    /// entity is duplicated in `anims`, the behavior is undefined, including
2081    /// (but not guaranteed) stepping the animation multiple times. You're
2082    /// responsible for ensuring the input entity slice contains distinct
2083    /// entities.
2084    ///
2085    /// # Example
2086    ///
2087    /// ```
2088    /// # use std::time::Duration;
2089    /// # use bevy::prelude::*;
2090    /// # use bevy_tweening::*;
2091    /// # fn make_tweenable() -> Tween { unimplemented!() }
2092    /// #[derive(Component)]
2093    /// struct MyMarker;
2094    ///
2095    /// fn my_system(world: &mut World) -> Result<()> {
2096    ///     let mut q_anims = world.query_filtered::<Entity, (With<TweenAnim>, With<MyMarker>)>();
2097    ///     let entities = q_anims.iter(world).collect::<Vec<Entity>>();
2098    ///     let delta_time = Duration::from_millis(200);
2099    ///     TweenAnim::step_many(world, delta_time, &entities[..]);
2100    ///     Ok(())
2101    /// }
2102    /// ```
2103    ///
2104    /// # Returns
2105    ///
2106    /// Returns the number of [`TweenAnim`] component found and stepped, which
2107    /// is always less than or equal to the input `anims` slice length.
2108    ///
2109    /// [`step_all()`]: Self::step_all
2110    pub fn step_many(world: &mut World, delta_time: Duration, anims: &[Entity]) -> usize {
2111        let mut targets = vec![];
2112        world.resource_scope(|world, mut resolver: Mut<TweenResolver>| {
2113            let mut q_anims = world.query::<(Entity, &TweenAnim, Option<&AnimTarget>)>();
2114            targets.reserve(anims.len());
2115            for entity in anims {
2116                if let Ok((entity, anim, maybe_target)) = q_anims.get(world, *entity) {
2117                    // Lazy registration with resolver if needed
2118                    if let Some(anim_target) = maybe_target {
2119                        anim_target.register(world.components(), &mut resolver);
2120                    }
2121
2122                    // Actually step the tweenable and update the target
2123                    if let Ok((target_type_id, component_id, target, is_retargetable)) =
2124                        Self::resolve_target(
2125                            world.components(),
2126                            maybe_target,
2127                            entity,
2128                            anim.tweenable(),
2129                        )
2130                    {
2131                        targets.push((
2132                            entity,
2133                            target_type_id,
2134                            component_id,
2135                            target,
2136                            is_retargetable,
2137                        ));
2138                    }
2139                }
2140            }
2141        });
2142        Self::step_impl(world, delta_time, &targets[..]);
2143        targets.len()
2144    }
2145
2146    /// Step all animations on the given world.
2147    ///
2148    /// _This function is called automatically by the animation system
2149    /// registered by the [`TweeningPlugin`], you generally don't need to call
2150    /// it._
2151    ///
2152    /// Step all the [`TweenAnim`] components of the input world by a given
2153    /// `delta_time`, which may be [`Duration::ZERO`]. Passing a zero delta
2154    /// time may be useful to force the current animation state to be
2155    /// applied to a target, in case you made change which do not
2156    /// automatically do so (for example, retargeting an animation).
2157    pub fn step_all(world: &mut World, delta_time: Duration) {
2158        let targets = world.resource_scope(|world, mut resolver: Mut<TweenResolver>| {
2159            let mut q_anims = world.query::<(Entity, &TweenAnim, Option<&AnimTarget>)>();
2160            q_anims
2161                .iter(world)
2162                .filter_map(|(entity, anim, maybe_target)| {
2163                    // Lazy registration with resolver if needed
2164                    if let Some(anim_target) = maybe_target {
2165                        anim_target.register(world.components(), &mut resolver);
2166                    }
2167
2168                    // Actually step the tweenable and update the target
2169                    match Self::resolve_target(
2170                        world.components(),
2171                        maybe_target,
2172                        entity,
2173                        anim.tweenable(),
2174                    ) {
2175                        Ok((target_type_id, component_id, target, is_retargetable)) => Some((
2176                            entity,
2177                            target_type_id,
2178                            component_id,
2179                            target,
2180                            is_retargetable,
2181                        )),
2182                        Err(err) => {
2183                            bevy::log::error!(
2184                                "Error while stepping TweenAnim on entity {:?}: {:?}",
2185                                entity,
2186                                err
2187                            );
2188                            None
2189                        }
2190                    }
2191                })
2192                .collect::<Vec<_>>()
2193        });
2194        Self::step_impl(world, delta_time, &targets[..]);
2195    }
2196
2197    fn resolve_target(
2198        components: &Components,
2199        maybe_target: Option<&AnimTarget>,
2200        anim_entity: Entity,
2201        tweenable: &dyn Tweenable,
2202    ) -> Result<(TypeId, ComponentId, AnimTargetKind, bool), TweeningError> {
2203        let type_id = tweenable
2204            .target_type_id()
2205            .ok_or(TweeningError::UntypedTweenable)?;
2206        if let Some(target) = maybe_target {
2207            // Target explicitly specified with AnimTarget component
2208            let component_id = match &target.kind {
2209                AnimTargetKind::Component { .. } => components
2210                    .get_id(type_id)
2211                    .ok_or(TweeningError::ComponentNotRegistered(type_id))?,
2212                AnimTargetKind::Resource => components
2213                    .get_id(type_id)
2214                    .ok_or(TweeningError::ResourceNotRegistered(type_id))?,
2215                AnimTargetKind::Asset { assets_type_id, .. } => components
2216                    .get_id(*assets_type_id)
2217                    .ok_or(TweeningError::AssetNotRegistered(type_id))?,
2218            };
2219            let is_retargetable = false; // explicit target
2220            Ok((type_id, component_id, target.kind, is_retargetable))
2221        } else {
2222            // Target implicitly self; this can only be a component target
2223            let is_retargetable = true;
2224            if let Some(component_id) = components.get_id(type_id) {
2225                Ok((
2226                    type_id,
2227                    component_id,
2228                    AnimTargetKind::Component {
2229                        entity: anim_entity,
2230                    },
2231                    is_retargetable,
2232                ))
2233            } else {
2234                // We can't implicitly target an asset without its AssetId
2235                Err(TweeningError::ComponentNotRegistered(type_id))
2236            }
2237        }
2238    }
2239
2240    fn step_impl(
2241        world: &mut World,
2242        delta_time: Duration,
2243        anims: &[(Entity, TypeId, ComponentId, AnimTargetKind, bool)],
2244    ) {
2245        let mut to_remove = Vec::with_capacity(anims.len());
2246        world.resource_scope(|world, resolver: Mut<TweenResolver>| {
2247            world.resource_scope(
2248                |world, mut cycle_events: Mut<Messages<CycleCompletedEvent>>| {
2249                    world.resource_scope(
2250                        |world, mut anim_events: Mut<Messages<AnimCompletedEvent>>| {
2251                            let anim_comp_id = world.component_id::<TweenAnim>().unwrap();
2252                            for (
2253                                anim_entity,
2254                                target_type_id,
2255                                component_id,
2256                                anim_target,
2257                                is_retargetable,
2258                            ) in anims
2259                            {
2260                                let retain = match anim_target {
2261                                    AnimTargetKind::Component {
2262                                        entity: comp_entity,
2263                                    } => {
2264                                        let (mut entities, commands) =
2265                                            world.entities_and_commands();
2266                                        let ret = if *anim_entity == *comp_entity {
2267                                            // The TweenAnim animates another component on the same
2268                                            // entity
2269                                            let Ok([mut ent]) = entities.get_mut([*anim_entity])
2270                                            else {
2271                                                continue;
2272                                            };
2273                                            let Ok([anim, target]) =
2274                                                ent.get_mut_by_id([anim_comp_id, *component_id])
2275                                            else {
2276                                                continue;
2277                                            };
2278                                            // SAFETY: We fetched the EntityMut from the component
2279                                            // ID of
2280                                            // TweenAnim
2281                                            #[allow(unsafe_code)]
2282                                            let mut anim = unsafe { anim.with_type::<TweenAnim>() };
2283                                            anim.step_self(
2284                                                commands,
2285                                                *anim_entity,
2286                                                delta_time,
2287                                                anim_target,
2288                                                target,
2289                                                target_type_id,
2290                                                cycle_events.reborrow(),
2291                                                anim_events.reborrow(),
2292                                            )
2293                                        } else {
2294                                            // The TweenAnim animates a component on a different
2295                                            // entity
2296                                            let Ok([mut anim, mut target]) =
2297                                                entities.get_mut([*anim_entity, *comp_entity])
2298                                            else {
2299                                                continue;
2300                                            };
2301                                            let Some(mut anim) = anim.get_mut::<TweenAnim>() else {
2302                                                continue;
2303                                            };
2304                                            let Ok(target) = target.get_mut_by_id(*component_id)
2305                                            else {
2306                                                continue;
2307                                            };
2308                                            anim.step_self(
2309                                                commands,
2310                                                *anim_entity,
2311                                                delta_time,
2312                                                anim_target,
2313                                                target,
2314                                                target_type_id,
2315                                                cycle_events.reborrow(),
2316                                                anim_events.reborrow(),
2317                                            )
2318                                        };
2319                                        match ret {
2320                                            Ok(res) => {
2321                                                if res.needs_retarget {
2322                                                    assert!(res.retain);
2323                                                    if *is_retargetable {
2324                                                        //to_retarget.push(anim_entity);
2325                                                        //true
2326                                                        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);
2327                                                        false
2328                                                    } else {
2329                                                        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);
2330                                                        false
2331                                                    }
2332                                                } else {
2333                                                    res.retain
2334                                                }
2335                                            }
2336                                            Err(_) => false,
2337                                        }
2338                                    }
2339                                    AnimTargetKind::Resource => resolver
2340                                        .resolve_resource(
2341                                            world,
2342                                            target_type_id,
2343                                            *component_id,
2344                                            *anim_entity,
2345                                            delta_time,
2346                                            cycle_events.reborrow(),
2347                                            anim_events.reborrow(),
2348                                        )
2349                                        .unwrap_or_else(|err| {
2350                                            bevy::log::error!(
2351                                                "Deleting resource animation due to error: {err:?}"
2352                                            );
2353                                            false
2354                                        }),
2355                                    AnimTargetKind::Asset { asset_id, .. } => resolver
2356                                        .resolve_asset(
2357                                            world,
2358                                            target_type_id,
2359                                            *component_id,
2360                                            *asset_id,
2361                                            *anim_entity,
2362                                            delta_time,
2363                                            cycle_events.reborrow(),
2364                                            anim_events.reborrow(),
2365                                        )
2366                                        .unwrap_or_else(|err| {
2367                                            bevy::log::error!(
2368                                                "Deleting asset animation due to error: {err:?}"
2369                                            );
2370                                            false
2371                                        }),
2372                                };
2373
2374                                if !retain {
2375                                    to_remove.push(*anim_entity);
2376                                }
2377                            }
2378                        },
2379                    );
2380                },
2381            );
2382        });
2383
2384        let mut cmds = world.commands();
2385        for entity in to_remove.drain(..) {
2386            cmds.entity(entity).try_remove::<TweenAnim>();
2387        }
2388
2389        world.flush();
2390    }
2391
2392    #[allow(clippy::too_many_arguments)]
2393    fn step_self(
2394        &mut self,
2395        mut commands: Commands,
2396        anim_entity: Entity,
2397        delta_time: Duration,
2398        target_kind: &AnimTargetKind,
2399        mut mut_untyped: MutUntyped,
2400        target_type_id: &TypeId,
2401        mut cycle_events: Mut<Messages<CycleCompletedEvent>>,
2402        mut anim_events: Mut<Messages<AnimCompletedEvent>>,
2403    ) -> Result<StepResult, TweeningError> {
2404        let mut completed_events = Vec::with_capacity(8);
2405
2406        // Sanity checks on fields which can be freely modified by the user
2407        self.speed = self.speed.max(0.);
2408
2409        // Retain completed animations only if requested
2410        if self.tween_state == TweenState::Completed {
2411            let ret = StepResult {
2412                retain: !self.destroy_on_completion,
2413                needs_retarget: false,
2414            };
2415            return Ok(ret);
2416        }
2417
2418        // Skip paused animations (but retain them)
2419        if self.playback_state == PlaybackState::Paused || self.speed <= 0. {
2420            let ret = StepResult {
2421                retain: true,
2422                needs_retarget: false,
2423            };
2424            return Ok(ret);
2425        }
2426
2427        // Scale delta time by this animation's speed. Reject negative speeds; use
2428        // backward playback to play in reverse direction.
2429        // Note: must use f64 for precision; f32 produces visible roundings.
2430        let delta_time = delta_time.mul_f64(self.speed);
2431
2432        // Step the tweenable animation
2433        let mut notify_completed = || {
2434            completed_events.push(CycleCompletedEvent {
2435                anim_entity,
2436                target: *target_kind,
2437            });
2438        };
2439        let (state, needs_retarget) = self.tweenable.step(
2440            anim_entity,
2441            delta_time,
2442            mut_untyped.reborrow(),
2443            target_type_id,
2444            &mut notify_completed,
2445        );
2446        self.tween_state = state;
2447
2448        // Send tween completed events once we reclaimed mut access to world and can get
2449        // a Commands.
2450        if !completed_events.is_empty() {
2451            for event in completed_events.drain(..) {
2452                // Send buffered event
2453                cycle_events.write(event);
2454
2455                // Trigger all entity-scoped observers
2456                commands.trigger(CycleCompletedEvent {
2457                    anim_entity,
2458                    ..event
2459                });
2460            }
2461        }
2462
2463        // Raise animation completed event
2464        if state == TweenState::Completed {
2465            let event: AnimCompletedEvent = AnimCompletedEvent {
2466                anim_entity,
2467                target: *target_kind,
2468            };
2469
2470            // Send buffered event
2471            anim_events.write(event);
2472
2473            // Trigger all entity-scoped observers
2474            commands.trigger(event);
2475        }
2476
2477        let ret = StepResult {
2478            retain: state == TweenState::Active || !self.destroy_on_completion,
2479            needs_retarget,
2480        };
2481        Ok(ret)
2482    }
2483
2484    /// Stop animation playback and rewind the animation.
2485    ///
2486    /// This changes the animator state to [`PlaybackState::Paused`] and rewinds
2487    /// its tweenable.
2488    ///
2489    /// # Panics
2490    ///
2491    /// Like [`Tweenable::rewind()`], this panics if the current playback
2492    /// direction is [`PlaybackDirection::Backward`] and the animation is
2493    /// infinitely repeating.
2494    pub fn stop(&mut self) {
2495        self.playback_state = PlaybackState::Paused;
2496        self.tweenable.rewind();
2497        self.tween_state = TweenState::Active;
2498    }
2499
2500    /// Get the tweenable describing this animation.
2501    ///
2502    /// To change the tweenable, use [`TweenAnim::set_tweenable()`].
2503    #[inline]
2504    pub fn tweenable(&self) -> &dyn Tweenable {
2505        self.tweenable.as_ref()
2506    }
2507
2508    /// Set a new animation description.
2509    ///
2510    /// Attempt to change the tweenable of an animation already spawned.
2511    ///
2512    /// If the tweenable is successfully swapped, this resets the
2513    /// [`tween_state()`] to [`TweenState::Active`], even if the tweenable would
2514    /// otherwise be completed _e.g._ because its current elapsed time is past
2515    /// its total duration. Conversely, this doesn't update the target
2516    /// component or asset, as this function doesn't have mutable access to
2517    /// it. To force applying the new state to the target without stepping the
2518    /// animation forward or backward, call one of the stepping functions like
2519    /// [`TweenAnim::step_one()`] passing a delta time of [`Duration::ZERO`].
2520    ///
2521    /// To ensure the old and new animations have the same elapsed time (for
2522    /// example if they need to be synchronized, if they're variants of each
2523    /// other), call [`set_elapsed()`] first on the input `tweenable`, with
2524    /// the duration value of the old tweenable returned by [`elapsed()`].
2525    ///
2526    /// ```
2527    /// # use std::time::Duration;
2528    /// # use bevy::prelude::*;
2529    /// # use bevy_tweening::*;
2530    /// # fn make_tweenable() -> Tween { unimplemented!() }
2531    /// fn my_system(mut anim: Single<&mut TweenAnim>) {
2532    ///     let mut tweenable = make_tweenable();
2533    ///     let elapsed = anim.tweenable().elapsed();
2534    ///     tweenable.set_elapsed(elapsed);
2535    ///     anim.set_tweenable(tweenable);
2536    /// }
2537    /// ```
2538    ///
2539    /// # Returns
2540    ///
2541    /// On success, returns the previous tweenable which has been swapped out.
2542    ///
2543    /// [`tween_state()`]: Self::tween_state
2544    /// [`set_elapsed()`]: crate::Tweenable::set_elapsed
2545    /// [`elapsed()`]: crate::Tweenable::elapsed
2546    /// [`step_one()`]: Self::step_one
2547    pub fn set_tweenable<T>(&mut self, tweenable: T) -> Result<BoxedTweenable, TweeningError>
2548    where
2549        T: Tweenable + 'static,
2550    {
2551        let mut old_tweenable: BoxedTweenable = Box::new(tweenable);
2552        std::mem::swap(&mut self.tweenable, &mut old_tweenable);
2553        // Reset tweening state, the new tweenable is at t=0
2554        self.tween_state = TweenState::Active;
2555        Ok(old_tweenable)
2556    }
2557
2558    /// Get the tweening completion state.
2559    ///
2560    /// In general this is [`TweenState::Active`], unless the animation
2561    /// completed and [`destroy_on_completion`] is `false`.
2562    ///
2563    /// [`destroy_on_completion`]: Self::destroy_on_completion
2564    #[inline]
2565    pub fn tween_state(&self) -> TweenState {
2566        self.tween_state
2567    }
2568}
2569
2570type ResourceResolver = Box<
2571    dyn for<'w> Fn(
2572            &mut World,
2573            Entity,
2574            &TypeId,
2575            Duration,
2576            Mut<Messages<CycleCompletedEvent>>,
2577            Mut<Messages<AnimCompletedEvent>>,
2578        ) -> Result<bool, TweeningError>
2579        + Send
2580        + Sync
2581        + 'static,
2582>;
2583
2584type AssetResolver = Box<
2585    dyn for<'w> Fn(
2586            &mut World,
2587            UntypedAssetId,
2588            Entity,
2589            &TypeId,
2590            Duration,
2591            Mut<Messages<CycleCompletedEvent>>,
2592            Mut<Messages<AnimCompletedEvent>>,
2593        ) -> Result<bool, TweeningError>
2594        + Send
2595        + Sync
2596        + 'static,
2597>;
2598
2599/// Resolver for resources and assets.
2600///
2601/// _This resource is largely an implementation detail. You can safely ignore
2602/// it._
2603///
2604/// Bevy doesn't provide a suitable untyped API to access resources and assets
2605/// at runtime without knowing their compile-time type.
2606/// - For resources, most of the API is in place, but unfortunately there's no
2607///   `World::resource_scope_untyped()` to temporarily extract a resource by ID
2608///   to allow concurrent mutability of the resource with other parts of the
2609///   [`World`], in particular the animation target.
2610/// - For assets, there's simply no untyped API. [`Assets`] doesn't allow
2611///   untyped asset access.
2612///
2613/// To work around those limitations, this resolver resource contains
2614/// type-erased closures allowing to resolve an animation target definition into
2615/// a mutable pointer [`MutUntyped`] to that instance, to allow the animation
2616/// engine to apply the animation on it.
2617#[derive(Default, Resource)]
2618pub struct TweenResolver {
2619    /// Resource resolver allowing to call `World::resource_scope()` to extract
2620    /// that resource type form the `World` while in parallel accessing mutably
2621    /// the animation entity itself.
2622    resource_resolver: HashMap<ComponentId, ResourceResolver>,
2623    /// Asset resolver allowing to convert a pair of { untyped pointer to
2624    /// `Assets<A>`, untyped `AssetId` } into an untyped pointer to the asset A
2625    /// itself. This is necessary because there's no UntypedAssets interface in
2626    /// Bevy. The TypeId key must be the type of the `Assets<A>` type itself.
2627    /// The resolver is allowed to fail (return `None`), for example when the
2628    /// asset ID doesn't reference a valid asset.
2629    asset_resolver: HashMap<ComponentId, AssetResolver>,
2630}
2631
2632impl TweenResolver {
2633    /// Register a resolver for the given resource type.
2634    pub(crate) fn register_resource_resolver_for<R: Resource>(&mut self, components: &Components) {
2635        let resource_id = components.component_id::<R>().unwrap();
2636        let resolver = |world: &mut World,
2637                        entity: Entity,
2638                        target_type_id: &TypeId,
2639                        delta_time: Duration,
2640                        mut cycle_events: Mut<Messages<CycleCompletedEvent>>,
2641                        mut anim_events: Mut<Messages<AnimCompletedEvent>>|
2642         -> Result<bool, TweeningError> {
2643            // First, remove the resource R from the world so we can access it mutably in
2644            // parallel of the TweenAnim
2645            world.resource_scope(|world, resource: Mut<R>| {
2646                let target = AnimTargetKind::Resource;
2647
2648                let (mut entities, commands) = world.entities_and_commands();
2649
2650                // Resolve the TweenAnim component
2651                let Ok([mut ent]) = entities.get_mut([entity]) else {
2652                    return Err(TweeningError::EntityNotFound(entity));
2653                };
2654                let Some(mut anim) = ent.get_mut::<TweenAnim>() else {
2655                    return Err(TweeningError::MissingTweenAnim(ent.id()));
2656                };
2657
2658                // Finally, step the TweenAnim and mutate the target
2659                let ret = anim.step_self(
2660                    commands,
2661                    entity,
2662                    delta_time,
2663                    &target,
2664                    resource.into(),
2665                    target_type_id,
2666                    cycle_events.reborrow(),
2667                    anim_events.reborrow(),
2668                );
2669                ret.map(|result| {
2670                    assert!(!result.needs_retarget, "Cannot use a multi-target sequence of tweenable animations with a resource target.");
2671                    result.retain
2672                })
2673            })
2674        };
2675        self.resource_resolver
2676            .entry(resource_id)
2677            .or_insert(Box::new(resolver));
2678    }
2679
2680    /// Register a resolver for the given asset type.
2681    pub(crate) fn register_asset_resolver_for<A: Asset>(&mut self, components: &Components) {
2682        let resource_id = components.component_id::<Assets<A>>().unwrap();
2683        let resolver = |world: &mut World,
2684                        asset_id: UntypedAssetId,
2685                        entity: Entity,
2686                        target_type_id: &TypeId,
2687                        delta_time: Duration,
2688                        mut cycle_events: Mut<Messages<CycleCompletedEvent>>,
2689                        mut anim_events: Mut<Messages<AnimCompletedEvent>>|
2690         -> Result<bool, TweeningError> {
2691            let asset_id = asset_id.typed::<A>();
2692            // First, remove the Assets<A> from the world so we can access it mutably in
2693            // parallel of the TweenAnim
2694            world.resource_scope(|world, mut assets: Mut<Assets<A>>| {
2695                // We abuse the fact that Assets<A> is changed every single frame by the asset_events()
2696                // system, and assume that the current tick is therefore equal to the last time
2697                // Assets<A> was changed.
2698                let this_tick = assets.last_changed().get();
2699
2700                let Some(mut asset_mut) = assets.get_mut(asset_id)
2701                else {
2702                    return Err(TweeningError::InvalidAssetId(asset_id.into()));
2703                };
2704
2705                let target = AnimTargetKind::Asset {
2706                    asset_id: asset_id.untyped(),
2707                    assets_type_id: TypeId::of::<Assets<A>>(),
2708                };
2709
2710                let (mut entities, commands) = world.entities_and_commands();
2711
2712                // Resolve the TweenAnim component
2713                let Ok([mut ent]) = entities.get_mut([entity]) else {
2714                    return Err(TweeningError::EntityNotFound(entity));
2715                };
2716                let Some(mut anim) = ent.get_mut::<TweenAnim>() else {
2717                    return Err(TweeningError::MissingTweenAnim(ent.id()));
2718                };
2719
2720                // Create a fake Mut<A> which is always unchanged before the anim steps.
2721                // Its sole purpose is to know if the Lens::lerp() changed the asset.
2722                // Ideally we'd directly use AssetMut<> but the interface doesn't match.
2723                let mut added = Tick::MAX;  // hopefully unused...
2724                let last_tick = this_tick.saturating_sub(1);
2725                let mut last_changed = Tick::new(last_tick);
2726                let last_run = last_changed;
2727                let this_run = Tick::new(this_tick);
2728                let mut caller = MaybeLocation::caller();
2729                let typed_mut = Mut::new(asset_mut.bypass_change_detection(), &mut added, &mut last_changed, last_run, this_run, caller.as_mut());
2730                assert!(!typed_mut.is_changed());
2731                let mut mut_untyped: MutUntyped = typed_mut.into();
2732
2733                // Finally, step the TweenAnim and mutate the target
2734                let ret = anim.step_self(
2735                    commands,
2736                    entity,
2737                    delta_time,
2738                    &target,
2739                    mut_untyped.reborrow(),
2740                    target_type_id,
2741                    cycle_events.reborrow(),
2742                    anim_events.reborrow(),
2743                );
2744
2745                // If the asset actually changed (as reported by Mut<>), mark it as such
2746                // through Assets<A> (which will send the asset modified message).
2747                if mut_untyped.is_changed() {
2748                    // into_inner() marks the asset as changed, which triggers the regular
2749                    // asset modified flow (emits AssetEvents::Modified).
2750                    let _ = asset_mut.into_inner();
2751                }
2752
2753                ret.map(|result| {
2754                    assert!(!result.needs_retarget, "Cannot use a multi-target sequence of tweenable animations with an asset target.");
2755                    result.retain
2756                })
2757            })
2758        };
2759        self.asset_resolver
2760            .entry(resource_id)
2761            .or_insert(Box::new(resolver));
2762    }
2763
2764    #[allow(clippy::too_many_arguments)]
2765    #[inline]
2766    pub(crate) fn resolve_resource(
2767        &self,
2768        world: &mut World,
2769        target_type_id: &TypeId,
2770        resource_id: ComponentId,
2771        entity: Entity,
2772        delta_time: Duration,
2773        cycle_events: Mut<Messages<CycleCompletedEvent>>,
2774        anim_events: Mut<Messages<AnimCompletedEvent>>,
2775    ) -> Result<bool, TweeningError> {
2776        let Some(resolver) = self.resource_resolver.get(&resource_id) else {
2777            println!("ERROR: resource not registered {:?}", resource_id);
2778            return Err(TweeningError::AssetResolverNotRegistered(resource_id));
2779        };
2780        resolver(
2781            world,
2782            entity,
2783            target_type_id,
2784            delta_time,
2785            cycle_events,
2786            anim_events,
2787        )
2788    }
2789
2790    #[allow(clippy::too_many_arguments)]
2791    #[inline]
2792    pub(crate) fn resolve_asset(
2793        &self,
2794        world: &mut World,
2795        target_type_id: &TypeId,
2796        resource_id: ComponentId,
2797        untyped_asset_id: UntypedAssetId,
2798        entity: Entity,
2799        delta_time: Duration,
2800        cycle_events: Mut<Messages<CycleCompletedEvent>>,
2801        anim_events: Mut<Messages<AnimCompletedEvent>>,
2802    ) -> Result<bool, TweeningError> {
2803        let Some(resolver) = self.asset_resolver.get(&resource_id) else {
2804            println!("ERROR: asset not registered {:?}", resource_id);
2805            return Err(TweeningError::AssetResolverNotRegistered(resource_id));
2806        };
2807        resolver(
2808            world,
2809            untyped_asset_id,
2810            entity,
2811            target_type_id,
2812            delta_time,
2813            cycle_events,
2814            anim_events,
2815        )
2816    }
2817}
2818
2819pub(crate) struct StepResult {
2820    /// Whether to retain the current [`TweenAnim`]? If `false`, the
2821    /// [`TweenAnim`] is destroyed unless [`TweenAnim::destroy_on_completion`]
2822    /// is `false`.
2823    pub retain: bool,
2824    /// Whether to recompute the new animation target and step again. This is
2825    /// used by sequences when the animation target changes type in a sequence.
2826    pub needs_retarget: bool,
2827}
2828
2829#[cfg(test)]
2830mod tests {
2831    use std::{
2832        f32::consts::{FRAC_PI_2, TAU},
2833        marker::PhantomData,
2834    };
2835
2836    use bevy::ecs::{change_detection::MaybeLocation, change_detection::Tick};
2837
2838    use super::*;
2839    use crate::test_utils::*;
2840
2841    struct DummyLens {
2842        start: f32,
2843        end: f32,
2844    }
2845
2846    struct DummyLens2 {
2847        start: i32,
2848        end: i32,
2849    }
2850
2851    #[derive(Debug, Default, Clone, Copy, Component)]
2852    struct DummyComponent {
2853        value: f32,
2854    }
2855
2856    #[derive(Debug, Default, Clone, Copy, Component)]
2857    struct DummyComponent2 {
2858        value: i32,
2859    }
2860
2861    #[derive(Debug, Default, Clone, Copy, Resource)]
2862    struct DummyResource {
2863        value: f32,
2864    }
2865
2866    #[derive(Asset, Debug, Default, Reflect)]
2867    struct DummyAsset {
2868        value: f32,
2869    }
2870
2871    impl Lens<DummyComponent> for DummyLens {
2872        fn lerp(&mut self, mut target: Mut<DummyComponent>, ratio: f32) {
2873            target.value = self.start.lerp(self.end, ratio);
2874        }
2875    }
2876
2877    impl Lens<DummyComponent2> for DummyLens2 {
2878        fn lerp(&mut self, mut target: Mut<DummyComponent2>, ratio: f32) {
2879            target.value = ((self.start as f32) * (1. - ratio) + (self.end as f32) * ratio) as i32;
2880        }
2881    }
2882
2883    #[test]
2884    fn dummy_lens_component() {
2885        let mut c = DummyComponent::default();
2886        let mut l = DummyLens { start: 0., end: 1. };
2887        for r in [0_f32, 0.01, 0.3, 0.5, 0.9, 0.999, 1.] {
2888            {
2889                let mut added = Tick::new(0);
2890                let mut last_changed = Tick::new(0);
2891                let mut caller = MaybeLocation::caller();
2892                let mut target = Mut::new(
2893                    &mut c,
2894                    &mut added,
2895                    &mut last_changed,
2896                    Tick::new(0),
2897                    Tick::new(1),
2898                    caller.as_mut(),
2899                );
2900
2901                l.lerp(target.reborrow(), r);
2902
2903                assert!(target.is_changed());
2904            }
2905            assert_approx_eq!(c.value, r);
2906        }
2907    }
2908
2909    impl Lens<DummyResource> for DummyLens {
2910        fn lerp(&mut self, mut target: Mut<DummyResource>, ratio: f32) {
2911            target.value = self.start.lerp(self.end, ratio);
2912        }
2913    }
2914
2915    #[test]
2916    fn dummy_lens_resource() {
2917        let mut res = DummyResource::default();
2918        let mut l = DummyLens { start: 0., end: 1. };
2919        for r in [0_f32, 0.01, 0.3, 0.5, 0.9, 0.999, 1.] {
2920            {
2921                let mut added = Tick::new(0);
2922                let mut last_changed = Tick::new(0);
2923                let mut caller = MaybeLocation::caller();
2924                let mut target = Mut::new(
2925                    &mut res,
2926                    &mut added,
2927                    &mut last_changed,
2928                    Tick::new(0),
2929                    Tick::new(0),
2930                    caller.as_mut(),
2931                );
2932                l.lerp(target.reborrow(), r);
2933            }
2934            assert_approx_eq!(res.value, r);
2935        }
2936    }
2937
2938    impl Lens<DummyAsset> for DummyLens {
2939        fn lerp(&mut self, mut target: Mut<DummyAsset>, ratio: f32) {
2940            target.value = self.start.lerp(self.end, ratio);
2941        }
2942    }
2943
2944    #[test]
2945    fn dummy_lens_asset() {
2946        let mut assets = Assets::<DummyAsset>::default();
2947        let handle = assets.add(DummyAsset::default());
2948
2949        let mut l = DummyLens { start: 0., end: 1. };
2950        for r in [0_f32, 0.01, 0.3, 0.5, 0.9, 0.999, 1.] {
2951            {
2952                let mut added = Tick::new(0);
2953                let mut last_changed = Tick::new(0);
2954                let mut caller = MaybeLocation::caller();
2955                let asset = assets.get_mut_untracked(handle.id()).unwrap();
2956                let target = Mut::new(
2957                    asset,
2958                    &mut added,
2959                    &mut last_changed,
2960                    Tick::new(0),
2961                    Tick::new(0),
2962                    caller.as_mut(),
2963                );
2964                l.lerp(target, r);
2965            }
2966            assert_approx_eq!(assets.get(handle.id()).unwrap().value, r);
2967        }
2968    }
2969
2970    #[test]
2971    fn repeat_count() {
2972        let cycle_duration = Duration::from_millis(100);
2973
2974        let repeat = RepeatCount::default();
2975        assert_eq!(repeat, RepeatCount::Finite(1));
2976        assert_eq!(
2977            repeat.total_duration(cycle_duration),
2978            TotalDuration::Finite(cycle_duration)
2979        );
2980
2981        let repeat: RepeatCount = 3u32.into();
2982        assert_eq!(repeat, RepeatCount::Finite(3));
2983        assert_eq!(
2984            repeat.total_duration(cycle_duration),
2985            TotalDuration::Finite(cycle_duration * 3)
2986        );
2987
2988        let duration = Duration::from_secs(5);
2989        let repeat: RepeatCount = duration.into();
2990        assert_eq!(repeat, RepeatCount::For(duration));
2991        assert_eq!(
2992            repeat.total_duration(cycle_duration),
2993            TotalDuration::Finite(duration)
2994        );
2995
2996        let repeat = RepeatCount::Infinite;
2997        assert_eq!(
2998            repeat.total_duration(cycle_duration),
2999            TotalDuration::Infinite
3000        );
3001    }
3002
3003    #[test]
3004    fn repeat_strategy() {
3005        let strategy = RepeatStrategy::default();
3006        assert_eq!(strategy, RepeatStrategy::Repeat);
3007    }
3008
3009    #[test]
3010    fn playback_direction() {
3011        let tweening_direction = PlaybackDirection::default();
3012        assert_eq!(tweening_direction, PlaybackDirection::Forward);
3013    }
3014
3015    #[test]
3016    fn playback_state() {
3017        let mut state = PlaybackState::default();
3018        assert_eq!(state, PlaybackState::Playing);
3019        state = !state;
3020        assert_eq!(state, PlaybackState::Paused);
3021        state = !state;
3022        assert_eq!(state, PlaybackState::Playing);
3023    }
3024
3025    #[test]
3026    fn ease_method() {
3027        let ease = EaseMethod::default();
3028        assert!(matches!(
3029            ease,
3030            EaseMethod::EaseFunction(EaseFunction::Linear)
3031        ));
3032
3033        let ease = EaseMethod::EaseFunction(EaseFunction::QuadraticIn);
3034        assert_eq!(0., ease.sample(0.));
3035        assert_eq!(0.25, ease.sample(0.5));
3036        assert_eq!(1., ease.sample(1.));
3037
3038        let ease = EaseMethod::EaseFunction(EaseFunction::Linear);
3039        assert_eq!(0., ease.sample(0.));
3040        assert_eq!(0.5, ease.sample(0.5));
3041        assert_eq!(1., ease.sample(1.));
3042
3043        let ease = EaseMethod::Discrete(0.3);
3044        assert_eq!(0., ease.sample(0.));
3045        assert_eq!(1., ease.sample(0.5));
3046        assert_eq!(1., ease.sample(1.));
3047
3048        let ease = EaseMethod::CustomFunction(|f| 1. - f);
3049        assert_eq!(0., ease.sample(1.));
3050        assert_eq!(0.5, ease.sample(0.5));
3051        assert_eq!(1., ease.sample(0.));
3052    }
3053
3054    // TweenAnim::playback_state is entirely user-controlled; stepping animations
3055    // won't change it.
3056    #[test]
3057    fn animation_playback_state() {
3058        for state in [PlaybackState::Playing, PlaybackState::Paused] {
3059            let tween = Tween::new::<DummyComponent, DummyLens>(
3060                EaseFunction::QuadraticInOut,
3061                Duration::from_secs(1),
3062                DummyLens { start: 0., end: 1. },
3063            );
3064            let mut env = TestEnv::<DummyComponent>::new(tween);
3065            let mut anim = env.anim_mut().unwrap();
3066            anim.playback_state = state;
3067            anim.destroy_on_completion = false;
3068
3069            // Tick once
3070            let dt = Duration::from_millis(100);
3071            env.step_all(dt);
3072            assert_eq!(env.anim().unwrap().tween_state(), TweenState::Active);
3073            assert_eq!(env.anim().unwrap().playback_state, state);
3074
3075            // Check elapsed
3076            let elapsed = match state {
3077                PlaybackState::Playing => dt,
3078                PlaybackState::Paused => Duration::ZERO,
3079            };
3080            assert_eq!(env.anim().unwrap().tweenable.elapsed(), elapsed);
3081
3082            // Force playback, otherwise we can't complete
3083            env.anim_mut().unwrap().playback_state = PlaybackState::Playing;
3084
3085            // Even after completion, the playback state is untouched
3086            env.step_all(Duration::from_secs(10) - elapsed);
3087            assert_eq!(env.anim().unwrap().tween_state(), TweenState::Completed);
3088            assert_eq!(env.anim().unwrap().playback_state, PlaybackState::Playing);
3089        }
3090    }
3091
3092    #[test]
3093    fn animation_events() {
3094        let tween = Tween::new::<DummyComponent, DummyLens>(
3095            EaseFunction::QuadraticInOut,
3096            Duration::from_secs(1),
3097            DummyLens { start: 0., end: 1. },
3098        )
3099        .with_repeat_count(2)
3100        .with_cycle_completed_event(true);
3101        let mut env = TestEnv::<DummyComponent>::new(tween);
3102
3103        // Tick until one cycle is completed, but not the entire animation
3104        let dt = Duration::from_millis(1200);
3105        env.step_all(dt);
3106        assert_eq!(env.anim().unwrap().tween_state(), TweenState::Active);
3107
3108        // Check events
3109        assert_eq!(env.event_count::<CycleCompletedEvent>(), 1);
3110        assert_eq!(env.event_count::<AnimCompletedEvent>(), 0);
3111
3112        // Tick until completion
3113        let dt = Duration::from_millis(1000);
3114        env.step_all(dt);
3115        assert!(env.anim().is_none());
3116
3117        // Check events (note that we didn't clear previous events, so that's a
3118        // cumulative count).
3119        assert_eq!(env.event_count::<CycleCompletedEvent>(), 1);
3120        assert_eq!(env.event_count::<AnimCompletedEvent>(), 1);
3121    }
3122
3123    #[derive(Debug, Resource)]
3124    struct Count<E: Event, T = ()> {
3125        pub count: i32,
3126        pub phantom: PhantomData<E>,
3127        pub phantom2: PhantomData<T>,
3128    }
3129
3130    impl<E: Event, T> Default for Count<E, T> {
3131        fn default() -> Self {
3132            Self {
3133                count: 0,
3134                phantom: PhantomData,
3135                phantom2: PhantomData,
3136            }
3137        }
3138    }
3139
3140    struct GlobalMarker;
3141
3142    #[test]
3143    fn animation_observe() {
3144        let tween = Tween::new::<DummyComponent, DummyLens>(
3145            EaseFunction::QuadraticInOut,
3146            Duration::from_secs(1),
3147            DummyLens { start: 0., end: 1. },
3148        )
3149        .with_repeat_count(2)
3150        .with_cycle_completed_event(true);
3151        let mut env = TestEnv::<DummyComponent>::new(tween);
3152
3153        env.world.init_resource::<Count<CycleCompletedEvent>>();
3154        assert_eq!(env.world.resource::<Count<CycleCompletedEvent>>().count, 0);
3155        env.world
3156            .init_resource::<Count<CycleCompletedEvent, GlobalMarker>>();
3157        assert_eq!(
3158            env.world
3159                .resource::<Count<CycleCompletedEvent, GlobalMarker>>()
3160                .count,
3161            0
3162        );
3163
3164        fn observe_global(
3165            _trigger: On<CycleCompletedEvent>,
3166            mut count: ResMut<Count<CycleCompletedEvent, GlobalMarker>>,
3167        ) {
3168            count.count += 1;
3169        }
3170        env.world.add_observer(observe_global);
3171
3172        fn observe_entity(
3173            _trigger: On<CycleCompletedEvent>,
3174            mut count: ResMut<Count<CycleCompletedEvent>>,
3175        ) {
3176            count.count += 1;
3177        }
3178        env.world.entity_mut(env.entity).observe(observe_entity);
3179
3180        // Tick until one cycle is completed, but not the entire animation
3181        let dt = Duration::from_millis(1200);
3182        env.step_all(dt);
3183        assert_eq!(env.anim().unwrap().tween_state(), TweenState::Active);
3184
3185        // Check observer system ran
3186        assert_eq!(env.world.resource::<Count<CycleCompletedEvent>>().count, 1);
3187        assert_eq!(
3188            env.world
3189                .resource::<Count<CycleCompletedEvent, GlobalMarker>>()
3190                .count,
3191            1
3192        );
3193
3194        // Tick until completion
3195        let dt = Duration::from_millis(1000);
3196        env.step_all(dt);
3197        assert!(env.anim().is_none());
3198
3199        // Check observer system ran (note that we didn't clear previous events, so
3200        // that's a cumulative count).
3201        assert_eq!(env.world.resource::<Count<CycleCompletedEvent>>().count, 2);
3202        assert_eq!(
3203            env.world
3204                .resource::<Count<CycleCompletedEvent, GlobalMarker>>()
3205                .count,
3206            2
3207        );
3208    }
3209
3210    // #[test]
3211    // fn animator_controls() {
3212    //     let tween = Tween::<DummyComponent>::new(
3213    //         EaseFunction::QuadraticInOut,
3214    //         Duration::from_secs(1),
3215    //         DummyLens { start: 0., end: 1. },
3216    //     );
3217    //     let mut animator = Animator::new(tween);
3218    //     assert_eq!(animator.state, AnimatorState::Playing);
3219    //     assert_approx_eq!(animator.tweenable().progress(), 0.);
3220
3221    //     animator.stop();
3222    //     assert_eq!(animator.state, AnimatorState::Paused);
3223    //     assert_approx_eq!(animator.tweenable().progress(), 0.);
3224
3225    //     animator.tweenable_mut().set_progress(0.5);
3226    //     assert_eq!(animator.state, AnimatorState::Paused);
3227    //     assert_approx_eq!(animator.tweenable().progress(), 0.5);
3228
3229    //     animator.tweenable_mut().rewind();
3230    //     assert_eq!(animator.state, AnimatorState::Paused);
3231    //     assert_approx_eq!(animator.tweenable().progress(), 0.);
3232
3233    //     animator.tweenable_mut().set_progress(0.5);
3234    //     animator.state = AnimatorState::Playing;
3235    //     assert_eq!(animator.state, AnimatorState::Playing);
3236    //     assert_approx_eq!(animator.tweenable().progress(), 0.5);
3237
3238    //     animator.tweenable_mut().rewind();
3239    //     assert_eq!(animator.state, AnimatorState::Playing);
3240    //     assert_approx_eq!(animator.tweenable().progress(), 0.);
3241
3242    //     animator.stop();
3243    //     assert_eq!(animator.state, AnimatorState::Paused);
3244    //     assert_approx_eq!(animator.tweenable().progress(), 0.);
3245    // }
3246
3247    #[test]
3248    fn animation_speed() {
3249        let tween = Tween::new::<DummyComponent, DummyLens>(
3250            EaseFunction::QuadraticInOut,
3251            Duration::from_secs(1),
3252            DummyLens { start: 0., end: 1. },
3253        );
3254
3255        let mut env = TestEnv::<DummyComponent>::new(tween);
3256
3257        assert_approx_eq!(env.anim().unwrap().speed, 1.); // default speed
3258
3259        env.anim_mut().unwrap().speed = 2.4;
3260        assert_approx_eq!(env.anim().unwrap().speed, 2.4);
3261
3262        env.step_all(Duration::from_millis(100));
3263        // Here we have enough precision for exact equality, but that may not always be
3264        // the case for larger durations or speed values.
3265        assert_eq!(
3266            env.anim().unwrap().tweenable.elapsed(),
3267            Duration::from_millis(240)
3268        );
3269
3270        env.anim_mut().unwrap().speed = -1.;
3271        env.step_all(Duration::from_millis(100));
3272        // Safety: invalid negative speed clamped to 0.
3273        assert_eq!(env.anim().unwrap().speed, 0.);
3274        // At zero speed, step is a no-op so elapse() didn't change
3275        assert_eq!(
3276            env.anim().unwrap().tweenable.elapsed(),
3277            Duration::from_millis(240)
3278        );
3279    }
3280
3281    #[test]
3282    fn animator_set_tweenable() {
3283        let tween = Tween::new::<DummyComponent, DummyLens>(
3284            EaseFunction::QuadraticInOut,
3285            Duration::from_secs(1),
3286            DummyLens { start: 0., end: 1. },
3287        );
3288        let tween2 = Tween::new::<DummyComponent, DummyLens>(
3289            EaseFunction::SmoothStep,
3290            Duration::from_secs(2),
3291            DummyLens { start: 2., end: 3. },
3292        );
3293
3294        let mut env = TestEnv::<DummyComponent>::new(tween);
3295        env.anim_mut().unwrap().destroy_on_completion = false;
3296
3297        let dt = Duration::from_millis(1500);
3298
3299        env.step_all(dt);
3300        assert_eq!(env.component().value, 1.);
3301        assert_eq!(env.anim().unwrap().tween_state(), TweenState::Completed);
3302
3303        // Swap tweens
3304        let old_tweenable = env.anim_mut().unwrap().set_tweenable(tween2).unwrap();
3305
3306        assert_eq!(env.anim().unwrap().tween_state(), TweenState::Active);
3307        // The elapsed is stored inside the tweenable
3308        assert_eq!(old_tweenable.elapsed(), Duration::from_secs(1)); // capped at total_duration()
3309        assert_eq!(env.anim().unwrap().tweenable.elapsed(), Duration::ZERO);
3310
3311        env.step_all(dt);
3312        assert!(env.component().value >= 2. && env.component().value <= 3.);
3313    }
3314
3315    // Currently multi-target sequences are not implemented. This _could_ work with
3316    // implicit targets (so, multiple components on the same entity), but is a bit
3317    // complex to implement with the current code. So leave that out for now, and
3318    // test we assert if the user attempts it. The workaround is to create separate
3319    // animations for each comopnent/target. Anyway multi-target sequence can't work
3320    // with other target types, since they need an explicit TweenAnim, and we
3321    // can't have more than one per entity.
3322    #[test]
3323    #[should_panic(
3324        expected = "TODO: Cannot use tweenable animations with different targets inside the same Sequence. Create separate animations for each target."
3325    )]
3326    fn seq_multi_target() {
3327        let tween = Tween::new::<DummyComponent, DummyLens>(
3328            EaseFunction::QuadraticInOut,
3329            Duration::from_secs(1),
3330            DummyLens { start: 0., end: 1. },
3331        )
3332        .then(Tween::new::<DummyComponent2, DummyLens2>(
3333            EaseFunction::SmoothStep,
3334            Duration::from_secs(1),
3335            DummyLens2 { start: -5, end: 5 },
3336        ));
3337        let mut env = TestEnv::<DummyComponent>::new(tween);
3338        let entity = env.entity;
3339        env.world
3340            .entity_mut(entity)
3341            .insert(DummyComponent2 { value: -42 });
3342        TweenAnim::step_one(&mut env.world, Duration::from_millis(1100), entity).unwrap();
3343    }
3344
3345    // #[test]
3346    // fn animator_set_target() {
3347    //     let tween = Tween::new::<DummyComponent, DummyLens>(
3348    //         EaseFunction::QuadraticInOut,
3349    //         Duration::from_secs(1),
3350    //         DummyLens { start: 0., end: 1. },
3351    //     );
3352    //     let mut env = TestEnv::<DummyComponent>::new(tween);
3353
3354    //     // Register our custom asset type
3355    //     env.world.init_resource::<Assets<DummyAsset>>();
3356
3357    //     // Invalid ID
3358    //     {
3359    //         let entity = env.entity;
3360    //         let target =
3361    //
3362    // ComponentAnimTarget::new::<DummyComponent>(env.world.components(),
3363    // entity).unwrap();         let err = env
3364    //             .animator_mut()
3365    //             .set_target(Entity::PLACEHOLDER, target.into())
3366    //             .err()
3367    //             .unwrap();
3368    //         let TweeningError::InvalidTweenId(err_id) = err else {
3369    //             panic!();
3370    //         };
3371    //         assert_eq!(err_id, Entity::PLACEHOLDER);
3372    //     }
3373
3374    //     // Spawn a second entity without any animation
3375    //     let entity1 = env.entity;
3376    //     let entity2 = env.world_mut().spawn(DummyComponent { value: 0.
3377    // }).id();     assert_ne!(entity1, entity2);
3378    //     assert_eq!(env.component().value, 0.);
3379
3380    //     // Step the current target
3381    //     let dt = Duration::from_millis(100);
3382    //     env.step_all(dt);
3383    //     assert!(env.component().value > 0.);
3384    //     assert_eq!(
3385    //         env.world
3386    //             .entity(entity2)
3387    //             .get_components::<&DummyComponent>()
3388    //             .unwrap()
3389    //             .value,
3390    //         0.
3391    //     );
3392
3393    //     // Now retarget
3394    //     let id = env.entity;
3395    //     let target2 =
3396    //         ComponentAnimTarget::new::<DummyComponent>(env.world.
3397    // components(), entity2).unwrap();     let target1 =
3398    // env.animator_mut().set_target(id, target2.into()).unwrap();
3399    //     assert!(target1.is_component());
3400    //     let comp1 = target1.as_component().unwrap();
3401    //     assert_eq!(comp1.entity, entity1);
3402    //     assert_eq!(
3403    //         comp1.component_id,
3404    //         env.world.component_id::<DummyComponent>().unwrap()
3405    //     );
3406
3407    //     // Step the new target
3408    //     env.step_all(dt);
3409    //     assert!(env.component().value > 0.);
3410    //     assert!(
3411    //         env.world
3412    //             .entity(entity1)
3413    //             .get_components::<&DummyComponent>()
3414    //             .unwrap()
3415    //             .value
3416    //             > 0.
3417    //     );
3418
3419    //     // Invalid target
3420    //     {
3421    //         let target3 =
3422    //             AssetAnimTarget::new(env.world.components(),
3423    // Handle::<DummyAsset>::default().id())                 .unwrap();
3424    //         let err3 = env.animator_mut().set_target(id, target3.into());
3425    //         assert!(err3.is_err());
3426    //         let err3 = err3.err().unwrap();
3427    //         let TweeningError::MismatchingTargetKind(oc, nc) = err3 else {
3428    //             panic!();
3429    //         };
3430    //         assert_eq!(oc, true);
3431    //         assert_eq!(nc, false);
3432    //     }
3433    // }
3434
3435    #[test]
3436    fn anim_target_component() {
3437        let mut env = TestEnv::<Transform>::empty();
3438        let entity = env.world.spawn(Transform::default()).id();
3439        let tween = Tween::new::<Transform, TransformPositionLens>(
3440            EaseFunction::Linear,
3441            Duration::from_secs(1),
3442            TransformPositionLens {
3443                start: Vec3::ZERO,
3444                end: Vec3::ONE,
3445            },
3446        );
3447        let target = AnimTarget::component::<Transform>(entity);
3448        let anim_entity = env
3449            .world
3450            .spawn((
3451                TweenAnim::new(tween)
3452                    .with_speed(2.)
3453                    .with_destroy_on_completed(true),
3454                target,
3455            ))
3456            .id();
3457
3458        // Step
3459        assert!(
3460            TweenAnim::step_one(&mut env.world, Duration::from_millis(100), anim_entity).is_ok()
3461        );
3462        let tr = env.world.entity(entity).get::<Transform>().unwrap();
3463        assert_eq!(tr.translation, Vec3::ONE * 0.2);
3464
3465        // Complete
3466        assert_eq!(
3467            TweenAnim::step_many(&mut env.world, Duration::from_millis(400), &[anim_entity]),
3468            1
3469        );
3470
3471        // Destroyed on completion
3472        assert!(env.world.entity(anim_entity).get::<TweenAnim>().is_none());
3473    }
3474
3475    #[test]
3476    fn anim_target_resource() {
3477        let mut env = TestEnv::<Transform>::empty();
3478        env.world.init_resource::<DummyResource>();
3479        let tween = Tween::new::<DummyResource, DummyLens>(
3480            EaseFunction::Linear,
3481            Duration::from_secs(1),
3482            DummyLens { start: 0., end: 1. },
3483        );
3484        let target = AnimTarget::resource::<DummyResource>();
3485        let anim_entity = env
3486            .world
3487            .spawn((
3488                TweenAnim::new(tween)
3489                    .with_speed(2.)
3490                    .with_destroy_on_completed(true),
3491                target,
3492            ))
3493            .id();
3494
3495        // Step
3496        assert!(
3497            TweenAnim::step_one(&mut env.world, Duration::from_millis(100), anim_entity).is_ok()
3498        );
3499        let res = env.world.resource::<DummyResource>();
3500        assert_eq!(res.value, 0.2);
3501
3502        // Complete
3503        assert_eq!(
3504            TweenAnim::step_many(&mut env.world, Duration::from_millis(400), &[anim_entity]),
3505            1
3506        );
3507
3508        // Destroyed on completion
3509        assert!(env.world.entity(anim_entity).get::<TweenAnim>().is_none());
3510    }
3511
3512    #[test]
3513    fn anim_target_asset() {
3514        let mut env = TestEnv::<Transform>::empty();
3515        let mut assets = Assets::<DummyAsset>::default();
3516        let handle = assets.add(DummyAsset::default());
3517        env.world.insert_resource(assets);
3518        let tween = Tween::new::<DummyAsset, DummyLens>(
3519            EaseFunction::Linear,
3520            Duration::from_secs(1),
3521            DummyLens { start: 0., end: 1. },
3522        );
3523        let target = AnimTarget::asset::<DummyAsset>(&handle);
3524        let anim_entity = env
3525            .world
3526            .spawn((
3527                TweenAnim::new(tween)
3528                    .with_speed(2.)
3529                    .with_destroy_on_completed(true),
3530                target,
3531            ))
3532            .id();
3533
3534        // Step
3535        assert!(
3536            TweenAnim::step_one(&mut env.world, Duration::from_millis(100), anim_entity).is_ok()
3537        );
3538        let assets = env.world.resource::<Assets<DummyAsset>>();
3539        let asset = assets.get(&handle).unwrap();
3540        assert_eq!(asset.value, 0.2);
3541
3542        // Complete
3543        assert_eq!(
3544            TweenAnim::step_many(&mut env.world, Duration::from_millis(400), &[anim_entity]),
3545            1
3546        );
3547
3548        // Destroyed on completion
3549        assert!(env.world.entity(anim_entity).get::<TweenAnim>().is_none());
3550    }
3551
3552    #[test]
3553    fn animated_entity_commands_common() {
3554        let dummy_tween = Tween::new::<DummyComponent, DummyLens>(
3555            EaseFunction::QuadraticInOut,
3556            Duration::from_secs(1),
3557            DummyLens { start: 0., end: 1. },
3558        );
3559        let mut env = TestEnv::<DummyComponent>::new(dummy_tween);
3560
3561        let entity = env
3562            .world
3563            .commands()
3564            .spawn(Transform::default())
3565            .move_to(Vec3::ONE, Duration::from_secs(1), EaseFunction::Linear)
3566            .with_repeat_count(4)
3567            .with_repeat_strategy(RepeatStrategy::MirroredRepeat)
3568            .id();
3569        let entity2 = env
3570            .world
3571            .commands()
3572            .spawn(Transform::default())
3573            .move_to(Vec3::ONE, Duration::from_secs(1), EaseFunction::Linear)
3574            .with_repeat(4, RepeatStrategy::MirroredRepeat)
3575            .into_inner()
3576            .id();
3577        env.world.flush();
3578
3579        env.step_all(Duration::from_millis(3300));
3580
3581        let tr = env.world.entity(entity).get::<Transform>().unwrap();
3582        assert_eq!(tr.translation, Vec3::ONE * 0.7);
3583        let tr = env.world.entity(entity2).get::<Transform>().unwrap();
3584        assert_eq!(tr.translation, Vec3::ONE * 0.7);
3585    }
3586
3587    #[test]
3588    fn animated_entity_commands_move_to() {
3589        let dummy_tween = Tween::new::<DummyComponent, DummyLens>(
3590            EaseFunction::QuadraticInOut,
3591            Duration::from_secs(1),
3592            DummyLens { start: 0., end: 1. },
3593        );
3594        let mut env = TestEnv::<DummyComponent>::new(dummy_tween);
3595
3596        let entity = env
3597            .world
3598            .commands()
3599            .spawn(Transform::default())
3600            .move_to(Vec3::ONE, Duration::from_secs(1), EaseFunction::Linear)
3601            .id();
3602        env.world.flush();
3603
3604        env.step_all(Duration::from_millis(300));
3605
3606        let tr = env.world.entity(entity).get::<Transform>().unwrap();
3607        assert_eq!(tr.translation, Vec3::ONE * 0.3);
3608    }
3609
3610    #[test]
3611    fn animated_entity_commands_move_from() {
3612        let dummy_tween = Tween::new::<DummyComponent, DummyLens>(
3613            EaseFunction::QuadraticInOut,
3614            Duration::from_secs(1),
3615            DummyLens { start: 0., end: 1. },
3616        );
3617        let mut env = TestEnv::<DummyComponent>::new(dummy_tween);
3618
3619        let entity = env
3620            .world
3621            .commands()
3622            .spawn(Transform::default())
3623            .move_from(Vec3::ONE, Duration::from_secs(1), EaseFunction::Linear)
3624            .id();
3625        env.world.flush();
3626
3627        env.step_all(Duration::from_millis(300));
3628
3629        let tr = env.world.entity(entity).get::<Transform>().unwrap();
3630        assert_eq!(tr.translation, Vec3::ONE * 0.7);
3631    }
3632
3633    #[test]
3634    fn animated_entity_commands_scale_to() {
3635        let dummy_tween = Tween::new::<DummyComponent, DummyLens>(
3636            EaseFunction::QuadraticInOut,
3637            Duration::from_secs(1),
3638            DummyLens { start: 0., end: 1. },
3639        );
3640        let mut env = TestEnv::<DummyComponent>::new(dummy_tween);
3641
3642        let entity = env
3643            .world
3644            .commands()
3645            .spawn(Transform::default())
3646            .scale_to(Vec3::ONE * 2., Duration::from_secs(1), EaseFunction::Linear)
3647            .id();
3648        env.world.flush();
3649
3650        env.step_all(Duration::from_millis(300));
3651
3652        let tr = env.world.entity(entity).get::<Transform>().unwrap();
3653        assert_eq!(tr.scale, Vec3::ONE * 1.3);
3654    }
3655
3656    #[test]
3657    fn animated_entity_commands_scale_from() {
3658        let dummy_tween = Tween::new::<DummyComponent, DummyLens>(
3659            EaseFunction::QuadraticInOut,
3660            Duration::from_secs(1),
3661            DummyLens { start: 0., end: 1. },
3662        );
3663        let mut env = TestEnv::<DummyComponent>::new(dummy_tween);
3664
3665        let entity = env
3666            .world
3667            .commands()
3668            .spawn(Transform::default())
3669            .scale_from(Vec3::ONE * 2., Duration::from_secs(1), EaseFunction::Linear)
3670            .id();
3671        env.world.flush();
3672
3673        env.step_all(Duration::from_millis(300));
3674
3675        let tr = env.world.entity(entity).get::<Transform>().unwrap();
3676        assert_eq!(tr.scale, Vec3::ONE * 1.7);
3677    }
3678
3679    #[test]
3680    fn animated_entity_commands_rotate_x() {
3681        let dummy_tween = Tween::new::<DummyComponent, DummyLens>(
3682            EaseFunction::QuadraticInOut,
3683            Duration::from_secs(1),
3684            DummyLens { start: 0., end: 1. },
3685        );
3686        let mut env = TestEnv::<DummyComponent>::new(dummy_tween);
3687
3688        let entity = env
3689            .world
3690            .commands()
3691            .spawn(Transform::default())
3692            .rotate_x(Duration::from_secs(1))
3693            .id();
3694        env.world.flush();
3695
3696        env.step_all(Duration::from_millis(1300));
3697
3698        let tr = env.world.entity(entity).get::<Transform>().unwrap();
3699        assert_eq!(tr.rotation, Quat::from_rotation_x(TAU * 0.3));
3700    }
3701
3702    #[test]
3703    fn animated_entity_commands_rotate_y() {
3704        let dummy_tween = Tween::new::<DummyComponent, DummyLens>(
3705            EaseFunction::QuadraticInOut,
3706            Duration::from_secs(1),
3707            DummyLens { start: 0., end: 1. },
3708        );
3709        let mut env = TestEnv::<DummyComponent>::new(dummy_tween);
3710
3711        let entity = env
3712            .world
3713            .commands()
3714            .spawn(Transform::default())
3715            .rotate_y(Duration::from_secs(1))
3716            .id();
3717        env.world.flush();
3718
3719        env.step_all(Duration::from_millis(1300));
3720
3721        let tr = env.world.entity(entity).get::<Transform>().unwrap();
3722        assert_eq!(tr.rotation, Quat::from_rotation_y(TAU * 0.3));
3723    }
3724
3725    #[test]
3726    fn animated_entity_commands_rotate_z() {
3727        let dummy_tween = Tween::new::<DummyComponent, DummyLens>(
3728            EaseFunction::QuadraticInOut,
3729            Duration::from_secs(1),
3730            DummyLens { start: 0., end: 1. },
3731        );
3732        let mut env = TestEnv::<DummyComponent>::new(dummy_tween);
3733
3734        let entity = env
3735            .world
3736            .commands()
3737            .spawn(Transform::default())
3738            .rotate_z(Duration::from_secs(1))
3739            .id();
3740        env.world.flush();
3741
3742        env.step_all(Duration::from_millis(1300));
3743
3744        let tr = env.world.entity(entity).get::<Transform>().unwrap();
3745        assert_eq!(tr.rotation, Quat::from_rotation_z(TAU * 0.3));
3746    }
3747
3748    #[test]
3749    fn animated_entity_commands_rotate_x_by() {
3750        let dummy_tween = Tween::new::<DummyComponent, DummyLens>(
3751            EaseFunction::QuadraticInOut,
3752            Duration::from_secs(1),
3753            DummyLens { start: 0., end: 1. },
3754        );
3755        let mut env = TestEnv::<DummyComponent>::new(dummy_tween);
3756
3757        let entity = env
3758            .world
3759            .commands()
3760            .spawn(Transform::default())
3761            .rotate_x_by(FRAC_PI_2, Duration::from_secs(1), EaseFunction::Linear)
3762            .id();
3763        env.world.flush();
3764
3765        env.step_all(Duration::from_millis(1300)); // 130%
3766
3767        let tr = env.world.entity(entity).get::<Transform>().unwrap();
3768        assert_eq!(tr.rotation, Quat::from_rotation_x(FRAC_PI_2)); // 100%
3769    }
3770
3771    #[test]
3772    fn animated_entity_commands_rotate_y_by() {
3773        let dummy_tween = Tween::new::<DummyComponent, DummyLens>(
3774            EaseFunction::QuadraticInOut,
3775            Duration::from_secs(1),
3776            DummyLens { start: 0., end: 1. },
3777        );
3778        let mut env = TestEnv::<DummyComponent>::new(dummy_tween);
3779
3780        let entity = env
3781            .world
3782            .commands()
3783            .spawn(Transform::default())
3784            .rotate_y_by(FRAC_PI_2, Duration::from_secs(1), EaseFunction::Linear)
3785            .id();
3786        env.world.flush();
3787
3788        env.step_all(Duration::from_millis(1300)); // 130%
3789
3790        let tr = env.world.entity(entity).get::<Transform>().unwrap();
3791        assert_eq!(tr.rotation, Quat::from_rotation_y(FRAC_PI_2)); // 100%
3792    }
3793
3794    #[test]
3795    fn animated_entity_commands_rotate_z_by() {
3796        let dummy_tween = Tween::new::<DummyComponent, DummyLens>(
3797            EaseFunction::QuadraticInOut,
3798            Duration::from_secs(1),
3799            DummyLens { start: 0., end: 1. },
3800        );
3801        let mut env = TestEnv::<DummyComponent>::new(dummy_tween);
3802
3803        let entity = env
3804            .world
3805            .commands()
3806            .spawn(Transform::default())
3807            .rotate_z_by(FRAC_PI_2, Duration::from_secs(1), EaseFunction::Linear)
3808            .id();
3809        env.world.flush();
3810
3811        env.step_all(Duration::from_millis(1300)); // 130%
3812
3813        let tr = env.world.entity(entity).get::<Transform>().unwrap();
3814        assert_eq!(tr.rotation, Quat::from_rotation_z(FRAC_PI_2)); // 100%
3815    }
3816
3817    #[test]
3818    fn resolver_resource() {
3819        let dummy_tween = Tween::new::<DummyComponent, DummyLens>(
3820            EaseFunction::QuadraticInOut,
3821            Duration::from_secs(1),
3822            DummyLens { start: 0., end: 1. },
3823        );
3824        let mut env = TestEnv::<DummyComponent>::new(dummy_tween);
3825
3826        // Register the resource and create a TweenAnim for it
3827        env.world.init_resource::<DummyResource>();
3828        let tween = Tween::new::<DummyResource, DummyLens>(
3829            EaseFunction::QuadraticInOut,
3830            Duration::from_secs(1),
3831            DummyLens { start: 0., end: 1. },
3832        );
3833        let entity = env.world.commands().spawn(TweenAnim::new(tween)).id();
3834
3835        // Ensure all commands are applied before starting the test
3836        env.world.flush();
3837
3838        let delta_time = Duration::from_millis(200);
3839        let resource_id = env.world.component_id::<DummyResource>().unwrap();
3840
3841        // Resource resolver not registered; fails
3842        env.world
3843            .resource_scope(|world, resolver: Mut<TweenResolver>| {
3844                world.resource_scope(
3845                    |world, mut cycle_events: Mut<Messages<CycleCompletedEvent>>| {
3846                        world.resource_scope(
3847                            |world, mut anim_events: Mut<Messages<AnimCompletedEvent>>| {
3848                                assert!(resolver
3849                                    .resolve_resource(
3850                                        world,
3851                                        &TypeId::of::<DummyResource>(),
3852                                        resource_id,
3853                                        entity,
3854                                        delta_time,
3855                                        cycle_events.reborrow(),
3856                                        anim_events.reborrow(),
3857                                    )
3858                                    .is_err());
3859                            },
3860                        );
3861                    },
3862                );
3863            });
3864
3865        // Register the resource resolver
3866        env.world
3867            .resource_scope(|world, mut resolver: Mut<TweenResolver>| {
3868                resolver.register_resource_resolver_for::<DummyResource>(world.components());
3869            });
3870
3871        // Resource resolver registered; succeeds
3872        env.world
3873            .resource_scope(|world, resolver: Mut<TweenResolver>| {
3874                world.resource_scope(
3875                    |world, mut cycle_events: Mut<Messages<CycleCompletedEvent>>| {
3876                        world.resource_scope(
3877                            |world, mut anim_events: Mut<Messages<AnimCompletedEvent>>| {
3878                                assert!(resolver
3879                                    .resolve_resource(
3880                                        world,
3881                                        &TypeId::of::<DummyResource>(),
3882                                        resource_id,
3883                                        entity,
3884                                        delta_time,
3885                                        cycle_events.reborrow(),
3886                                        anim_events.reborrow(),
3887                                    )
3888                                    .unwrap());
3889                            },
3890                        );
3891                    },
3892                );
3893            });
3894    }
3895
3896    #[test]
3897    fn resolver_asset() {
3898        let dummy_tween = Tween::new::<DummyComponent, DummyLens>(
3899            EaseFunction::QuadraticInOut,
3900            Duration::from_secs(1),
3901            DummyLens { start: 0., end: 1. },
3902        );
3903        let mut env = TestEnv::<DummyComponent>::new(dummy_tween);
3904
3905        // Register the asset and create a TweenAnim for it
3906        let mut assets = Assets::<DummyAsset>::default();
3907        let handle = assets.add(DummyAsset::default());
3908        let untyped_asset_id = handle.id().untyped();
3909        env.world.insert_resource(assets);
3910        let tween = Tween::new::<DummyAsset, DummyLens>(
3911            EaseFunction::QuadraticInOut,
3912            Duration::from_secs(1),
3913            DummyLens { start: 0., end: 1. },
3914        );
3915        let entity = env.world.commands().spawn(TweenAnim::new(tween)).id();
3916
3917        // Ensure all commands are applied before starting the test
3918        env.world.flush();
3919
3920        let delta_time = Duration::from_millis(200);
3921        let resource_id = env.world.component_id::<Assets<DummyAsset>>().unwrap();
3922
3923        // Asset resolver not registered; fails
3924        env.world
3925            .resource_scope(|world, resolver: Mut<TweenResolver>| {
3926                world.resource_scope(
3927                    |world, mut cycle_events: Mut<Messages<CycleCompletedEvent>>| {
3928                        world.resource_scope(
3929                            |world, mut anim_events: Mut<Messages<AnimCompletedEvent>>| {
3930                                assert!(resolver
3931                                    .resolve_asset(
3932                                        world,
3933                                        &TypeId::of::<DummyAsset>(),
3934                                        resource_id,
3935                                        untyped_asset_id,
3936                                        entity,
3937                                        delta_time,
3938                                        cycle_events.reborrow(),
3939                                        anim_events.reborrow(),
3940                                    )
3941                                    .is_err());
3942                            },
3943                        );
3944                    },
3945                );
3946            });
3947
3948        // Register the asset resolver
3949        env.world
3950            .resource_scope(|world, mut resolver: Mut<TweenResolver>| {
3951                resolver.register_asset_resolver_for::<DummyAsset>(world.components());
3952            });
3953
3954        // Asset resolver registered; succeeds
3955        env.world
3956            .resource_scope(|world, resolver: Mut<TweenResolver>| {
3957                world.resource_scope(
3958                    |world, mut cycle_events: Mut<Messages<CycleCompletedEvent>>| {
3959                        world.resource_scope(
3960                            |world, mut anim_events: Mut<Messages<AnimCompletedEvent>>| {
3961                                assert!(resolver
3962                                    .resolve_asset(
3963                                        world,
3964                                        &TypeId::of::<DummyAsset>(),
3965                                        resource_id,
3966                                        untyped_asset_id,
3967                                        entity,
3968                                        delta_time,
3969                                        cycle_events.reborrow(),
3970                                        anim_events.reborrow(),
3971                                    )
3972                                    .unwrap());
3973                            },
3974                        );
3975                    },
3976                );
3977            });
3978    }
3979}