spring_motion 0.1.0

Small&simple spring spring motion library
Documentation
use bevy::prelude::*;
use bevy::sprite::MaterialMesh2dBundle;
use spring_motion::{SpringDescription, SpringSystem};

#[derive(Component)]
struct SpringState {
    position: SpringSystem<Vec2>,
}

impl Default for SpringState {
    fn default() -> Self {
        Self {
            position: SpringSystem::new(
                SpringDescription {
                    frequency: 3.,
                    damping: 0.5,
                    initial_response: 0.,
                },
                Vec2::splat(0.),
                Vec2::splat(0.),
            ),
        }
    }
}

#[derive(Component, Default)]
struct Target(Vec2);

fn track_mouse(
    mut query: Query<&mut Target>,
    mut mouse: EventReader<CursorMoved>,
    camera_query: Query<(&Camera, &GlobalTransform)>,
) {
    let (camera, camera_transform) = camera_query.single();

    let mut target = query.single_mut();

    for event in mouse.iter() {
        if let Some(mouse_position) = camera
            .viewport_to_world(camera_transform, event.position)
            .map(|r| r.origin.truncate())
        {
            target.0 = mouse_position;
        }
    }
}

fn animate_spring(mut query: Query<(&mut SpringState, &mut Transform, &Target)>, time: Res<Time>) {
    for (mut spring, mut transform, target) in &mut query {
        let position = spring.position.step_clamped(time.delta_seconds(), target.0);

        *transform = Transform::from_translation(Vec3::from((position, 0.)))
    }
}

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_startup_system(setup)
        .add_systems((track_mouse, animate_spring))
        .run();
}

fn setup(
    mut commands: Commands,
    mut meshes: ResMut<Assets<Mesh>>,
    mut materials: ResMut<Assets<ColorMaterial>>,
) {
    commands.spawn(Camera2dBundle::default());
    commands.spawn((
        Target::default(),
        SpringState::default(),
        MaterialMesh2dBundle {
            mesh: meshes.add(shape::Circle::new(50.).into()).into(),
            material: materials.add(ColorMaterial::from(Color::PURPLE)),
            transform: Transform::from_translation(Vec3::new(-150., 0., 0.)),
            ..default()
        },
    ));
}