working-ish boids
This commit is contained in:
14
Cargo.lock
generated
14
Cargo.lock
generated
@@ -2957,6 +2957,13 @@ dependencies = [
|
|||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rust-boids"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"bevy",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc-hash"
|
name = "rustc-hash"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
@@ -3174,13 +3181,6 @@ dependencies = [
|
|||||||
"winapi-util",
|
"winapi-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "test-game"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"bevy",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.57"
|
version = "1.0.57"
|
||||||
|
|||||||
139
src/boids.rs
139
src/boids.rs
@@ -1,43 +1,142 @@
|
|||||||
use bevy::prelude::*;
|
use bevy::{
|
||||||
|
prelude::*,
|
||||||
|
sprite::{MaterialMesh2dBundle, Mesh2dHandle},
|
||||||
|
window::PrimaryWindow,
|
||||||
|
};
|
||||||
pub struct Boids;
|
pub struct Boids;
|
||||||
|
|
||||||
impl Plugin for Boids {
|
impl Plugin for Boids {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.insert_resource(PrintTimer(Timer::from_seconds(2.0, TimerMode::Repeating)))
|
app.insert_resource(ClearColor(Color::rgb(0.09, 0., 0.0390625)))
|
||||||
.add_systems(Startup, add_boids)
|
.add_systems(Startup, init)
|
||||||
.add_systems(Update, (update_boid, print_boid).chain());
|
.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>>) {
|
fn simulate_boids(time: Res<Time>, mut boids: Query<(&Transform, &mut Vel), With<Boid>>) {
|
||||||
if !timer.0.tick(time.delta()).just_finished() {
|
let mut combos = boids.iter_combinations_mut();
|
||||||
return;
|
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();
|
||||||
}
|
}
|
||||||
for name in &query {
|
|
||||||
println!("{}", name.0);
|
// println!("{}", a_total_vel.length());
|
||||||
|
a.1.value = a_total_vel;
|
||||||
|
b.1.value = b_total_vel;
|
||||||
|
}
|
||||||
|
|
||||||
|
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>>) {
|
fn limit_velocity(v: &Vel) -> Vec3 {
|
||||||
for mut name in &mut query {
|
let max_speed = 200.;
|
||||||
if name.0 == "B1" {
|
if v.value.length_squared() > max_speed * max_speed {
|
||||||
name.0 = "B1.Updated".to_string();
|
return v.value.normalize() * max_speed;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
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 add_boids(mut commands: Commands) {
|
fn wrap_boids(
|
||||||
commands.spawn((Boid, Name("B1".to_string())));
|
cam_query: Query<(&Camera, &GlobalTransform)>,
|
||||||
commands.spawn((Boid, Name("B2".to_string())));
|
window: Query<&Window, With<PrimaryWindow>>,
|
||||||
commands.spawn((Boid, Name("B3".to_string())));
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Resource)]
|
t.translation = t.translation.min(max.extend(0.)).max(min.extend(0.));
|
||||||
struct PrintTimer(Timer);
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
|
},
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
struct Boid;
|
struct Boid;
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
struct Name(String);
|
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;
|
mod boids;
|
||||||
|
|
||||||
fn main() {
|
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