use argh::FromArgs; use bevy::{prelude::*, utils::HashMap, utils::HashSet}; use bevy_spatial::{kdtree::KDTree3, SpatialAccess}; pub type NNTree = KDTree3; // toid const SPEED: f32 = 1.0; const SPEED_DIFF_RANGE: f32 = 0.08; // +/- 8% // how far from origin before really wanting to come back const RADIUS: f32 = 800.0; // how close to try to stay to your buddies const BUDDY_RADIUS: f32 = 15.0; // how much room to try to maintain between toids const PERSONAL_SPACE: f32 = 1.5; const MIN_ALTITUDE: f32 = 1.5; #[derive(Debug, FromArgs, Resource)] /// Toid Watching pub struct Config { /// how many Toids to spawn #[argh(option, short = 't', default = "100")] pub toids: usize, } #[derive(Clone, Debug, Default, Deref, DerefMut, Resource)] pub struct Positions(pub HashMap); #[derive(Component, Debug, Clone, Default)] pub struct Buddies(HashSet); #[derive(Component, Debug, Clone, Deref, DerefMut, Default)] pub struct Velocity(Vec3); #[derive(Component)] pub struct Toid { pub speed: f32, pub buddies: usize, } pub fn turkey_time( commands: &mut Commands, scene: &Handle, r: &mut impl rand::prelude::Rng, ) -> Entity { let speed_diff = r.gen_range(-SPEED_DIFF_RANGE..=SPEED_DIFF_RANGE); let speed = SPEED + (SPEED * speed_diff); let vel = unit_vec(r) * speed; let buddies = r.gen_range(6..=8); let x = r.gen_range(-5.0..=5.0); let z = r.gen_range(-5.0..=5.0); let y = r.gen_range(0.1..=2.5); let spatial_bundle = SpatialBundle { transform: Transform::from_xyz(x, MIN_ALTITUDE + y, z), ..Default::default() }; commands .spawn(spatial_bundle) .insert((Velocity(vel), Buddies::default(), Toid { speed, buddies })) .with_children(|t| { t.spawn(SceneBundle { scene: scene.to_owned(), ..Default::default() }) .insert(Transform::from_rotation(Quat::from_axis_angle( Vec3::Y, -std::f32::consts::FRAC_PI_2, ))); }) .id() } pub fn update_vel( mut toids: Query<(&Transform, &mut Velocity, &Toid, &Buddies)>, positions: Res, ) { for (xform, mut vel, toid, buddies) in toids.iter_mut() { let speed = toid.speed; let mut dir = vel.normalize(); let pos = xform.translation; // nudge up if too low if pos.y < MIN_ALTITUDE { let dh = MIN_ALTITUDE - pos.y; let pct = (dh / MIN_ALTITUDE).min(1.0).powi(2); let rot = Quat::from_rotation_arc(dir, Vec3::Y); let rot = Quat::IDENTITY.slerp(rot, pct); dir = rot.mul_vec3(dir).normalize(); } // nudge toward origin if too far { let dist = pos.length(); let toward_origin = -pos.normalize(); let rot = Quat::from_rotation_arc(dir, toward_origin); let pct = (dist / RADIUS).min(1.0); let rot = Quat::IDENTITY.slerp(rot, pct); dir = rot.mul_vec3(dir).normalize(); } // find buddies and orient; point more towards further-away buddies for buddy in buddies.0.iter() { let bp = positions.get(buddy).unwrap_or(&pos); let bdir = *bp - pos; let dist = bdir.length(); let rot = Quat::from_rotation_arc(dir, bdir.normalize()); let s = (dist / (BUDDY_RADIUS * 1.2)).min(1.0); let rot = Quat::IDENTITY.slerp(rot, s); dir = rot.mul_vec3(dir).normalize(); } **vel = dir * speed; } } pub fn update_pos( mut toids: Query<(&mut Transform, &Velocity, Entity), With>, mut positions: ResMut, time: Res