Flocking params as a resource & egui-inspector
The birdoid flocking parameters are now a resource named `FlockingParameters`. Adjustments can be made using the Egui inspector widget, although I plan to make a custom UI in the future.
This commit is contained in:
873
Cargo.lock
generated
873
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -6,6 +6,7 @@ license = "AGPL-3.0-only"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bevy = "0.16.0"
|
bevy = "0.16.0"
|
||||||
|
bevy-inspector-egui = "0.32"
|
||||||
|
|
||||||
# Grand-dependency pins
|
# Grand-dependency pins
|
||||||
# ab_glyph = "0.2.16"
|
# ab_glyph = "0.2.16"
|
||||||
|
|||||||
@@ -6,14 +6,12 @@ use bevy_spatial::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::birdoids::physics::{Force, Velocity, apply_velocity};
|
use crate::birdoids::physics::{Force, Velocity, apply_velocity};
|
||||||
|
use bevy_inspector_egui::{InspectorOptions, prelude::ReflectInspectorOptions};
|
||||||
|
|
||||||
const BACKGROUND_COLOR: Color = Color::srgb(0.4, 0.4, 0.4);
|
const BACKGROUND_COLOR: Color = Color::srgb(0.4, 0.4, 0.4);
|
||||||
const PLAYERBOID_COLOR: Color = Color::srgb(1.0, 0.0, 0.0);
|
const PLAYERBOID_COLOR: Color = Color::srgb(1.0, 0.0, 0.0);
|
||||||
const TURN_FACTOR: f32 = 1.0;
|
const TURN_FACTOR: f32 = 1.0;
|
||||||
const BOID_VIEW_RANGE: f32 = 15.0;
|
|
||||||
const COHESION_FACTOR: f32 = 1.0;
|
|
||||||
const SEPARATION_FACTOR: f32 = 5.0;
|
|
||||||
const ALIGNMENT_FACTOR: f32 = 2.0;
|
|
||||||
const SPACEBRAKES_COEFFICIENT: f32 = 0.5;
|
const SPACEBRAKES_COEFFICIENT: f32 = 0.5;
|
||||||
const LOW_SPEED_THRESHOLD: f32 = 50.0;
|
const LOW_SPEED_THRESHOLD: f32 = 50.0;
|
||||||
const HIGH_SPEED_THRESHOLD: f32 = 200.0;
|
const HIGH_SPEED_THRESHOLD: f32 = 200.0;
|
||||||
@@ -29,6 +27,8 @@ impl Plugin for BoidsPlugin {
|
|||||||
.with_spatial_ds(SpatialStructure::KDTree2),
|
.with_spatial_ds(SpatialStructure::KDTree2),
|
||||||
)
|
)
|
||||||
.insert_resource(ClearColor(BACKGROUND_COLOR))
|
.insert_resource(ClearColor(BACKGROUND_COLOR))
|
||||||
|
.insert_resource(FlockingParameters::new())
|
||||||
|
.register_type::<FlockingParameters>()
|
||||||
.add_systems(Startup, (spawn_camera, spawn_boids))
|
.add_systems(Startup, (spawn_camera, spawn_boids))
|
||||||
.add_systems(
|
.add_systems(
|
||||||
FixedUpdate,
|
FixedUpdate,
|
||||||
@@ -45,6 +45,26 @@ impl Plugin for BoidsPlugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(InspectorOptions, Reflect, Resource, Debug, Clone, Copy)]
|
||||||
|
#[reflect(Resource, InspectorOptions)]
|
||||||
|
pub(crate) struct FlockingParameters {
|
||||||
|
view_range: f32,
|
||||||
|
cohesion: f32,
|
||||||
|
separation: f32,
|
||||||
|
alignment: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FlockingParameters {
|
||||||
|
pub(crate) fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
view_range: 15.0,
|
||||||
|
cohesion: 1.0,
|
||||||
|
separation: 5.0,
|
||||||
|
alignment: 2.0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
#[require(Velocity, Force, TrackedByKdTree)]
|
#[require(Velocity, Force, TrackedByKdTree)]
|
||||||
pub(crate) struct Boid;
|
pub(crate) struct Boid;
|
||||||
@@ -157,6 +177,7 @@ fn check_keyboard(
|
|||||||
fn cohesion(
|
fn cohesion(
|
||||||
spatial_tree: Res<KDTree2<TrackedByKdTree>>,
|
spatial_tree: Res<KDTree2<TrackedByKdTree>>,
|
||||||
mut boids: Query<(Entity, &Transform, &mut Force), With<Boid>>,
|
mut boids: Query<(Entity, &Transform, &mut Force), With<Boid>>,
|
||||||
|
props: Res<FlockingParameters>,
|
||||||
) {
|
) {
|
||||||
// for each boid
|
// for each boid
|
||||||
// find neighbors
|
// find neighbors
|
||||||
@@ -165,7 +186,7 @@ fn cohesion(
|
|||||||
// apply force
|
// apply force
|
||||||
for (this_entt, transform, mut force) in &mut boids {
|
for (this_entt, transform, mut force) in &mut boids {
|
||||||
let (len, sum) = spatial_tree
|
let (len, sum) = spatial_tree
|
||||||
.within_distance(transform.translation.xy(), BOID_VIEW_RANGE)
|
.within_distance(transform.translation.xy(), props.view_range)
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|(pos, entt)| {
|
.filter_map(|(pos, entt)| {
|
||||||
// Skip self-comparison. A boid should not try to separate from itself.
|
// Skip self-comparison. A boid should not try to separate from itself.
|
||||||
@@ -183,15 +204,17 @@ fn cohesion(
|
|||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
let impulse = cohesive_force(center_of_mass, transform.translation.xy()).expect("damn");
|
let impulse = cohesive_force(center_of_mass, transform.translation.xy(), props.view_range)
|
||||||
|
.expect("damn");
|
||||||
|
|
||||||
force.0 -= impulse.0 * COHESION_FACTOR;
|
force.0 -= impulse.0 * props.cohesion;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn separation(
|
fn separation(
|
||||||
spatial_tree: Res<KDTree2<TrackedByKdTree>>,
|
spatial_tree: Res<KDTree2<TrackedByKdTree>>,
|
||||||
mut boids: Query<(Entity, &Transform, &mut Force), With<Boid>>,
|
mut boids: Query<(Entity, &Transform, &mut Force), With<Boid>>,
|
||||||
|
props: Res<FlockingParameters>,
|
||||||
) {
|
) {
|
||||||
// for each boid
|
// for each boid
|
||||||
// find neighbors
|
// find neighbors
|
||||||
@@ -199,7 +222,7 @@ fn separation(
|
|||||||
// apply force
|
// apply force
|
||||||
for (this_entt, tsfm, mut force) in &mut boids {
|
for (this_entt, tsfm, mut force) in &mut boids {
|
||||||
let impulse = spatial_tree
|
let impulse = spatial_tree
|
||||||
.within_distance(tsfm.translation.xy(), BOID_VIEW_RANGE / 4.0)
|
.within_distance(tsfm.translation.xy(), props.view_range / 4.0)
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|(pos, entt)| {
|
.filter_map(|(pos, entt)| {
|
||||||
// Skip self-comparison. A boid should not try to separate from itself.
|
// Skip self-comparison. A boid should not try to separate from itself.
|
||||||
@@ -213,10 +236,11 @@ fn separation(
|
|||||||
})
|
})
|
||||||
.fold(Vec3::ZERO, |acc, other| {
|
.fold(Vec3::ZERO, |acc, other| {
|
||||||
// let force = tsfm.translation - other;
|
// let force = tsfm.translation - other;
|
||||||
let force = separation_force(tsfm.translation.xy(), other.xy()).expect("angy");
|
let force = separation_force(tsfm.translation.xy(), other.xy(), props.view_range)
|
||||||
|
.expect("angy");
|
||||||
acc + force.0
|
acc + force.0
|
||||||
});
|
});
|
||||||
force.0 += impulse * SEPARATION_FACTOR;
|
force.0 += impulse * props.separation;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,6 +248,7 @@ fn alignment(
|
|||||||
spatial_tree: Res<KDTree2<TrackedByKdTree>>,
|
spatial_tree: Res<KDTree2<TrackedByKdTree>>,
|
||||||
mut boids: Query<(Entity, &Transform, &mut Force), With<Boid>>,
|
mut boids: Query<(Entity, &Transform, &mut Force), With<Boid>>,
|
||||||
boid_velocities: Query<&Velocity, With<Boid>>,
|
boid_velocities: Query<&Velocity, With<Boid>>,
|
||||||
|
props: Res<FlockingParameters>,
|
||||||
) {
|
) {
|
||||||
// for each boid
|
// for each boid
|
||||||
// find neighbors
|
// find neighbors
|
||||||
@@ -233,7 +258,7 @@ fn alignment(
|
|||||||
// apply steering force
|
// apply steering force
|
||||||
|
|
||||||
for (this_entt, transform, mut force) in &mut boids {
|
for (this_entt, transform, mut force) in &mut boids {
|
||||||
let neighbors = spatial_tree.within_distance(transform.translation.xy(), BOID_VIEW_RANGE);
|
let neighbors = spatial_tree.within_distance(transform.translation.xy(), props.view_range);
|
||||||
// averaging divides by length. Guard against an empty set of neighbors
|
// averaging divides by length. Guard against an empty set of neighbors
|
||||||
let (len, sum) = neighbors
|
let (len, sum) = neighbors
|
||||||
.iter()
|
.iter()
|
||||||
@@ -261,7 +286,7 @@ fn alignment(
|
|||||||
};
|
};
|
||||||
|
|
||||||
let boid_vel = boid_velocities.get(this_entt).unwrap();
|
let boid_vel = boid_velocities.get(this_entt).unwrap();
|
||||||
force.0 += (avg.extend(0.0) - boid_vel.0) * ALIGNMENT_FACTOR;
|
force.0 += (avg.extend(0.0) - boid_vel.0) * props.alignment;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -287,13 +312,13 @@ fn average_of_vec2s(points: impl Iterator<Item = Vec2>) -> Option<Vec2> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// f(x) = 4((x-0.5)^3 + 0.125)
|
// f(x) = 4((x-0.5)^3 + 0.125)
|
||||||
fn cohesive_force(boid: Vec2, target: Vec2) -> Option<Force> {
|
fn cohesive_force(boid: Vec2, target: Vec2, view_range: f32) -> Option<Force> {
|
||||||
let deviation = target - boid;
|
let deviation = target - boid;
|
||||||
/*
|
/*
|
||||||
Scale deviation vector by the boid's view range. The curve is made to
|
Scale deviation vector by the boid's view range. The curve is made to
|
||||||
operate on the range (0, 1), so that needs to be the viewing circle.
|
operate on the range (0, 1), so that needs to be the viewing circle.
|
||||||
*/
|
*/
|
||||||
let scaled = deviation / BOID_VIEW_RANGE;
|
let scaled = deviation / view_range;
|
||||||
let mag = scaled.length();
|
let mag = scaled.length();
|
||||||
if mag > 0.0 {
|
if mag > 0.0 {
|
||||||
let cube: f32 = (mag - 0.5).powf(3.0);
|
let cube: f32 = (mag - 0.5).powf(3.0);
|
||||||
@@ -310,9 +335,9 @@ fn cohesive_force(boid: Vec2, target: Vec2) -> Option<Force> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// f(x) = x^2 - 1
|
// f(x) = x^2 - 1
|
||||||
fn separation_force(boid: Vec2, target: Vec2) -> Option<Force> {
|
fn separation_force(boid: Vec2, target: Vec2, view_range: f32) -> Option<Force> {
|
||||||
// Scale from BOID_VIEW_RANGE to unit space
|
// Scale from BOID_VIEW_RANGE to unit space
|
||||||
let distance_unit = (target - boid) / BOID_VIEW_RANGE;
|
let distance_unit = (target - boid) / view_range;
|
||||||
let mag = distance_unit.length();
|
let mag = distance_unit.length();
|
||||||
if mag > 0.0 {
|
if mag > 0.0 {
|
||||||
let force_mag = mag.powf(2.0) - 1.0;
|
let force_mag = mag.powf(2.0) - 1.0;
|
||||||
@@ -329,14 +354,15 @@ mod tests {
|
|||||||
|
|
||||||
use crate::birdoids::{cohesive_force, separation_force};
|
use crate::birdoids::{cohesive_force, separation_force};
|
||||||
|
|
||||||
use super::{BOID_VIEW_RANGE, physics::Force};
|
use super::{FlockingParameters, physics::Force};
|
||||||
|
|
||||||
// forces are relative to the boid's view range, so all
|
// forces are relative to the boid's view range, so all
|
||||||
// distances need to be fractions of that
|
// distances need to be fractions of that
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn check_cohesion_zero_zero() {
|
fn check_cohesion_zero_zero() {
|
||||||
let force = cohesive_force(Vec2::ZERO, Vec2::ZERO);
|
let props = FlockingParameters::new();
|
||||||
|
let force = cohesive_force(Vec2::ZERO, Vec2::ZERO, props.view_range);
|
||||||
assert!(force.is_none());
|
assert!(force.is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -347,36 +373,56 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn check_cohesion_midpoint_x_positive() {
|
fn check_cohesion_midpoint_x_positive() {
|
||||||
// Pull right 0.5 units
|
// Pull right 0.5 units
|
||||||
|
let props = FlockingParameters::new();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Some(Force(Vec3::new(0.5, 0.0, 0.0))),
|
Some(Force(Vec3::new(0.5, 0.0, 0.0))),
|
||||||
cohesive_force(Vec2::new(0.0, 0.0), Vec2::new(0.5 * BOID_VIEW_RANGE, 0.0),)
|
cohesive_force(
|
||||||
|
Vec2::new(0.0, 0.0),
|
||||||
|
Vec2::new(0.5 * props.view_range, 0.0),
|
||||||
|
props.view_range
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn check_cohesion_midpoint_x_negative() {
|
fn check_cohesion_midpoint_x_negative() {
|
||||||
// Pull left 0.5 units
|
// Pull left 0.5 units
|
||||||
|
let props = FlockingParameters::new();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Some(Force(Vec3::new(-0.5, 0.0, 0.0))),
|
Some(Force(Vec3::new(-0.5, 0.0, 0.0))),
|
||||||
cohesive_force(Vec2::new(0.0, 0.0), Vec2::new(-0.5 * BOID_VIEW_RANGE, 0.0),)
|
cohesive_force(
|
||||||
|
Vec2::new(0.0, 0.0),
|
||||||
|
Vec2::new(-0.5 * props.view_range, 0.0),
|
||||||
|
props.view_range
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn check_cohesion_edge_x_positive() {
|
fn check_cohesion_edge_x_positive() {
|
||||||
// pull left 1.0 units
|
// pull left 1.0 units
|
||||||
|
let props = FlockingParameters::new();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Some(Force(Vec3::new(1.0, 0.0, 0.0))),
|
Some(Force(Vec3::new(1.0, 0.0, 0.0))),
|
||||||
cohesive_force(Vec2::new(0.0, 0.0), Vec2::new(1.0 * BOID_VIEW_RANGE, 0.0),)
|
cohesive_force(
|
||||||
|
Vec2::new(0.0, 0.0),
|
||||||
|
Vec2::new(1.0 * props.view_range, 0.0),
|
||||||
|
props.view_range
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn check_cohesion_edge_x_negative() {
|
fn check_cohesion_edge_x_negative() {
|
||||||
// pull left 1.0 units
|
// pull left 1.0 units
|
||||||
|
let props = FlockingParameters::new();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Some(Force(Vec3::new(-1.0, 0.0, 0.0))),
|
Some(Force(Vec3::new(-1.0, 0.0, 0.0))),
|
||||||
cohesive_force(Vec2::new(0.0, 0.0), Vec2::new(-1.0 * BOID_VIEW_RANGE, 0.0),)
|
cohesive_force(
|
||||||
|
Vec2::new(0.0, 0.0),
|
||||||
|
Vec2::new(-1.0 * props.view_range, 0.0),
|
||||||
|
props.view_range
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -387,43 +433,64 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn check_cohesion_midpoint_y_positive() {
|
fn check_cohesion_midpoint_y_positive() {
|
||||||
// Pull up 0.5 units
|
// Pull up 0.5 units
|
||||||
|
let props = FlockingParameters::new();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Some(Force(Vec3::new(0.0, 0.5, 0.0))),
|
Some(Force(Vec3::new(0.0, 0.5, 0.0))),
|
||||||
cohesive_force(Vec2::new(0.0, 0.0), Vec2::new(0.0, 0.5 * BOID_VIEW_RANGE),)
|
cohesive_force(
|
||||||
|
Vec2::new(0.0, 0.0),
|
||||||
|
Vec2::new(0.0, 0.5 * props.view_range),
|
||||||
|
props.view_range
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn check_cohesion_midpoint_y_negative() {
|
fn check_cohesion_midpoint_y_negative() {
|
||||||
// Pull down 0.5 units
|
// Pull down 0.5 units
|
||||||
|
let props = FlockingParameters::new();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Some(Force(Vec3::new(0.0, -0.5, 0.0))),
|
Some(Force(Vec3::new(0.0, -0.5, 0.0))),
|
||||||
cohesive_force(Vec2::new(0.0, 0.0), Vec2::new(0.0, -0.5 * BOID_VIEW_RANGE),)
|
cohesive_force(
|
||||||
|
Vec2::new(0.0, 0.0),
|
||||||
|
Vec2::new(0.0, -0.5 * props.view_range),
|
||||||
|
props.view_range
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn check_cohesion_edge_y_positive() {
|
fn check_cohesion_edge_y_positive() {
|
||||||
// Pull up 1.0 units
|
// Pull up 1.0 units
|
||||||
|
let props = FlockingParameters::new();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Some(Force(Vec3::new(0.0, 1.0, 0.0))),
|
Some(Force(Vec3::new(0.0, 1.0, 0.0))),
|
||||||
cohesive_force(Vec2::new(0.0, 0.0), Vec2::new(0.0, 1.0 * BOID_VIEW_RANGE))
|
cohesive_force(
|
||||||
|
Vec2::new(0.0, 0.0),
|
||||||
|
Vec2::new(0.0, 1.0 * props.view_range),
|
||||||
|
props.view_range
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn check_cohesion_edge_y_negative() {
|
fn check_cohesion_edge_y_negative() {
|
||||||
// pull down 0.2 units
|
// pull down 0.2 units
|
||||||
|
let props = FlockingParameters::new();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Some(Force(Vec3::new(0.0, -1.0, 0.0))),
|
Some(Force(Vec3::new(0.0, -1.0, 0.0))),
|
||||||
cohesive_force(Vec2::new(0.0, 0.0), Vec2::new(0.0, -1.0 * BOID_VIEW_RANGE),)
|
cohesive_force(
|
||||||
|
Vec2::new(0.0, 0.0),
|
||||||
|
Vec2::new(0.0, -1.0 * props.view_range),
|
||||||
|
props.view_range
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Separation 0,0 test
|
// Separation 0,0 test
|
||||||
#[test]
|
#[test]
|
||||||
fn check_separation_zero_zero() {
|
fn check_separation_zero_zero() {
|
||||||
let force = separation_force(Vec2::ZERO, Vec2::ZERO);
|
let props = FlockingParameters::new();
|
||||||
|
let force = separation_force(Vec2::ZERO, Vec2::ZERO, props.view_range);
|
||||||
assert!(force.is_none());
|
assert!(force.is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -432,39 +499,53 @@ mod tests {
|
|||||||
// *********************
|
// *********************
|
||||||
#[test]
|
#[test]
|
||||||
fn check_separation_midpoint_x_positive() {
|
fn check_separation_midpoint_x_positive() {
|
||||||
|
let props = FlockingParameters::new();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Some(Force(Vec3::new(0.75, 0.0, 0.0))), // expected force
|
Some(Force(Vec3::new(0.75, 0.0, 0.0))), // expected force
|
||||||
separation_force(
|
separation_force(
|
||||||
Vec2::new(0.5 * BOID_VIEW_RANGE, 0.0), // boid position
|
Vec2::new(0.5 * props.view_range, 0.0), // boid position
|
||||||
Vec2::ZERO, // obstacle position
|
Vec2::ZERO, // obstacle position
|
||||||
|
props.view_range
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn check_separation_midpoint_x_negative() {
|
fn check_separation_midpoint_x_negative() {
|
||||||
|
let props = FlockingParameters::new();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Some(Force(Vec3::new(-0.75, 0.0, 0.0))), // expected force
|
Some(Force(Vec3::new(-0.75, 0.0, 0.0))), // expected force
|
||||||
separation_force(
|
separation_force(
|
||||||
Vec2::new(-0.5 * BOID_VIEW_RANGE, 0.0), // boid position
|
Vec2::new(-0.5 * props.view_range, 0.0), // boid position
|
||||||
Vec2::ZERO, // obstacle position
|
Vec2::ZERO, // obstacle position
|
||||||
|
props.view_range
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn check_separation_edge_x_positive() {
|
fn check_separation_edge_x_positive() {
|
||||||
|
let props = FlockingParameters::new();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Some(Force(Vec3::ZERO)),
|
Some(Force(Vec3::ZERO)),
|
||||||
separation_force(Vec2::new(1.0 * BOID_VIEW_RANGE, 0.0), Vec2::ZERO,),
|
separation_force(
|
||||||
|
Vec2::new(1.0 * props.view_range, 0.0),
|
||||||
|
Vec2::ZERO,
|
||||||
|
props.view_range
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn check_separation_edge_x_negative() {
|
fn check_separation_edge_x_negative() {
|
||||||
|
let props = FlockingParameters::new();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Some(Force(Vec3::ZERO)),
|
Some(Force(Vec3::ZERO)),
|
||||||
separation_force(Vec2::new(-1.0 * BOID_VIEW_RANGE, 0.0), Vec2::ZERO,),
|
separation_force(
|
||||||
|
Vec2::new(-1.0 * props.view_range, 0.0),
|
||||||
|
Vec2::ZERO,
|
||||||
|
props.view_range
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -473,33 +554,53 @@ mod tests {
|
|||||||
// *********************
|
// *********************
|
||||||
#[test]
|
#[test]
|
||||||
fn check_separation_midpoint_y_positive() {
|
fn check_separation_midpoint_y_positive() {
|
||||||
|
let props = FlockingParameters::new();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Some(Force(Vec3::new(0.0, 0.75, 0.0))),
|
Some(Force(Vec3::new(0.0, 0.75, 0.0))),
|
||||||
separation_force(Vec2::new(0.0, 0.5 * BOID_VIEW_RANGE), Vec2::ZERO,)
|
separation_force(
|
||||||
|
Vec2::new(0.0, 0.5 * props.view_range),
|
||||||
|
Vec2::ZERO,
|
||||||
|
props.view_range
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn check_separation_midpoint_y_negative() {
|
fn check_separation_midpoint_y_negative() {
|
||||||
|
let props = FlockingParameters::new();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Some(Force(Vec3::new(0.0, -0.75, 0.0))),
|
Some(Force(Vec3::new(0.0, -0.75, 0.0))),
|
||||||
separation_force(Vec2::new(0.0, -0.5 * BOID_VIEW_RANGE), Vec2::ZERO,)
|
separation_force(
|
||||||
|
Vec2::new(0.0, -0.5 * props.view_range),
|
||||||
|
Vec2::ZERO,
|
||||||
|
props.view_range
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn check_separation_edge_y_positive() {
|
fn check_separation_edge_y_positive() {
|
||||||
|
let props = FlockingParameters::new();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Some(Force(Vec3::ZERO)),
|
Some(Force(Vec3::ZERO)),
|
||||||
separation_force(Vec2::new(0.0, 1.0 * BOID_VIEW_RANGE), Vec2::ZERO,)
|
separation_force(
|
||||||
|
Vec2::new(0.0, 1.0 * props.view_range),
|
||||||
|
Vec2::ZERO,
|
||||||
|
props.view_range
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn check_separation_edge_y_negative() {
|
fn check_separation_edge_y_negative() {
|
||||||
|
let props = FlockingParameters::new();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Some(Force(Vec3::ZERO)),
|
Some(Force(Vec3::ZERO)),
|
||||||
separation_force(Vec2::new(0.0, -1.0 * BOID_VIEW_RANGE), Vec2::ZERO,)
|
separation_force(
|
||||||
|
Vec2::new(0.0, -1.0 * props.view_range),
|
||||||
|
Vec2::ZERO,
|
||||||
|
props.view_range
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,9 +3,15 @@ use bevy::prelude::*;
|
|||||||
mod birdoids;
|
mod birdoids;
|
||||||
mod debug_plugin;
|
mod debug_plugin;
|
||||||
|
|
||||||
|
use bevy_inspector_egui::{
|
||||||
|
bevy_egui::EguiPlugin,
|
||||||
|
quick::{ResourceInspectorPlugin},
|
||||||
|
};
|
||||||
use birdoids::BoidsPlugin;
|
use birdoids::BoidsPlugin;
|
||||||
use debug_plugin::BoidsDebugPlugin;
|
use debug_plugin::BoidsDebugPlugin;
|
||||||
|
|
||||||
|
use crate::birdoids::FlockingParameters;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
App::new()
|
App::new()
|
||||||
.add_plugins(DefaultPlugins.set(WindowPlugin {
|
.add_plugins(DefaultPlugins.set(WindowPlugin {
|
||||||
@@ -18,5 +24,7 @@ fn main() {
|
|||||||
}))
|
}))
|
||||||
.add_plugins(BoidsDebugPlugin)
|
.add_plugins(BoidsDebugPlugin)
|
||||||
.add_plugins(BoidsPlugin)
|
.add_plugins(BoidsPlugin)
|
||||||
|
.add_plugins(EguiPlugin::default())
|
||||||
|
.add_plugins(ResourceInspectorPlugin::<FlockingParameters>::new()) // TODO: monitor only the flocking params resource (once it exists)
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user