bevy_animator/
animation.rs

1//=================================================================================
2// The animation ID will tell the animator what animation to play on the component.
3// This is the trait that you will define for your animation file. For now we only
4// handle 2D animations for now but plan for 3D later.
5//=================================================================================
6
7use std::marker::PhantomData;
8use bevy::{ecs::query::{QueryData, WorldQuery}, prelude::*};
9
10//=================================================================================
11//    Animation Plugin
12//=================================================================================
13
14/// This plugin will register the required systems so that an animation of a type will work. Required to be implemented for each animation type.
15pub struct AnimationPlugin<A : Animation>(PhantomData<A>);
16
17impl <A : Animation + Send + Sync + 'static> Default for AnimationPlugin<A> {
18    fn default() -> Self {
19        AnimationPlugin(PhantomData)
20    }
21}
22
23impl <A : Animation + Send + Sync + 'static> Plugin for AnimationPlugin<A> {
24    fn build(&self, app: &mut App) {
25        app
26            .add_systems(PostUpdate, update_animators::<A>)
27        ;
28    }
29
30    fn is_unique(&self) -> bool {
31        false
32    }
33}
34
35//=================================================================================
36//    Animation Systems
37//=================================================================================
38
39/// This system will update all of the animators in the world and apply the animations to the components they are attached to.
40pub(crate) fn update_animators<A : Animation + Send + Sync + 'static>(
41    mut animators : Query<(&mut Animator<A>, A::Query<'_, '_>, &Handle<A::AsociatedAsset>)>,
42    assets : Res<Assets<A::AsociatedAsset>>,
43    time : Res<Time>,
44) {
45    for (mut animator, mut query, handle) in animators.iter_mut() {
46        let Some(asset) = assets.get(handle) else { continue };
47        animator.progress += time.delta_seconds() / animator.animation.duration(asset) * animator.speed;
48        A::apply(&mut animator, &mut query, asset);
49    }
50}
51
52//=================================================================================
53//    Animation
54//=================================================================================
55
56/// Defines an animation and how that animation should update the world every tick. Any type that implements this trait can be given state by
57/// also implementing the `AnimationState` trait. To spawn an animation, you can use the `spawn_animation` method on the `Commands` struct.
58pub trait Animation : Sized {
59    
60    /// The asset that is associated with this animation. For example, aseprite animations are associated with the aseprite asset.
61    type AsociatedAsset : Asset;
62    
63    /// A query that will allow the animation to effect the component it is attached to.
64    type Query<'w, 's> : QueryData;
65    
66    /// This method defines what the animation should do to the component it is attached to every tick.
67    fn apply(
68        animator : &Animator<Self>, 
69        items : &mut <Self::Query<'_, '_> as WorldQuery>::Item<'_>,
70        asset : &Self::AsociatedAsset,
71    );
72    
73    /// Describes how to spawn the animation in the world. This method is used by the `spawn_animation` method on the `Commands` struct.
74    fn spawn(animation : Option<Self>, world: &mut World, path : String, entity : Entity);
75    
76    /// Given the state of the animation, should return the duration of the animation in seconds.
77    fn duration(&self, asset : &Self::AsociatedAsset) -> f32;
78}
79
80//=================================================================================
81//    Animator
82//=================================================================================
83
84/// This is the component that will animate the entity it is attached to based on the embeded animation. 
85/// It will hold the progress of the animation.
86#[derive(Component)]
87pub struct Animator<A : Animation> {
88    pub animation: A,
89    pub speed : f32,
90    progress : f32,
91}
92
93impl <A : Animation + Default> Default for Animator<A> {
94    fn default() -> Self {
95        Animator {
96            animation : A::default(),
97            progress : 0.0,
98            speed : 1.0,
99        }
100    }
101}
102
103impl <A : Animation> Animator<A> {
104    /// Creates a new animator with the given animation.
105    pub fn new(current_state : A) -> Self {
106        Animator {
107            animation: current_state,
108            progress : 0.0,
109            speed : 1.0,
110        }
111    }
112    
113    /// Sets the speed of the animation. 1.0 is normal speed, 0.5 is half speed, 2.0 is double speed, etc.
114    pub fn set_animation(&mut self, animation : A) {
115        self.animation = animation;
116    }
117    
118    /// Sets the animation's progress to 0.0.
119    pub fn reset(&mut self) {
120        self.progress = 0.0;
121    }
122    
123    /// Gets the progress of the animation. This is a value between 0.0 and 1.0.
124    pub fn progress(&self) -> f32 {
125        self.progress.fract()
126    }
127    
128    /// Gets the number of repititions the animation has gone through.
129    pub fn repititions(&self) -> u32 {
130        self.progress.floor() as u32
131    }
132    
133    /// Returns a number that represents the total progress of the animation and the repititions. 
134    /// For example, if the animation has gone through 2 repititions and is 50% through the 3rd repitition, this will return 2.5.
135    pub fn total_progress(&self) -> f32 {
136        self.progress
137    }
138}
139