From 96c27d4b0d23f4e0d40203147109c8b363a8e36d Mon Sep 17 00:00:00 2001 From: Matthew Deville Date: Mon, 28 Sep 2026 23:17:46 +0200 Subject: [PATCH] track cursor --- src/boids.rs | 16 ++++++++++++++-- src/config.rs | 4 +++- src/main.rs | 8 +++++++- src/pointer.rs | 28 ++++++++++++++++++++++++++++ src/ui.rs | 1 + 5 files changed, 53 insertions(+), 4 deletions(-) create mode 100644 src/pointer.rs diff --git a/src/boids.rs b/src/boids.rs index 83d8866..8a4390f 100644 --- a/src/boids.rs +++ b/src/boids.rs @@ -3,6 +3,7 @@ use std::f32::consts::PI; use crate::{ config::ModelConfig, + pointer::PointerTarget, spatial_grid::{SpatialGrid, update_spatial_grid}, }; @@ -122,6 +123,7 @@ fn update_boids( window: Single<&Window, With>, time: Res>, grid: Res, + pointer: Res, mut query: Query<(Entity, &mut Boid, &mut Transform)>, ) { let dt = time.delta_secs(); @@ -194,8 +196,18 @@ fn update_boids( // Push each boid forward along its current heading let self_force = boid.velocity.normalize_or_zero() * model_config.self_acceleration; - let acceleration = - boundary_force + separation_force + alignment_force + cohesion_force + self_force; + // Pull towards the mouse cursor or touch point + let attraction_force = pointer.0.map_or(Vec2::ZERO, |target| { + (target - transform.translation.xy()).normalize_or_zero() + * model_config.attraction_force + }); + + let acceleration = boundary_force + + separation_force + + alignment_force + + cohesion_force + + self_force + + attraction_force; boid.velocity += acceleration * dt; boid.velocity = boid.velocity.clamp_length_max(model_config.max_velocity); diff --git a/src/config.rs b/src/config.rs index 9e64942..d1799b7 100644 --- a/src/config.rs +++ b/src/config.rs @@ -22,6 +22,7 @@ pub struct ModelConfig { pub cohesion_factor: f32, pub separation_factor: f32, pub self_acceleration: f32, + pub attraction_force: f32, } impl Default for ModelConfig { @@ -36,7 +37,8 @@ impl Default for ModelConfig { alignment_factor: 0.3, cohesion_factor: 0.2, separation_factor: 500.0, - self_acceleration: 30.0, + self_acceleration: 100.0, + attraction_force: 100.0, } } } diff --git a/src/main.rs b/src/main.rs index f349b58..b9eb092 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,6 @@ mod boids; mod config; +mod pointer; mod spatial_grid; mod ui; @@ -18,7 +19,12 @@ fn main() { }), ..default() })) - .add_plugins((config::ConfigPlugin, boids::BoidsPlugin, ui::ConfigUiPlugin)) + .add_plugins(( + config::ConfigPlugin, + pointer::PointerPlugin, + boids::BoidsPlugin, + ui::ConfigUiPlugin, + )) .add_systems(Startup, |mut commands: Commands| { commands.spawn(Camera2d); }) diff --git a/src/pointer.rs b/src/pointer.rs new file mode 100644 index 0000000..da945fe --- /dev/null +++ b/src/pointer.rs @@ -0,0 +1,28 @@ +use bevy::{prelude::*, window::PrimaryWindow}; + +pub struct PointerPlugin; + +impl Plugin for PointerPlugin { + fn build(&self, app: &mut App) { + app.init_resource::() + .add_systems(Update, update_pointer_target); + } +} + +/// World position of the active touch or mouse cursor, if any. +#[derive(Resource, Default)] +pub struct PointerTarget(pub Option); + +fn update_pointer_target( + window: Single<&Window, With>, + camera: Single<(&Camera, &GlobalTransform)>, + touches: Res, + mut target: ResMut, +) { + let (camera, camera_transform) = *camera; + // Touch and cursor positions are both in logical window pixels + target.0 = touches + .first_pressed_position() + .or_else(|| window.cursor_position()) + .and_then(|position| camera.viewport_to_world_2d(camera_transform, position).ok()); +} diff --git a/src/ui.rs b/src/ui.rs index 2b03db8..a1ce618 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -28,6 +28,7 @@ const CONFIG_FIELDS: &[(&str, f32)] = &[ ("cohesion_factor", 0.05), ("separation_factor", 10.0), ("self_acceleration", 5.0), + ("attraction_force", 10.0), ]; #[derive(Component)]