3
3
#![ warn( missing_docs) ]
4
4
5
5
use std:: ops:: Deref ;
6
+ use std:: time:: Duration ;
6
7
7
8
use bevy_app:: { App , CoreStage , Plugin } ;
8
9
use bevy_asset:: { AddAsset , Assets , Handle } ;
9
10
use bevy_core:: Name ;
10
11
use bevy_ecs:: {
11
- change_detection:: DetectChanges ,
12
+ change_detection:: { DetectChanges , Mut } ,
12
13
entity:: Entity ,
13
14
prelude:: Component ,
14
15
query:: With ,
@@ -114,22 +115,18 @@ impl AnimationClip {
114
115
}
115
116
}
116
117
117
- /// Animation controls
118
- #[ derive( Component , Reflect ) ]
119
- #[ reflect( Component ) ]
120
- pub struct AnimationPlayer {
121
- paused : bool ,
118
+ #[ derive( Reflect ) ]
119
+ struct PlayingAnimation {
122
120
repeat : bool ,
123
121
speed : f32 ,
124
122
elapsed : f32 ,
125
123
animation_clip : Handle < AnimationClip > ,
126
124
path_cache : Vec < Vec < Option < Entity > > > ,
127
125
}
128
126
129
- impl Default for AnimationPlayer {
127
+ impl Default for PlayingAnimation {
130
128
fn default ( ) -> Self {
131
129
Self {
132
- paused : false ,
133
130
repeat : false ,
134
131
speed : 1.0 ,
135
132
elapsed : 0.0 ,
@@ -139,33 +136,106 @@ impl Default for AnimationPlayer {
139
136
}
140
137
}
141
138
139
+ /// An animation that is being faded out as part of a transition
140
+ struct AnimationTransition {
141
+ /// The current weight. Starts at 1.0 and goes to 0.0 during the fade-out.
142
+ current_weight : f32 ,
143
+ /// How much to decrease `current_weight` per second
144
+ weight_decline_per_sec : f32 ,
145
+ /// The animation that is being faded out
146
+ animation : PlayingAnimation ,
147
+ }
148
+
149
+ /// Animation controls
150
+ #[ derive( Component , Default , Reflect ) ]
151
+ #[ reflect( Component ) ]
152
+ pub struct AnimationPlayer {
153
+ paused : bool ,
154
+
155
+ animation : PlayingAnimation ,
156
+
157
+ // List of previous animations we're currently transitioning away from.
158
+ // Usually this is empty, when transitioning between animations, there is
159
+ // one entry. When another animation transition happens while a transition
160
+ // is still ongoing, then there can be more than one entry.
161
+ // Once a transition is finished, it will be automatically removed from the list
162
+ #[ reflect( ignore) ]
163
+ transitions : Vec < AnimationTransition > ,
164
+ }
165
+
142
166
impl AnimationPlayer {
143
167
/// Start playing an animation, resetting state of the player
168
+ /// This will use a linear blending between the previous and the new animation to make a smooth transition
144
169
pub fn start ( & mut self , handle : Handle < AnimationClip > ) -> & mut Self {
145
- * self = Self {
170
+ self . animation = PlayingAnimation {
146
171
animation_clip : handle,
147
172
..Default :: default ( )
148
173
} ;
174
+
175
+ // We want a hard transition.
176
+ // In case any previous transitions are still playing, stop them
177
+ self . transitions . clear ( ) ;
178
+
179
+ self
180
+ }
181
+
182
+ /// Start playing an animation, resetting state of the player
183
+ /// This will use a linear blending between the previous and the new animation to make a smooth transition
184
+ pub fn start_with_transition (
185
+ & mut self ,
186
+ handle : Handle < AnimationClip > ,
187
+ transition_duration : Duration ,
188
+ ) -> & mut Self {
189
+ let mut animation = PlayingAnimation {
190
+ animation_clip : handle,
191
+ ..Default :: default ( )
192
+ } ;
193
+ std:: mem:: swap ( & mut animation, & mut self . animation ) ;
194
+
195
+ // Add the current transition. If other transitions are still ongoing,
196
+ // this will keep those transitions running and cause a transition between
197
+ // the output of that previous transition to the new animation.
198
+ self . transitions . push ( AnimationTransition {
199
+ current_weight : 1.0 ,
200
+ weight_decline_per_sec : 1.0 / transition_duration. as_secs_f32 ( ) ,
201
+ animation,
202
+ } ) ;
203
+
149
204
self
150
205
}
151
206
152
207
/// Start playing an animation, resetting state of the player, unless the requested animation is already playing.
208
+ /// If `transition_duration` is set, this will use a linear blending
209
+ /// between the previous and the new animation to make a smooth transition
153
210
pub fn play ( & mut self , handle : Handle < AnimationClip > ) -> & mut Self {
154
- if self . animation_clip != handle || self . is_paused ( ) {
211
+ if self . animation . animation_clip != handle || self . is_paused ( ) {
155
212
self . start ( handle) ;
156
213
}
157
214
self
158
215
}
159
216
217
+ /// Start playing an animation, resetting state of the player, unless the requested animation is already playing.
218
+ /// This will use a linear blending between the previous and the new animation to make a smooth transition
219
+ pub fn play_with_transition (
220
+ & mut self ,
221
+ handle : Handle < AnimationClip > ,
222
+ transition_duration : Duration ,
223
+ ) -> & mut Self {
224
+ if self . animation . animation_clip != handle || self . is_paused ( ) {
225
+ self . start_with_transition ( handle, transition_duration) ;
226
+ }
227
+ self
228
+ }
229
+
160
230
/// Set the animation to repeat
161
231
pub fn repeat ( & mut self ) -> & mut Self {
162
- self . repeat = true ;
232
+ self . animation . repeat = true ;
163
233
self
164
234
}
165
235
166
236
/// Stop the animation from repeating
167
237
pub fn stop_repeating ( & mut self ) -> & mut Self {
168
- self . repeat = false ;
238
+ self . animation . repeat = false ;
169
239
self
170
240
}
171
241
@@ -186,23 +256,23 @@ impl AnimationPlayer {
186
256
187
257
/// Speed of the animation playback
188
258
pub fn speed ( & self ) -> f32 {
189
- self . speed
259
+ self . animation . speed
190
260
}
191
261
192
262
/// Set the speed of the animation playback
193
263
pub fn set_speed ( & mut self , speed : f32 ) -> & mut Self {
194
- self . speed = speed;
264
+ self . animation . speed = speed;
195
265
self
196
266
}
197
267
198
268
/// Time elapsed playing the animation
199
269
pub fn elapsed ( & self ) -> f32 {
200
- self . elapsed
270
+ self . animation . elapsed
201
271
}
202
272
203
273
/// Seek to a specific time in the animation
204
274
pub fn set_elapsed ( & mut self , elapsed : f32 ) -> & mut Self {
205
- self . elapsed = elapsed;
275
+ self . animation . elapsed = elapsed;
206
276
self
207
277
}
208
278
}
@@ -283,37 +353,119 @@ pub fn animation_player(
283
353
mut animation_players : Query < ( Entity , Option < & Parent > , & mut AnimationPlayer ) > ,
284
354
) {
285
355
animation_players. par_for_each_mut ( 10 , |( root, maybe_parent, mut player) | {
286
- let Some ( animation_clip) = animations. get ( & player. animation_clip ) else { return } ;
287
- // Continue if paused unless the `AnimationPlayer` was changed
288
- // This allow the animation to still be updated if the player.elapsed field was manually updated in pause
289
- if player. paused && !player. is_changed ( ) {
290
- return ;
291
- }
292
- if !player. paused {
293
- player. elapsed += time. delta_seconds ( ) * player. speed ;
356
+ update_transitions ( & mut player, & time) ;
357
+ run_animation_player (
358
+ root,
359
+ player,
360
+ & time,
361
+ & animations,
362
+ & names,
363
+ & transforms,
364
+ maybe_parent,
365
+ & parents,
366
+ & children,
367
+ ) ;
368
+ } ) ;
369
+ }
370
+
371
+ #[ allow( clippy:: too_many_arguments) ]
372
+ fn run_animation_player (
373
+ root : Entity ,
374
+ mut player : Mut < AnimationPlayer > ,
375
+ time : & Time ,
376
+ animations : & Assets < AnimationClip > ,
377
+ names : & Query < & Name > ,
378
+ transforms : & Query < & mut Transform > ,
379
+ maybe_parent : Option < & Parent > ,
380
+ parents : & Query < ( Option < With < AnimationPlayer > > , Option < & Parent > ) > ,
381
+ children : & Query < & Children > ,
382
+ ) {
383
+ let paused = player. paused ;
384
+ // Continue if paused unless the `AnimationPlayer` was changed
385
+ // This allow the animation to still be updated if the player.elapsed field was manually updated in pause
386
+ if paused && !player. is_changed ( ) {
387
+ return ;
388
+ }
389
+
390
+ // Apply the main animation
391
+ apply_animation (
392
+ 1.0 ,
393
+ & mut player. animation ,
394
+ paused,
395
+ root,
396
+ time,
397
+ animations,
398
+ names,
399
+ transforms,
400
+ maybe_parent,
401
+ parents,
402
+ children,
403
+ ) ;
404
+
405
+ // Apply any potential fade-out transitions from previous animations
406
+ for AnimationTransition {
407
+ current_weight,
408
+ animation,
409
+ ..
410
+ } in & mut player. transitions
411
+ {
412
+ apply_animation (
413
+ * current_weight,
414
+ animation,
415
+ paused,
416
+ root,
417
+ time,
418
+ animations,
419
+ names,
420
+ transforms,
421
+ maybe_parent,
422
+ parents,
423
+ children,
424
+ ) ;
425
+ }
426
+ }
427
+
428
+ #[ allow( clippy:: too_many_arguments) ]
429
+ fn apply_animation (
430
+ weight : f32 ,
431
+ animation : & mut PlayingAnimation ,
432
+ paused : bool ,
433
+ root : Entity ,
434
+ time : & Time ,
435
+ animations : & Assets < AnimationClip > ,
436
+ names : & Query < & Name > ,
437
+ transforms : & Query < & mut Transform > ,
438
+ maybe_parent : Option < & Parent > ,
439
+ parents : & Query < ( Option < With < AnimationPlayer > > , Option < & Parent > ) > ,
440
+ children : & Query < & Children > ,
441
+ ) {
442
+ if let Some ( animation_clip) = animations. get ( & animation. animation_clip ) {
443
+ if !paused {
444
+ animation. elapsed += time. delta_seconds ( ) * animation. speed ;
294
445
}
295
- let mut elapsed = player . elapsed ;
296
- if player . repeat {
446
+ let mut elapsed = animation . elapsed ;
447
+ if animation . repeat {
297
448
elapsed %= animation_clip. duration ;
298
449
}
299
450
if elapsed < 0.0 {
300
451
elapsed += animation_clip. duration ;
301
452
}
302
- if player . path_cache . len ( ) != animation_clip. paths . len ( ) {
303
- player . path_cache = vec ! [ Vec :: new( ) ; animation_clip. paths. len( ) ] ;
453
+ if animation . path_cache . len ( ) != animation_clip. paths . len ( ) {
454
+ animation . path_cache = vec ! [ Vec :: new( ) ; animation_clip. paths. len( ) ] ;
304
455
}
305
- if !verify_no_ancestor_player ( maybe_parent, & parents) {
456
+ if !verify_no_ancestor_player ( maybe_parent, parents) {
306
457
warn ! ( "Animation player on {:?} has a conflicting animation player on an ancestor. Cannot safely animate." , root) ;
307
458
return ;
308
459
}
460
+
309
461
for ( path, bone_id) in & animation_clip. paths {
310
- let cached_path = & mut player . path_cache [ * bone_id] ;
462
+ let cached_path = & mut animation . path_cache [ * bone_id] ;
311
463
let curves = animation_clip. get_curves ( * bone_id) . unwrap ( ) ;
312
- let Some ( target) = find_bone ( root, path, & children, & names, cached_path) else { continue } ;
464
+ let Some ( target) = find_bone ( root, path, children, names, cached_path) else { continue } ;
313
465
// SAFETY: The verify_no_ancestor_player check above ensures that two animation players cannot alias
314
466
// any of their descendant Transforms.
315
- //
316
- // The system scheduler prevents any other system from mutating Transforms at the same time,
467
+ //
468
+ // The system scheduler prevents any other system from mutating Transforms at the same time,
317
469
// so the only way this fetch can alias is if two AnimationPlayers are targetting the same bone.
318
470
// This can only happen if there are two or more AnimationPlayers are ancestors to the same
319
471
// entities. By verifying that there is no other AnimationPlayer in the ancestors of a
@@ -327,11 +479,16 @@ pub fn animation_player(
327
479
// Some curves have only one keyframe used to set a transform
328
480
if curve. keyframe_timestamps . len ( ) == 1 {
329
481
match & curve. keyframes {
330
- Keyframes :: Rotation ( keyframes) => transform. rotation = keyframes[ 0 ] ,
482
+ Keyframes :: Rotation ( keyframes) => {
483
+ transform. rotation = transform. rotation . slerp ( keyframes[ 0 ] , weight) ;
484
+ }
331
485
Keyframes :: Translation ( keyframes) => {
332
- transform. translation = keyframes[ 0 ] ;
486
+ transform. translation =
487
+ transform. translation . lerp ( keyframes[ 0 ] , weight) ;
488
+ }
489
+ Keyframes :: Scale ( keyframes) => {
490
+ transform. scale = transform. scale . lerp ( keyframes[ 0 ] , weight) ;
333
491
}
334
- Keyframes :: Scale ( keyframes) => transform. scale = keyframes[ 0 ] ,
335
492
}
336
493
continue ;
337
494
}
@@ -362,24 +519,31 @@ pub fn animation_player(
362
519
rot_end = -rot_end;
363
520
}
364
521
// Rotations are using a spherical linear interpolation
365
- transform . rotation =
366
- rot_start . normalize ( ) . slerp ( rot_end . normalize ( ) , lerp ) ;
522
+ let rot = rot_start . normalize ( ) . slerp ( rot_end . normalize ( ) , lerp ) ;
523
+ transform . rotation = transform . rotation . slerp ( rot , weight ) ;
367
524
}
368
525
Keyframes :: Translation ( keyframes) => {
369
526
let translation_start = keyframes[ step_start] ;
370
527
let translation_end = keyframes[ step_start + 1 ] ;
371
528
let result = translation_start. lerp ( translation_end, lerp) ;
372
- transform. translation = result;
529
+ transform. translation = transform . translation . lerp ( result, weight ) ;
373
530
}
374
531
Keyframes :: Scale ( keyframes) => {
375
532
let scale_start = keyframes[ step_start] ;
376
533
let scale_end = keyframes[ step_start + 1 ] ;
377
534
let result = scale_start. lerp ( scale_end, lerp) ;
378
- transform. scale = result;
535
+ transform. scale = transform . scale . lerp ( result, weight ) ;
379
536
}
380
537
}
381
538
}
382
539
}
540
+ }
541
+ }
542
+
543
+ fn update_transitions ( player : & mut AnimationPlayer , time : & Time ) {
544
+ player. transitions . retain_mut ( |animation| {
545
+ animation. current_weight -= animation. weight_decline_per_sec * time. delta_seconds ( ) ;
546
+ animation. current_weight > 0.0
383
547
} ) ;
384
548
}
385
549
0 commit comments