working-ish boids
This commit is contained in:
14
Cargo.lock
generated
14
Cargo.lock
generated
@@ -2957,6 +2957,13 @@ dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust-boids"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bevy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "1.1.0"
|
||||
@@ -3174,13 +3181,6 @@ dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "test-game"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bevy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.57"
|
||||
|
||||
145
src/boids.rs
145
src/boids.rs
@@ -1,43 +1,142 @@
|
||||
use bevy::prelude::*;
|
||||
use bevy::{
|
||||
prelude::*,
|
||||
sprite::{MaterialMesh2dBundle, Mesh2dHandle},
|
||||
window::PrimaryWindow,
|
||||
};
|
||||
pub struct Boids;
|
||||
|
||||
impl Plugin for Boids {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.insert_resource(PrintTimer(Timer::from_seconds(2.0, TimerMode::Repeating)))
|
||||
.add_systems(Startup, add_boids)
|
||||
.add_systems(Update, (update_boid, print_boid).chain());
|
||||
app.insert_resource(ClearColor(Color::rgb(0.09, 0., 0.0390625)))
|
||||
.add_systems(Startup, init)
|
||||
.add_systems(Update, (update_boid_vel, wrap_boids, simulate_boids));
|
||||
}
|
||||
}
|
||||
|
||||
fn print_boid(time: Res<Time>, mut timer: ResMut<PrintTimer>, query: Query<&Name, With<Boid>>) {
|
||||
if !timer.0.tick(time.delta()).just_finished() {
|
||||
return;
|
||||
fn simulate_boids(time: Res<Time>, mut boids: Query<(&Transform, &mut Vel), With<Boid>>) {
|
||||
let mut combos = boids.iter_combinations_mut();
|
||||
while let Some([mut a, mut b]) = combos.fetch_next() {
|
||||
let mut a_total_vel = a.1.value;
|
||||
let mut b_total_vel = b.1.value;
|
||||
//Match Speed
|
||||
let avg_vel = (a.1.value + b.1.value) / 2.;
|
||||
a_total_vel += ((avg_vel - a_total_vel) / 8.) * time.delta_seconds();
|
||||
b_total_vel -= ((avg_vel - b_total_vel) / 8.) * time.delta_seconds();
|
||||
|
||||
//Collision Avoidance
|
||||
let dist = a.0.translation - b.0.translation;
|
||||
if dist.length_squared() < 50. * 50. {
|
||||
a_total_vel += dist * time.elapsed_seconds();
|
||||
b_total_vel += -dist * time.elapsed_seconds();
|
||||
} else {
|
||||
//Flocking
|
||||
let avg = (a.0.translation + b.0.translation) / 2.;
|
||||
a_total_vel -= avg * 0.05 * time.elapsed_seconds();
|
||||
b_total_vel += avg * 0.05 * time.elapsed_seconds();
|
||||
}
|
||||
|
||||
// println!("{}", a_total_vel.length());
|
||||
a.1.value = a_total_vel;
|
||||
b.1.value = b_total_vel;
|
||||
}
|
||||
for name in &query {
|
||||
println!("{}", name.0);
|
||||
|
||||
for (t, mut v) in &mut boids {
|
||||
//Tend to Center
|
||||
if t.translation.length_squared() > 500. * 500. {
|
||||
v.value += -t.translation * time.elapsed_seconds() * 0.3;
|
||||
}
|
||||
|
||||
//Limit Velocity
|
||||
if v.value == Vec3::ZERO {
|
||||
continue;
|
||||
}
|
||||
v.value = limit_velocity(&v);
|
||||
}
|
||||
}
|
||||
|
||||
fn update_boid(mut query: Query<&mut Name, With<Boid>>) {
|
||||
for mut name in &mut query {
|
||||
if name.0 == "B1" {
|
||||
name.0 = "B1.Updated".to_string();
|
||||
break;
|
||||
fn limit_velocity(v: &Vel) -> Vec3 {
|
||||
let max_speed = 200.;
|
||||
if v.value.length_squared() > max_speed * max_speed {
|
||||
return v.value.normalize() * max_speed;
|
||||
}
|
||||
return v.value;
|
||||
}
|
||||
|
||||
fn update_boid_vel(time: Res<Time>, mut query: Query<(&mut Transform, &Vel), With<Boid>>) {
|
||||
for (mut t, v) in &mut query {
|
||||
t.translation += v.value * time.delta_seconds();
|
||||
if v.value == Vec3::ZERO {
|
||||
continue;
|
||||
}
|
||||
t.rotation = Quat::from_rotation_arc(Vec3::Y, v.value.normalize());
|
||||
}
|
||||
}
|
||||
|
||||
fn wrap_boids(
|
||||
cam_query: Query<(&Camera, &GlobalTransform)>,
|
||||
window: Query<&Window, With<PrimaryWindow>>,
|
||||
mut boids: Query<(&mut Transform, &mut Vel), With<Boid>>,
|
||||
) {
|
||||
let (cam, cam_pos) = cam_query.single();
|
||||
let w = window.single();
|
||||
let (min, max) = (
|
||||
cam.viewport_to_world_2d(cam_pos, (0., w.height()).into())
|
||||
.unwrap(),
|
||||
cam.viewport_to_world_2d(cam_pos, (w.width(), 0.).into())
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
for (mut t, mut v) in &mut boids {
|
||||
if t.translation.x < min.x || t.translation.x > max.x {
|
||||
v.value.x = -v.value.x;
|
||||
}
|
||||
if t.translation.y < min.y || t.translation.y > max.y {
|
||||
v.value.y = -v.value.y;
|
||||
}
|
||||
|
||||
t.translation = t.translation.min(max.extend(0.)).max(min.extend(0.));
|
||||
}
|
||||
}
|
||||
|
||||
fn init(
|
||||
mut commands: Commands,
|
||||
mut meshes: ResMut<Assets<Mesh>>,
|
||||
mut materials: ResMut<Assets<ColorMaterial>>,
|
||||
) {
|
||||
commands.spawn(Camera2dBundle::default());
|
||||
|
||||
let size = 20;
|
||||
|
||||
for x in 0..size {
|
||||
for y in 0..size {
|
||||
let shape = Mesh2dHandle(meshes.add(Triangle2d::new(
|
||||
Vec2::Y * 5.0,
|
||||
Vec2::new(-5.0, -5.0),
|
||||
Vec2::new(5.0, -5.0),
|
||||
)));
|
||||
let color = Color::hsl(360.0 * x as f32 / 10.0, 0.75, 0.7);
|
||||
commands.spawn((
|
||||
Boid,
|
||||
Vel { value: Vec3::ZERO },
|
||||
Name(format!("B{x}").into()),
|
||||
MaterialMesh2dBundle {
|
||||
mesh: shape,
|
||||
material: materials.add(color),
|
||||
transform: Transform::from_xyz(x as f32 * 8.0, y as f32 * 8.0, 0.0),
|
||||
..default()
|
||||
},
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn add_boids(mut commands: Commands) {
|
||||
commands.spawn((Boid, Name("B1".to_string())));
|
||||
commands.spawn((Boid, Name("B2".to_string())));
|
||||
commands.spawn((Boid, Name("B3".to_string())));
|
||||
}
|
||||
|
||||
#[derive(Resource)]
|
||||
struct PrintTimer(Timer);
|
||||
|
||||
#[derive(Component)]
|
||||
struct Boid;
|
||||
|
||||
#[derive(Component)]
|
||||
struct Name(String);
|
||||
|
||||
#[derive(Component)]
|
||||
struct Vel {
|
||||
pub value: Vec3,
|
||||
}
|
||||
|
||||
20
src/main.rs
20
src/main.rs
@@ -2,5 +2,23 @@ use bevy::prelude::*;
|
||||
mod boids;
|
||||
|
||||
fn main() {
|
||||
App::new().add_plugins((DefaultPlugins, boids::Boids)).run();
|
||||
App::new()
|
||||
.add_plugins((
|
||||
DefaultPlugins.set(WindowPlugin {
|
||||
primary_window: Some(Window {
|
||||
title: "Boids".into(),
|
||||
name: Some("rest.boids".into()),
|
||||
resolution: (1920.0, 1080.0).into(),
|
||||
resizable: false,
|
||||
enabled_buttons: bevy::window::EnabledButtons{
|
||||
maximize: false,
|
||||
..Default::default()
|
||||
},
|
||||
..default()
|
||||
}),
|
||||
..default()
|
||||
}),
|
||||
boids::Boids,
|
||||
))
|
||||
.run();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user