Skip to content
This repository was archived by the owner on Oct 25, 2021. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions resources/prefabs/creatures/carnivore.ron
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,14 @@ Prefab (
faction: "Carnivores",
),
),
intelligence_tag: (),
perception: (
range: 5.0,
range: 6.0,
),
boids: (
seek: (
names: ["Herbivore"],
strength: 10.0,
),
),
),
),
Expand Down
23 changes: 21 additions & 2 deletions resources/prefabs/creatures/herbivore.ron
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,28 @@ Prefab (
faction: "Herbivores",
),
),
intelligence_tag: (),
perception: (
range: 5.0,
range: 4.0,
),
boids: (
flocking: (
strength: 0.4,
),
match_velocity: (
strength: 3.0,
),
avoid: (
names: ["Carnivore"],
strength: 20.0,
),
seek: (
names: ["Plant"],
strength: 10.0,
),
minimum_distance: (
minimum: 1.5,
strength: 5.0,
),
),
),
),
Expand Down
5 changes: 3 additions & 2 deletions src/components/creatures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ use amethyst_inspector::Inspect;
use serde::{Deserialize, Serialize};

use crate::components::{
collider::Circle, combat::CombatPrefabData, digestion::DigestionPrefabData,
perception::Perception,
boids::BoidsPrefabData, collider::Circle, combat::CombatPrefabData,
digestion::DigestionPrefabData, perception::Perception,
};

pub type CreatureType = String;
Expand Down Expand Up @@ -84,4 +84,5 @@ pub struct CreaturePrefabData {
combat: Option<CombatPrefabData>,
intelligence_tag: Option<IntelligenceTag>,
perception: Option<Perception>,
boids: Option<BoidsPrefabData>,
}
96 changes: 96 additions & 0 deletions src/components/experimental/boids.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
use amethyst::{
assets::{PrefabData, PrefabError, ProgressCounter},
derive::PrefabData,
ecs::{Component, DenseVecStorage, Entity, WriteStorage},
};
use amethyst_inspector::Inspect;

use serde::{Deserialize, Serialize};

#[derive(Default, Clone, Inspect, Serialize, Deserialize, PrefabData)]
#[prefab(Component)]
#[serde(default)]
pub struct FlockingRule {
pub strength: f32,
}

impl Component for FlockingRule {
type Storage = DenseVecStorage<Self>;
}

#[derive(Default, Clone, Inspect, Serialize, Deserialize, PrefabData)]
#[prefab(Component)]
#[serde(default)]
pub struct MatchVelocityRule {
pub strength: f32,
}

impl Component for MatchVelocityRule {
type Storage = DenseVecStorage<Self>;
}

#[derive(Default, Clone, Inspect, Serialize, Deserialize, PrefabData)]
#[prefab(Component)]
#[serde(default)]
pub struct MinimumDistanceRule {
pub minimum: f32,
pub strength: f32,
}

impl Component for MinimumDistanceRule {
type Storage = DenseVecStorage<Self>;
}

#[derive(Default, Clone, Serialize, Deserialize, PrefabData)]
#[prefab(Component)]
#[serde(default)]
pub struct AvoidRule {
pub names: Vec<String>,
pub strength: f32,
}

impl Component for AvoidRule {
type Storage = DenseVecStorage<Self>;
}

#[derive(Default, Clone, Serialize, Deserialize, PrefabData)]
#[prefab(Component)]
#[serde(default)]
pub struct SeekRule {
pub names: Vec<String>,
pub strength: f32,
}

impl Component for SeekRule {
type Storage = DenseVecStorage<Self>;
}

#[derive(Clone, Serialize, Deserialize, PrefabData)]
#[prefab(Component)]
pub struct WorldBoundsRule {
pub strength: f32,
}

impl Default for WorldBoundsRule {
fn default() -> Self {
WorldBoundsRule { strength: 20.0 }
}
}

impl Component for WorldBoundsRule {
type Storage = DenseVecStorage<Self>;
}

