Add unit tests for force functions
Finally time to just use the unit testing tools Cargo gives me. These
are all a bunch of simple tests to verify that the functions work as
expected.
... They don't. I made the assertion that the input ranges are (0, 1)
when, in fact, they are not. The boid's view distance is scaled into
a unit vector space. The trouble with this is that a valid coordinate
is (-1, 0). The force functions don't have the correct shape at this
range.
The fix is to get the absolute value -- the vector's magnitude -- and
feed that through the force calculation function. This way it *is* in
the range (0, 1). The magnitude can then be multiplied back over the
deviation vector to get a proper force application.
This unit vector can then optionally be multiplied back up to have more
exaggerated effects. The {COHESION,SEPARATION,ALIGNMENT}_FACTOR's can
pick up that role again.
This commit is contained in:
@@ -10,7 +10,7 @@ const TURN_FACTOR: f32 = 1.0;
|
||||
const BOID_VIEW_RANGE: f32 = 50.0;
|
||||
const COHESION_FACTOR: f32 = 1.0;
|
||||
const SEPARATION_FACTOR: f32 = 1.0;
|
||||
const ALIGNMENT_FACTOR: f32 = 500.0;
|
||||
const ALIGNMENT_FACTOR: f32 = 1.0;
|
||||
const SPACEBRAKES_COEFFICIENT: f32 = 0.01;
|
||||
|
||||
pub struct BoidsPlugin;
|
||||
@@ -51,7 +51,7 @@ struct PlayerBoid;
|
||||
#[derive(Component, Deref, DerefMut)]
|
||||
pub(crate) struct Velocity(Vec3);
|
||||
|
||||
#[derive(Component, Deref, DerefMut)]
|
||||
#[derive(Component, Deref, DerefMut, PartialEq, Debug)]
|
||||
pub(crate) struct Force(Vec3);
|
||||
|
||||
#[derive(Component)]
|
||||
@@ -311,3 +311,224 @@ fn separation_force(us: Vec2, neighbor: Vec2) -> Force {
|
||||
let force_vec = -scaled.powf(2.0) + Vec2::ONE;
|
||||
Force(force_vec.extend(0.0))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests{
|
||||
use bevy::prelude::*;
|
||||
|
||||
use crate::birdoids_plugin::{
|
||||
cohesive_force, separation_force
|
||||
};
|
||||
|
||||
use super::{
|
||||
BOID_VIEW_RANGE,
|
||||
Force,
|
||||
};
|
||||
|
||||
// forces are relative to the boid's view range, so all
|
||||
// distances need to be fractions of that
|
||||
|
||||
#[test]
|
||||
fn check_cohesion_zero_zero() {
|
||||
todo!("Make test for cohesion_force when boid and target are at the same point")
|
||||
}
|
||||
|
||||
// *********************
|
||||
// Cohesion x-axis tests
|
||||
// *********************
|
||||
|
||||
#[test]
|
||||
fn check_cohesion_midpoint_x_positive(){
|
||||
// Pull right 0.5 units
|
||||
assert_eq!(
|
||||
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),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_cohesion_midpoint_x_negative(){
|
||||
// Pull left 0.5 units
|
||||
assert_eq!(
|
||||
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),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_cohesion_edge_x_positive() {
|
||||
// pull left 1.0 units
|
||||
assert_eq!(
|
||||
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),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_cohesion_edge_x_negative() {
|
||||
// pull left 1.0 units
|
||||
assert_eq!(
|
||||
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),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// *********************
|
||||
// Cohesion y-axis tests
|
||||
// *********************
|
||||
|
||||
#[test]
|
||||
fn check_cohesion_midpoint_y_positive(){
|
||||
// Pull up 0.5 units
|
||||
assert_eq!(
|
||||
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),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_cohesion_midpoint_y_negative(){
|
||||
// Pull down 0.5 units
|
||||
assert_eq!(
|
||||
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),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_cohesion_edge_y_positive() {
|
||||
// Pull up 1.0 units
|
||||
assert_eq!(
|
||||
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)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_cohesion_edge_y_negative() {
|
||||
// pull down 0.2 units
|
||||
assert_eq!(
|
||||
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),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// *********************
|
||||
// Separation x-axis tests
|
||||
// *********************
|
||||
#[test]
|
||||
fn check_separation_midpoint_x_positive(){
|
||||
assert_eq!(
|
||||
Force(Vec3::new(0.75, 0.0, 0.0)), // expected force
|
||||
separation_force(
|
||||
Vec2::new(0.5, 0.0), // boid position
|
||||
Vec2::ZERO, // obstacle position
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_separation_midpoint_x_negative(){
|
||||
assert_eq!(
|
||||
Force(Vec3::new(-0.75, 0.0, 0.0)), // expected force
|
||||
separation_force(
|
||||
Vec2::new(-0.5, 0.0), // boid position
|
||||
Vec2::ZERO, // obstacle position
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_separation_edge_x_positive() {
|
||||
assert_eq!(
|
||||
Force(Vec3::ZERO),
|
||||
separation_force(
|
||||
Vec2::new(1.0, 0.0),
|
||||
Vec2::ZERO,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_separation_edge_x_negative() {
|
||||
assert_eq!(
|
||||
Force(Vec3::ZERO),
|
||||
separation_force(
|
||||
Vec2::new(-1.0, 0.0),
|
||||
Vec2::ZERO,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// *********************
|
||||
// Separation y-axis tests
|
||||
// *********************
|
||||
#[test]
|
||||
fn check_separation_midpoint_y_positive(){
|
||||
assert_eq!(
|
||||
Force(Vec3::new(0.0, 0.75, 0.0)),
|
||||
separation_force(
|
||||
Vec2::new(0.0, 0.5),
|
||||
Vec2::ZERO,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_separation_midpoint_y_negative(){
|
||||
assert_eq!(
|
||||
Force(Vec3::new(0.0, -0.75, 0.0)),
|
||||
separation_force(
|
||||
Vec2::new(0.0, -0.5),
|
||||
Vec2::ZERO,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_separation_edge_y_positive() {
|
||||
assert_eq!(
|
||||
Force(Vec3::ZERO),
|
||||
separation_force(
|
||||
Vec2::new(0.0, 1.0),
|
||||
Vec2::ZERO,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_separation_edge_y_negative() {
|
||||
assert_eq!(
|
||||
Force(Vec3::ZERO),
|
||||
separation_force(
|
||||
Vec2::new(0.0, -1.0),
|
||||
Vec2::ZERO,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user