use bevy_ecs::{
component::Component,
reflect::ReflectComponent,
system::{Query, Res},
};
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
use bevy_time::Time;
use core::time::Duration;
use crate::{graph::AnimationNodeIndex, ActiveAnimation, AnimationPlayer};
#[derive(Component, Default, Reflect)]
#[reflect(Component, Default, Clone)]
pub struct AnimationTransitions {
main_animation: Option<AnimationNodeIndex>,
transitions: Vec<AnimationTransition>,
}
impl Clone for AnimationTransitions {
fn clone(&self) -> Self {
Self {
main_animation: self.main_animation,
transitions: self.transitions.clone(),
}
}
fn clone_from(&mut self, source: &Self) {
self.main_animation = source.main_animation;
self.transitions.clone_from(&source.transitions);
}
}
#[derive(Debug, Clone, Copy, Reflect)]
#[reflect(Clone)]
pub struct AnimationTransition {
current_weight: f32,
weight_decline_per_sec: f32,
animation: AnimationNodeIndex,
}
impl AnimationTransitions {
pub fn new() -> AnimationTransitions {
AnimationTransitions::default()
}
pub fn play<'p>(
&mut self,
player: &'p mut AnimationPlayer,
new_animation: AnimationNodeIndex,
transition_duration: Duration,
) -> &'p mut ActiveAnimation {
if let Some(old_animation_index) = self.main_animation.replace(new_animation)
&& let Some(old_animation) = player.animation_mut(old_animation_index)
&& !old_animation.is_paused()
{
self.transitions.push(AnimationTransition {
current_weight: old_animation.weight,
weight_decline_per_sec: 1.0 / transition_duration.as_secs_f32(),
animation: old_animation_index,
});
}
self.transitions
.retain(|transition| transition.animation != new_animation);
player.start(new_animation)
}
pub fn get_main_animation(&self) -> Option<AnimationNodeIndex> {
self.main_animation
}
}
pub fn advance_transitions(
mut query: Query<(&mut AnimationTransitions, &mut AnimationPlayer)>,
time: Res<Time>,
) {
for (mut animation_transitions, mut player) in query.iter_mut() {
let mut remaining_weight = 1.0;
for transition in &mut animation_transitions.transitions.iter_mut().rev() {
transition.current_weight = (transition.current_weight
- transition.weight_decline_per_sec * time.delta_secs())
.max(0.0);
let Some(ref mut animation) = player.animation_mut(transition.animation) else {
continue;
};
animation.weight = transition.current_weight * remaining_weight;
remaining_weight -= animation.weight;
}
if let Some(main_animation_index) = animation_transitions.main_animation
&& let Some(ref mut animation) = player.animation_mut(main_animation_index)
{
animation.weight = remaining_weight;
}
}
}
pub fn expire_completed_transitions(
mut query: Query<(&mut AnimationTransitions, &mut AnimationPlayer)>,
) {
for (mut animation_transitions, mut player) in query.iter_mut() {
animation_transitions.transitions.retain(|transition| {
let expire = transition.current_weight <= 0.0;
if expire {
player.stop(transition.animation);
}
!expire
});
}
}