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