bevy_support_misc 0.9.0

My tiny common code to share between bevy game projects
Documentation
use crate::DummyState;
use bevy::app::{
    App,
    Update,
};
use bevy::math::Vec3;
use bevy::prelude::{
    default,
    in_state,
    Commands,
    Component,
    Curve,
    EaseFunction,
    EasingCurve,
    Entity,
    EntityEvent,
    IntoScheduleConfigs,
    Plugin,
    Query,
    Res,
    States,
    Time,
    Transform,
    UiTransform,
    Vec2,
    Vec3Swizzles,
};

pub struct ScaleEasingPlugin<T>
where
    T: States,
{
    pub states: Option<Vec<T>>,
}

impl<T> ScaleEasingPlugin<T>
where
    T: States,
{
    pub fn new(states: Vec<T>) -> Self {
        Self { states: Some(states) }
    }
    pub fn any() -> Self {
        Self { states: None }
    }
}

impl<T> Plugin for ScaleEasingPlugin<T>
where
    T: States,
{
    fn build(&self, app: &mut App) {
        if let Some(states) = &self.states {
            for state in states {
                app.add_systems(Update, easing.run_if(in_state(state.clone())));
            }
        } else {
            app.add_systems(Update, easing);
        }
    }
}

pub struct ScaleEasingPluginAnyState;

impl ScaleEasingPluginAnyState {
    pub fn new() -> ScaleEasingPlugin<DummyState> {
        ScaleEasingPlugin::any()
    }
}

#[derive(EntityEvent)]
pub struct ScaleEasingEnded {
    pub entity: Entity,
    pub ease_function: EaseFunction,
}

#[derive(Component, Default, Clone)]
pub struct ScaleEasingEffect {
    pub scale_gap: Vec3,
    pub scale_orig: Option<Vec3>,
    pub scale_start: Option<Vec3>,
    pub duration_ms: u128,
    elapsed: u128,
    pub function: Option<EaseFunction>,
    pub repeat: bool,
    pub pause: bool,
}

impl ScaleEasingEffect {
    pub fn orig_scale(&self) -> Option<Vec3> {
        self.scale_orig
    }

    pub fn popout(scale_gap: Vec3, duration_ms: u128) -> Self {
        Self {
            scale_gap,
            duration_ms,
            elapsed: 0,
            function: Some(EaseFunction::ElasticOut),
            ..default()
        }
    }

    pub fn with_duration(mut self, duration_ms: u128) -> Self {
        self.duration_ms = duration_ms;
        self
    }

    pub fn with_scale_gap(mut self, scale: Vec3) -> Self {
        self.scale_gap = scale;
        self
    }

    pub fn with_ease_function(mut self, ease_function: EaseFunction) -> Self {
        self.function = Some(ease_function);
        self
    }

    pub fn with_repeat(mut self, repeat: bool) -> Self {
        self.repeat = repeat;
        self
    }

    pub fn on_going(&self) -> bool {
        self.elapsed > 0 && self.elapsed < self.duration_ms && !self.pause
    }

    pub fn reset(&mut self) {
        self.elapsed = 0;
    }

    pub fn pause(&mut self) {
        self.pause = true;
    }

    pub fn unpause(&mut self) {
        self.pause = false;
    }
}

fn easing(
    mut commands: Commands,
    mut query: Query<(
        Option<&mut Transform>,
        Option<&mut UiTransform>,
        &mut ScaleEasingEffect,
        Entity,
    )>,
    time: Res<Time>,
) {
    let delta = time.delta().as_millis();
    for (transform, ui_transform, mut easing, entity) in query.iter_mut() {
        if easing.pause || easing.elapsed > easing.duration_ms || easing.function.is_none() {
            continue;
        }

        let f = EasingCurve::new(0.0, 1.0, easing.function.unwrap());

        if let Some(ui_transform) = ui_transform.as_ref() {
            if easing.scale_orig.is_none() {
                easing.scale_orig = Some(Vec3::from((ui_transform.scale, 0.)));
            }
        } else if let Some(transform) = transform.as_ref() {
            if easing.scale_orig.is_none() {
                easing.scale_orig = Some(transform.scale);
            }
        }

        if easing.scale_start.is_none() {
            easing.scale_start = easing.scale_orig;
        }

        easing.elapsed += delta;
        let percent = easing.elapsed as f32 / easing.duration_ms as f32;
        let rate = f.sample(percent).unwrap_or(1.);
        if let Some(start_scale) = easing.scale_start {
            if let Some(mut ui_transform) = ui_transform {
                ui_transform.scale = start_scale.xy() + easing.scale_gap.xy() * Vec2::splat(rate);
            }
            if let Some(mut transform) = transform {
                transform.scale = start_scale + easing.scale_gap * Vec3::splat(rate);
            }
        }

        if percent >= 1. {
            commands.trigger(ScaleEasingEnded {
                entity,
                ease_function: easing.function.unwrap(),
            });
            if easing.repeat {
                easing.reset();
            }
        }
    }
}