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()
},
));
}