#[derive(Default, Deserialize, Serialize, PrefabData)]
#[serde(default)]
#[serde(deny_unknown_fields)]
pub struct BoidsPrefabData {
flocking: Option<FlockingRule>,
match_velocity: Option<MatchVelocityRule>,
avoid: Option<AvoidRule>,
seek: Option<SeekRule>,
minimum_distance: Option<MinimumDistanceRule>,

#[serde(skip)]
wall_bounds: WorldBoundsRule,
}
1 change: 1 addition & 0 deletions src/components/experimental/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
pub mod boids;
pub mod perception;
4 changes: 2 additions & 2 deletions src/components/experimental/perception.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use amethyst::{
assets::{PrefabData, PrefabError},
ecs::{Component, DenseVecStorage, Entity, WriteStorage},
ecs::{BitSet, Component, DenseVecStorage, Entity, WriteStorage},
};
use amethyst_inspector::Inspect;
use serde::{Deserialize, Serialize};
Expand All @@ -18,7 +18,7 @@ impl Component for Perception {

#[derive(Default, Clone, Debug)]
pub struct DetectedEntities {
pub entities: Vec<Entity>,
pub entities: BitSet,
}

impl Component for DetectedEntities {
Expand Down
22 changes: 14 additions & 8 deletions src/resources/experimental/spatial_grid.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use amethyst::{
core::{nalgebra::Vector4, transform::GlobalTransform},
ecs::Entity,
ecs::{BitSet, Entity},
};

use std::collections::HashMap;
Expand All @@ -9,7 +9,7 @@ use std::f32;
// The SpatialGrid is a spatial hashing structure used to accelerate neighbor searches for entities.
pub struct SpatialGrid {
cell_size: f32,
cells: HashMap<i32, HashMap<i32, Vec<Entity>>>,
cells: HashMap<i32, HashMap<i32, BitSet>>,
}

impl SpatialGrid {
Expand All @@ -31,23 +31,23 @@ impl SpatialGrid {
let x_cell = (pos[0] / self.cell_size).floor() as i32;
let y_cell = (pos[1] / self.cell_size).floor() as i32;
let row_entry = self.cells.entry(x_cell).or_insert(HashMap::new());
let col_entry = row_entry.entry(y_cell).or_insert(Vec::new());
col_entry.push(entity);
let col_entry = row_entry.entry(y_cell).or_insert(BitSet::new());
col_entry.add(entity.id());
}

// Query the entities close to a certain position.
// The range of the query is defined by the range input.
pub fn query(&self, transform: &GlobalTransform, range: f32) -> Vec<Entity> {
pub fn query(&self, transform: &GlobalTransform, range: f32) -> BitSet {
let pos = Vector4::from(transform.as_ref()[3]);
let x_cell = (pos[0] / self.cell_size).floor() as i32;
let y_cell = (pos[1] / self.cell_size).floor() as i32;
let integer_range = 1 + (range / self.cell_size).ceil() as i32;
let mut entities = Vec::new();
let mut entities = BitSet::new();
for x in -integer_range..integer_range {
for y in -integer_range..integer_range {
match self.cells.get(&(x_cell + x)) {
Some(col) => match col.get(&(y_cell + y)) {
Some(cell) => entities.extend_from_slice(cell.as_slice()),
Some(cell) => entities |= cell,
None => (),
},
None => (),
Expand Down Expand Up @@ -76,7 +76,13 @@ mod tests {
let global_transform = GlobalTransform::from(*transform_matrix.as_ref());
spatial_grid.insert(world.create_entity().build(), &global_transform);

assert!(spatial_grid.query(&global_transform, 1.0f32).len() == 1);
assert!(
spatial_grid
.query(&global_transform, 1.0f32)
.into_iter()
.count()
== 1
);
}

}
130 changes: 70 additions & 60 deletions src/states/main_game.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,76 +43,86 @@ impl MainGameState {
"entity_detection",
&["spatial_grid"],
)
.with(boids::FlockingSystem, "flocking", &["entity_detection"])
.with(
QueryPredatorsAndPreySystem,
"query_predators_and_prey_system",
&[],
)
.with(ClosestObstacleSystem, "closest_obstacle_system", &[])
.with(
ClosestSystem::<Prey>::default(),
"closest_prey_system",
&["query_predators_and_prey_system"],
)
.with(
ClosestSystem::<Predator>::default(),
"closest_predator_system",
&["query_predators_and_prey_system"],
)
.with(
SeekSystem::<Prey>::new(
Rotation3::from_axis_angle(&Vector3::z_axis(), 0.0),
1.0,
),
"seek_prey_system",
&["closest_prey_system"],
boids::MatchVelocitySystem,
"match_velocity",
&["entity_detection"],
)
.with(boids::AvoidSystem, "avoid", &["entity_detection"])
.with(boids::SeekSystem, "seek", &["entity_detection"])
.with(
SeekSystem::<Predator>::new(
// 180 degrees, run away!
Rotation3::from_axis_angle(&Vector3::z_axis(), std::f32::consts::PI),
1.0,
),
"avoid_predator_system",
&["closest_predator_system"],
boids::MinimumDistanceSystem,
"minimum_distance",
&["entity_detection"],
)
.with(
SeekSystem::<Obstacle>::new(
// 120 degrees. A little more than perpendicular so the creature
// tries to steer away from the wall rather than just follow it.
Rotation3::from_axis_angle(
&Vector3::z_axis(),
2f32 * std::f32::consts::FRAC_PI_3,
),
5.0,
),
"avoid_obstacle_system",
&["closest_obstacle_system"],
)
.with(
behaviors::wander::WanderSystem,
"wander_system",
&[
"seek_prey_system",
"avoid_predator_system",
"avoid_obstacle_system",
],
)
.with(
movement::MovementSystem,
"movement_system",
&["wander_system"],
boids::WorldBoundsSystem,
"world_bounds",
&["entity_detection"],
)
//.with(
//QueryPredatorsAndPreySystem,
//"query_predators_and_prey_system",
//&[],
//)
//.with(ClosestObstacleSystem, "closest_obstacle_system", &[])
//.with(
//ClosestSystem::<Prey>::default(),
//"closest_prey_system",
//&["query_predators_and_prey_system"],
//)
//.with(
//ClosestSystem::<Predator>::default(),
//"closest_predator_system",
//&["query_predators_and_prey_system"],
//)
//.with(
//SeekSystem::<Prey>::new(
//Rotation3::from_axis_angle(&Vector3::z_axis(), 0.0),
//1.0,
//),
//"seek_prey_system",
//&["closest_prey_system"],
//)
//.with(
//SeekSystem::<Predator>::new(
//// 180 degrees, run away!
//Rotation3::from_axis_angle(&Vector3::z_axis(), std::f32::consts::PI),
//1.0,
//),
//"avoid_predator_system",
//&["closest_predator_system"],
//)
//.with(
//SeekSystem::<Obstacle>::new(
//// 120 degrees. A little more than perpendicular so the creature
//// tries to steer away from the wall rather than just follow it.
//Rotation3::from_axis_angle(
//&Vector3::z_axis(),
//2f32 * std::f32::consts::FRAC_PI_3,
//),
//5.0,
//),
//"avoid_obstacle_system",
//&["closest_obstacle_system"],
//)
//.with(
//behaviors::wander::WanderSystem,
//"wander_system",
//&["seek_prey_system", "avoid_predator_system"],
//)
.with(movement::MovementSystem, "movement_system", &[])
.with(
collision::CollisionSystem,
"collision_system",
&["movement_system"],
)
.with(
collision::EnforceBoundsSystem,
"enforce_bounds_system",
&["movement_system"],
)
//.with(
//collision::EnforceBoundsSystem,
//"enforce_bounds_system",
//&["movement_system"],
//)
.with(digestion::DigestionSystem, "digestion_system", &[])
.with(
digestion::StarvationSystem,
Expand Down Expand Up @@ -275,7 +285,7 @@ impl<'a> State<GameData<'a, 'a>, CustomStateEvent> for MainGameState {
};

let mut transform = Transform::default();
transform.set_position([0.0, 0.0, 12.0].into());
transform.set_position([0.0, 0.0, 20.0].into());

data.world
.create_entity()
Expand Down
Loading