From c9619ba51a2a3b3a3ae379848362ad768ea60f6a Mon Sep 17 00:00:00 2001 From: Victor Cornillere Date: Wed, 22 May 2019 10:59:27 +0200 Subject: [PATCH 1/9] Change spatial grid to use BitSet instead of Vec --- src/components/experimental/perception.rs | 4 ++-- src/resources/experimental/spatial_grid.rs | 14 +++++++------- src/systems/experimental/flocking.rs | 20 ++++++++++++++++++++ src/systems/experimental/mod.rs | 1 + src/systems/experimental/perception.rs | 17 +++++------------ 5 files changed, 35 insertions(+), 21 deletions(-) create mode 100644 src/systems/experimental/flocking.rs diff --git a/src/components/experimental/perception.rs b/src/components/experimental/perception.rs index f8c92bb..9d30a51 100644 --- a/src/components/experimental/perception.rs +++ b/src/components/experimental/perception.rs @@ -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}; @@ -18,7 +18,7 @@ impl Component for Perception { #[derive(Default, Clone, Debug)] pub struct DetectedEntities { - pub entities: Vec, + pub entities: BitSet, } impl Component for DetectedEntities { diff --git a/src/resources/experimental/spatial_grid.rs b/src/resources/experimental/spatial_grid.rs index 0306b9a..9c8c8ab 100644 --- a/src/resources/experimental/spatial_grid.rs +++ b/src/resources/experimental/spatial_grid.rs @@ -1,6 +1,6 @@ use amethyst::{ core::{nalgebra::Vector4, transform::GlobalTransform}, - ecs::Entity, + ecs::{BitSet, Entity}, }; use std::collections::HashMap; @@ -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>>, + cells: HashMap>, } impl SpatialGrid { @@ -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 { + 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 => (), diff --git a/src/systems/experimental/flocking.rs b/src/systems/experimental/flocking.rs new file mode 100644 index 0000000..482bb26 --- /dev/null +++ b/src/systems/experimental/flocking.rs @@ -0,0 +1,20 @@ +use amethyst::{ + core::{nalgebra::Vector4, transform::GlobalTransform}, + ecs::{ + BitSet, Entities, Join, ReadExpect, ReadStorage, System, Write, WriteExpect, WriteStorage, + }, +}; + +use crate::components::{creatures::Movement, perception::DetectedEntities}; + +pub struct FlockingSystem; + +impl<'s> System<'s> for FlockingSystem { + type SystemData = ( + ReadStorage<'s, DetectedEntities>, + ReadStorage<'s, GlobalTransform>, + WriteStorage<'s, Movement>, + ); + + fn run(&mut self, (detected_entities, globals, mut movements): Self::SystemData) {} +} diff --git a/src/systems/experimental/mod.rs b/src/systems/experimental/mod.rs index d9db8c2..5063a3d 100644 --- a/src/systems/experimental/mod.rs +++ b/src/systems/experimental/mod.rs @@ -1 +1,2 @@ +pub mod flocking; pub mod perception; diff --git a/src/systems/experimental/perception.rs b/src/systems/experimental/perception.rs index b21af37..2ff20e3 100644 --- a/src/systems/experimental/perception.rs +++ b/src/systems/experimental/perception.rs @@ -38,23 +38,17 @@ impl<'s> System<'s> for EntityDetectionSystem { for (entity, perception, mut detected, global) in (&entities, &perceptions, &mut detected_entities, &globals).join() { - detected.entities = Vec::new(); + detected.entities = BitSet::new(); let nearby_entities = grid.query(global, perception.range); let pos = Vector4::from(global.as_ref()[3]).xyz(); let sq_range = perception.range * perception.range; - let mut nearby_entities_bitset = BitSet::new(); - for other_entity in &nearby_entities { - if entity == *other_entity { + for (other_entity, other_global, _) in (&entities, &globals, &nearby_entities).join() { + if entity == other_entity { continue; } - nearby_entities_bitset.add(other_entity.id()); - } - for (other_entity, other_global, _) in - (&entities, &globals, &nearby_entities_bitset).join() - { let other_pos = Vector4::from(other_global.as_ref()[3]).xyz(); if (pos - other_pos).norm_squared() < sq_range { - detected.entities.push(other_entity); + detected.entities.add(other_entity.id()); } } } @@ -90,8 +84,7 @@ impl<'s> System<'s> for DebugEntityDetectionSystem { fn run(&mut self, (detected_entities, globals, mut debug_lines): Self::SystemData) { for (detected, global) in (&detected_entities, &globals).join() { let pos = Vector4::from(global.as_ref()[3]).xyz(); - for other_entity in &detected.entities { - let other_global = globals.get(*other_entity).unwrap(); + for (other_global, other_entity) in (&globals, &detected.entities).join() { let other_pos = Vector4::from(other_global.as_ref()[3]).xyz(); debug_lines.draw_line( [pos[0], pos[1], 0.0].into(), From 9accbf85a8e6e1156c4c152d1d6504fce3481f78 Mon Sep 17 00:00:00 2001 From: Victor Cornillere Date: Wed, 22 May 2019 15:17:26 +0200 Subject: [PATCH 2/9] Add first version of Boids flocking rule --- resources/prefabs/creatures/herbivore.ron | 9 +++ src/components/creatures.rs | 5 +- src/components/experimental/boids.rs | 39 ++++++++++ src/components/experimental/mod.rs | 1 + src/states/main_game.rs | 26 +++---- src/systems/experimental/flocking.rs | 90 ++++++++++++++++++++++- src/systems/spawner.rs | 6 +- 7 files changed, 153 insertions(+), 23 deletions(-) create mode 100644 src/components/experimental/boids.rs diff --git a/resources/prefabs/creatures/herbivore.ron b/resources/prefabs/creatures/herbivore.ron index 84cc7ac..a63fee8 100644 --- a/resources/prefabs/creatures/herbivore.ron +++ b/resources/prefabs/creatures/herbivore.ron @@ -54,6 +54,15 @@ Prefab ( perception: ( range: 5.0, ), + boids: ( + flocking: ( + strength: 1.0, + ), + minimum_distance: ( + minimum: 1.5, + strength: 10.0, + ), + ), ), ), ], diff --git a/src/components/creatures.rs b/src/components/creatures.rs index ef734a6..d102fff 100644 --- a/src/components/creatures.rs +++ b/src/components/creatures.rs @@ -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; @@ -84,4 +84,5 @@ pub struct CreaturePrefabData { combat: Option, intelligence_tag: Option, perception: Option, + boids: Option, } diff --git a/src/components/experimental/boids.rs b/src/components/experimental/boids.rs new file mode 100644 index 0000000..d92ee20 --- /dev/null +++ b/src/components/experimental/boids.rs @@ -0,0 +1,39 @@ +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; +} + +#[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; +} + +#[derive(Default, Deserialize, Serialize, PrefabData)] +#[serde(default)] +#[serde(deny_unknown_fields)] +pub struct BoidsPrefabData { + flocking: Option, + minimum_distance: Option, +} diff --git a/src/components/experimental/mod.rs b/src/components/experimental/mod.rs index d9db8c2..8e7677c 100644 --- a/src/components/experimental/mod.rs +++ b/src/components/experimental/mod.rs @@ -1 +1,2 @@ +pub mod boids; pub mod perception; diff --git a/src/states/main_game.rs b/src/states/main_game.rs index fdc6c9e..d1649ff 100644 --- a/src/states/main_game.rs +++ b/src/states/main_game.rs @@ -43,6 +43,12 @@ impl MainGameState { "entity_detection", &["spatial_grid"], ) + .with(flocking::FlockingSystem, "flocking", &["entity_detection"]) + .with( + flocking::MinimumDistanceSystem, + "minimum_distance", + &["entity_detection"], + ) .with( QueryPredatorsAndPreySystem, "query_predators_and_prey_system", @@ -89,20 +95,12 @@ impl MainGameState { "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"], - ) + //.with( + //behaviors::wander::WanderSystem, + //"wander_system", + //&["seek_prey_system", "avoid_predator_system"], + //) + .with(movement::MovementSystem, "movement_system", &[]) .with( collision::CollisionSystem, "collision_system", diff --git a/src/systems/experimental/flocking.rs b/src/systems/experimental/flocking.rs index 482bb26..dac9433 100644 --- a/src/systems/experimental/flocking.rs +++ b/src/systems/experimental/flocking.rs @@ -1,20 +1,102 @@ use amethyst::{ - core::{nalgebra::Vector4, transform::GlobalTransform}, + core::{ + nalgebra::{Vector3, Vector4}, + transform::GlobalTransform, + Named, Time, + }, ecs::{ - BitSet, Entities, Join, ReadExpect, ReadStorage, System, Write, WriteExpect, WriteStorage, + BitSet, Entities, Join, Read, ReadExpect, ReadStorage, System, Write, WriteExpect, + WriteStorage, }, }; -use crate::components::{creatures::Movement, perception::DetectedEntities}; +use crate::components::{ + boids::{FlockingRule, MinimumDistanceRule}, + creatures::Movement, + perception::DetectedEntities, +}; pub struct FlockingSystem; impl<'s> System<'s> for FlockingSystem { type SystemData = ( + ReadStorage<'s, Named>, + ReadStorage<'s, FlockingRule>, + ReadStorage<'s, DetectedEntities>, + ReadStorage<'s, GlobalTransform>, + WriteStorage<'s, Movement>, + Read<'s, Time>, + ); + + fn run( + &mut self, + (names, flocking_rules, detected_entities, globals, mut movements, time): Self::SystemData, + ) { + let delta_time = time.delta_seconds(); + for (name, rule, detected, global, mut movement) in ( + &names, + &flocking_rules, + &detected_entities, + &globals, + &mut movements, + ) + .join() + { + let pos = Vector4::from(global.as_ref()[3]).xyz(); + let mut average_position = pos; + let mut count = 1; + for (other_name, other_global, _) in (&names, &globals, &detected.entities).join() { + if other_name.name == name.name { + let other_pos = Vector4::from(other_global.as_ref()[3]).xyz(); + average_position += other_pos; + count += 1; + } + } + average_position /= count as f32; + let diff_vector = average_position - pos; + movement.velocity += delta_time * rule.strength * diff_vector; + } + } +} + +pub struct MinimumDistanceSystem; + +impl<'s> System<'s> for MinimumDistanceSystem { + type SystemData = ( + ReadStorage<'s, Named>, + ReadStorage<'s, MinimumDistanceRule>, ReadStorage<'s, DetectedEntities>, ReadStorage<'s, GlobalTransform>, WriteStorage<'s, Movement>, + Read<'s, Time>, ); - fn run(&mut self, (detected_entities, globals, mut movements): Self::SystemData) {} + fn run( + &mut self, + (names, min_distances, detected_entities, globals, mut movements, time): Self::SystemData, + ) { + let delta_time = time.delta_seconds(); + for (name, min_distance, detected, global, mut movement) in ( + &names, + &min_distances, + &detected_entities, + &globals, + &mut movements, + ) + .join() + { + let sq_min_dist = min_distance.minimum * min_distance.minimum; + let pos = Vector4::from(global.as_ref()[3]).xyz(); + let mut total_diff = Vector3::new(0.0, 0.0, 0.0); + for (other_name, other_global, _) in (&names, &globals, &detected.entities).join() { + let other_pos = Vector4::from(other_global.as_ref()[3]).xyz(); + let diff = pos - other_pos; + let dist = diff.norm_squared(); + if dist < sq_min_dist { + total_diff += diff; + } + } + movement.velocity += delta_time * min_distance.strength * total_diff; + } + } } diff --git a/src/systems/spawner.rs b/src/systems/spawner.rs index d318327..613afa3 100644 --- a/src/systems/spawner.rs +++ b/src/systems/spawner.rs @@ -30,10 +30,10 @@ impl Distribution for Standard { creature_type: "Herbivore".to_string(), }, 1 => CreatureTypeDistribution { - creature_type: "Carnivore".to_string(), + creature_type: "Herbivore".to_string(), }, _ => CreatureTypeDistribution { - creature_type: "Plant".to_string(), + creature_type: "Herbivore".to_string(), }, } } @@ -94,7 +94,7 @@ impl<'s> System<'s> for DebugSpawnTriggerSystem { self.timer_to_next_spawn -= delta_seconds; if self.timer_to_next_spawn <= 0.0 { let mut creature_entity_builder = lazy_update.create_entity(&entities); - self.timer_to_next_spawn = 1.5; + self.timer_to_next_spawn = 0.5; let mut rng = thread_rng(); let x = (rng.next_u32() % 100) as f32 / 5.0 - 10.0; let y = (rng.next_u32() % 100) as f32 / 5.0 - 10.0; From 6a37d1b6d1a1408315bcf3eb1615750cab1640de Mon Sep 17 00:00:00 2001 From: Victor Cornillere Date: Wed, 22 May 2019 15:38:00 +0200 Subject: [PATCH 3/9] Renamed flocking file to boids --- src/states/main_game.rs | 4 ++-- src/systems/experimental/{flocking.rs => boids.rs} | 0 src/systems/experimental/mod.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename src/systems/experimental/{flocking.rs => boids.rs} (100%) diff --git a/src/states/main_game.rs b/src/states/main_game.rs index d1649ff..e946fff 100644 --- a/src/states/main_game.rs +++ b/src/states/main_game.rs @@ -43,9 +43,9 @@ impl MainGameState { "entity_detection", &["spatial_grid"], ) - .with(flocking::FlockingSystem, "flocking", &["entity_detection"]) + .with(boids::FlockingSystem, "flocking", &["entity_detection"]) .with( - flocking::MinimumDistanceSystem, + boids::MinimumDistanceSystem, "minimum_distance", &["entity_detection"], ) diff --git a/src/systems/experimental/flocking.rs b/src/systems/experimental/boids.rs similarity index 100% rename from src/systems/experimental/flocking.rs rename to src/systems/experimental/boids.rs diff --git a/src/systems/experimental/mod.rs b/src/systems/experimental/mod.rs index 5063a3d..8e7677c 100644 --- a/src/systems/experimental/mod.rs +++ b/src/systems/experimental/mod.rs @@ -1,2 +1,2 @@ -pub mod flocking; +pub mod boids; pub mod perception; From eaa15261026bb7ca06511ad842b882c5c874a62d Mon Sep 17 00:00:00 2001 From: Victor Cornillere Date: Thu, 23 May 2019 18:10:16 +0200 Subject: [PATCH 4/9] More boids rule systems --- resources/prefabs/creatures/herbivore.ron | 11 +- src/components/experimental/boids.rs | 44 +++++++ src/states/main_game.rs | 18 ++- src/systems/experimental/boids.rs | 133 ++++++++++++++++++++-- src/systems/spawner.rs | 6 +- 5 files changed, 192 insertions(+), 20 deletions(-) diff --git a/resources/prefabs/creatures/herbivore.ron b/resources/prefabs/creatures/herbivore.ron index a63fee8..f572541 100644 --- a/resources/prefabs/creatures/herbivore.ron +++ b/resources/prefabs/creatures/herbivore.ron @@ -50,17 +50,20 @@ Prefab ( faction: "Herbivores", ), ), - intelligence_tag: (), perception: ( range: 5.0, ), boids: ( flocking: ( - strength: 1.0, + strength: 0.5, + ), + avoid: ( + names: ["Carnivore"], + strength: 20.0, ), minimum_distance: ( - minimum: 1.5, - strength: 10.0, + minimum: 1.1, + strength: 5.0, ), ), ), diff --git a/src/components/experimental/boids.rs b/src/components/experimental/boids.rs index d92ee20..0c9e448 100644 --- a/src/components/experimental/boids.rs +++ b/src/components/experimental/boids.rs @@ -18,6 +18,17 @@ impl Component for FlockingRule { type Storage = DenseVecStorage; } +#[derive(Default, Clone, Inspect, Serialize, Deserialize, PrefabData)] +#[prefab(Component)] +#[serde(default)] +pub struct MatchVelocityRule { + pub strength: f32, +} + +impl Component for MatchVelocityRule { + type Storage = DenseVecStorage; +} + #[derive(Default, Clone, Inspect, Serialize, Deserialize, PrefabData)] #[prefab(Component)] #[serde(default)] @@ -30,10 +41,43 @@ impl Component for MinimumDistanceRule { type Storage = DenseVecStorage; } +#[derive(Default, Clone, Serialize, Deserialize, PrefabData)] +#[prefab(Component)] +#[serde(default)] +pub struct AvoidRule { + pub names: Vec, + pub strength: f32, +} + +impl Component for AvoidRule { + type Storage = DenseVecStorage; +} + +#[derive(Clone, Serialize, Deserialize, PrefabData)] +#[prefab(Component)] +pub struct WorldBoundsRule { + pub strength: f32, +} + +impl Default for WorldBoundsRule { + fn default() -> Self { + WorldBoundsRule { strength: 10.0 } + } +} + +impl Component for WorldBoundsRule { + type Storage = DenseVecStorage; +} + #[derive(Default, Deserialize, Serialize, PrefabData)] #[serde(default)] #[serde(deny_unknown_fields)] pub struct BoidsPrefabData { flocking: Option, + match_velocity: Option, + avoid: Option, minimum_distance: Option, + + #[serde(skip)] + wall_bounds: WorldBoundsRule, } diff --git a/src/states/main_game.rs b/src/states/main_game.rs index e946fff..91e8c54 100644 --- a/src/states/main_game.rs +++ b/src/states/main_game.rs @@ -44,11 +44,17 @@ impl MainGameState { &["spatial_grid"], ) .with(boids::FlockingSystem, "flocking", &["entity_detection"]) + .with(boids::AvoidSystem, "avoid", &["entity_detection"]) .with( boids::MinimumDistanceSystem, "minimum_distance", &["entity_detection"], ) + .with( + boids::WorldBoundsSystem, + "world_bounds", + &["entity_detection"], + ) .with( QueryPredatorsAndPreySystem, "query_predators_and_prey_system", @@ -106,11 +112,11 @@ impl MainGameState { "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, @@ -273,7 +279,7 @@ impl<'a> State, 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() diff --git a/src/systems/experimental/boids.rs b/src/systems/experimental/boids.rs index dac9433..7a1fa25 100644 --- a/src/systems/experimental/boids.rs +++ b/src/systems/experimental/boids.rs @@ -10,10 +10,9 @@ use amethyst::{ }, }; -use crate::components::{ - boids::{FlockingRule, MinimumDistanceRule}, - creatures::Movement, - perception::DetectedEntities, +use crate::{ + components::{boids::*, creatures::Movement, perception::DetectedEntities}, + resources::world_bounds::WorldBounds, }; pub struct FlockingSystem; @@ -52,9 +51,52 @@ impl<'s> System<'s> for FlockingSystem { count += 1; } } - average_position /= count as f32; - let diff_vector = average_position - pos; - movement.velocity += delta_time * rule.strength * diff_vector; + if count >= 2 { + average_position /= count as f32; + let diff_vector = (average_position - pos); + movement.velocity += delta_time * rule.strength * diff_vector; + } + } + } +} + +pub struct MatchVelocitySystem; + +impl<'s> System<'s> for MatchVelocitySystem { + type SystemData = ( + ReadStorage<'s, Named>, + ReadStorage<'s, MatchVelocityRule>, + ReadStorage<'s, DetectedEntities>, + ReadStorage<'s, GlobalTransform>, + WriteStorage<'s, Movement>, + Read<'s, Time>, + ); + + fn run( + &mut self, + (names, velocity_rules, detected_entities, globals, mut movements, time): Self::SystemData, + ) { + let delta_time = time.delta_seconds(); + for (name, rule, detected, global, movement) in ( + &names, + &velocity_rules, + &detected_entities, + &globals, + &movements, + ) + .join() + { + let pos = Vector4::from(global.as_ref()[3]).xyz(); + let mut average_velocity = Vector3::new(0.0, 0.0, 0.0); + let mut count = 0; + for (other_name, other_global, other_movement, _) in + (&names, &globals, &movements, &detected.entities).join() + { + if other_name.name == name.name { + average_velocity += other_movement.velocity; + count += 1; + } + } } } } @@ -100,3 +142,80 @@ impl<'s> System<'s> for MinimumDistanceSystem { } } } + +pub struct AvoidSystem; + +impl<'s> System<'s> for AvoidSystem { + type SystemData = ( + ReadStorage<'s, Named>, + ReadStorage<'s, AvoidRule>, + ReadStorage<'s, DetectedEntities>, + ReadStorage<'s, GlobalTransform>, + WriteStorage<'s, Movement>, + Read<'s, Time>, + ); + + fn run( + &mut self, + (names, avoid_rules, detected_entities, globals, mut movements, time): Self::SystemData, + ) { + let delta_time = time.delta_seconds(); + for (name, rule, detected, global, mut movement) in ( + &names, + &avoid_rules, + &detected_entities, + &globals, + &mut movements, + ) + .join() + { + let pos = Vector4::from(global.as_ref()[3]).xyz(); + let mut average_position = Vector3::new(0.0, 0.0, 0.0); + let mut count = 0; + for (other_name, other_global, _) in (&names, &globals, &detected.entities).join() { + if rule.names.contains(&(&*other_name.name).to_string()) { + let other_pos = Vector4::from(other_global.as_ref()[3]).xyz(); + average_position += other_pos; + count += 1; + } + } + if count >= 1 { + average_position /= count as f32; + let diff_vector = (average_position - pos); + movement.velocity -= delta_time * rule.strength * diff_vector; + } + } + } +} + +pub struct WorldBoundsSystem; + +impl<'s> System<'s> for WorldBoundsSystem { + type SystemData = ( + Read<'s, WorldBounds>, + ReadStorage<'s, WorldBoundsRule>, + ReadStorage<'s, GlobalTransform>, + WriteStorage<'s, Movement>, + Read<'s, Time>, + ); + + fn run(&mut self, (bounds, bounds_rules, globals, mut movements, time): Self::SystemData) { + let delta_time = time.delta_seconds(); + for (rule, global, mut movement) in (&bounds_rules, &globals, &mut movements).join() { + let pos = Vector4::from(global.as_ref()[3]).xyz(); + + if pos[0] < bounds.left { + movement.velocity[0] += delta_time * rule.strength; + } + if pos[0] > bounds.right { + movement.velocity[0] -= delta_time * rule.strength; + } + if pos[1] < bounds.bottom { + movement.velocity[1] += delta_time * rule.strength; + } + if pos[1] > bounds.top { + movement.velocity[1] -= delta_time * rule.strength; + } + } + } +} diff --git a/src/systems/spawner.rs b/src/systems/spawner.rs index 613afa3..8c923cf 100644 --- a/src/systems/spawner.rs +++ b/src/systems/spawner.rs @@ -25,12 +25,12 @@ struct CreatureTypeDistribution { } impl Distribution for Standard { fn sample(&self, rng: &mut R) -> CreatureTypeDistribution { - match rng.gen_range(0, 3) { + match rng.gen_range(0, 6) { 0 => CreatureTypeDistribution { - creature_type: "Herbivore".to_string(), + creature_type: "Carnivore".to_string(), }, 1 => CreatureTypeDistribution { - creature_type: "Herbivore".to_string(), + creature_type: "Plant".to_string(), }, _ => CreatureTypeDistribution { creature_type: "Herbivore".to_string(), From 670e4d32c6ef5ff2942303a1d5424019a8d34289 Mon Sep 17 00:00:00 2001 From: Victor Cornillere Date: Fri, 24 May 2019 11:48:41 +0200 Subject: [PATCH 5/9] Add match velocity rule for Boids --- resources/prefabs/creatures/carnivore.ron | 1 + resources/prefabs/creatures/herbivore.ron | 11 +++++---- src/states/main_game.rs | 5 ++++ src/systems/experimental/boids.rs | 28 +++++++++++++++++++++-- 4 files changed, 39 insertions(+), 6 deletions(-) diff --git a/resources/prefabs/creatures/carnivore.ron b/resources/prefabs/creatures/carnivore.ron index d6973e7..dbd8860 100644 --- a/resources/prefabs/creatures/carnivore.ron +++ b/resources/prefabs/creatures/carnivore.ron @@ -54,6 +54,7 @@ Prefab ( perception: ( range: 5.0, ), + boids: (), ), ), ], diff --git a/resources/prefabs/creatures/herbivore.ron b/resources/prefabs/creatures/herbivore.ron index f572541..8055cdf 100644 --- a/resources/prefabs/creatures/herbivore.ron +++ b/resources/prefabs/creatures/herbivore.ron @@ -55,15 +55,18 @@ Prefab ( ), boids: ( flocking: ( - strength: 0.5, + strength: 0.4, + ), + match_velocity: ( + strength: 2.0, ), avoid: ( names: ["Carnivore"], - strength: 20.0, + strength: 30.0, ), minimum_distance: ( - minimum: 1.1, - strength: 5.0, + minimum: 1.3, + strength: 15.0, ), ), ), diff --git a/src/states/main_game.rs b/src/states/main_game.rs index 91e8c54..08eb956 100644 --- a/src/states/main_game.rs +++ b/src/states/main_game.rs @@ -44,6 +44,11 @@ impl MainGameState { &["spatial_grid"], ) .with(boids::FlockingSystem, "flocking", &["entity_detection"]) + .with( + boids::MatchVelocitySystem, + "match_velocity", + &["entity_detection"], + ) .with(boids::AvoidSystem, "avoid", &["entity_detection"]) .with( boids::MinimumDistanceSystem, diff --git a/src/systems/experimental/boids.rs b/src/systems/experimental/boids.rs index 7a1fa25..35380b8 100644 --- a/src/systems/experimental/boids.rs +++ b/src/systems/experimental/boids.rs @@ -10,6 +10,8 @@ use amethyst::{ }, }; +use std::collections::HashMap; + use crate::{ components::{boids::*, creatures::Movement, perception::DetectedEntities}, resources::world_bounds::WorldBounds, @@ -64,6 +66,7 @@ pub struct MatchVelocitySystem; impl<'s> System<'s> for MatchVelocitySystem { type SystemData = ( + Entities<'s>, ReadStorage<'s, Named>, ReadStorage<'s, MatchVelocityRule>, ReadStorage<'s, DetectedEntities>, @@ -74,10 +77,13 @@ impl<'s> System<'s> for MatchVelocitySystem { fn run( &mut self, - (names, velocity_rules, detected_entities, globals, mut movements, time): Self::SystemData, + (entities, names, velocity_rules, detected_entities, globals, mut movements, time): Self::SystemData, ) { let delta_time = time.delta_seconds(); - for (name, rule, detected, global, movement) in ( + + let mut perceived_velocities = HashMap::new(); + for (entity, name, rule, detected, global, movement) in ( + &entities, &names, &velocity_rules, &detected_entities, @@ -97,6 +103,24 @@ impl<'s> System<'s> for MatchVelocitySystem { count += 1; } } + if count >= 1 { + perceived_velocities.insert(entity, average_velocity / count as f32); + } + } + for (entity, _, rule, _, _, mut movement) in ( + &entities, + &names, + &velocity_rules, + &detected_entities, + &globals, + &mut movements, + ) + .join() + { + match perceived_velocities.get(&entity) { + Some(v) => movement.velocity += delta_time * rule.strength * v, + None => (), + } } } } From 627fce15e8280f9734845814eb8aedda5d13eb1e Mon Sep 17 00:00:00 2001 From: Victor Cornillere Date: Fri, 24 May 2019 11:50:02 +0200 Subject: [PATCH 6/9] Make predators avoid each other slightly --- resources/prefabs/creatures/carnivore.ron | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/resources/prefabs/creatures/carnivore.ron b/resources/prefabs/creatures/carnivore.ron index dbd8860..080cfe3 100644 --- a/resources/prefabs/creatures/carnivore.ron +++ b/resources/prefabs/creatures/carnivore.ron @@ -54,7 +54,12 @@ Prefab ( perception: ( range: 5.0, ), - boids: (), + boids: ( + avoid: ( + names: ["Carnivore"], + strength: 2.0, + ), + ), ), ), ], From 4dd951a924e903729513f26599ee742aa380e7e2 Mon Sep 17 00:00:00 2001 From: Victor Cornillere Date: Fri, 24 May 2019 14:32:49 +0200 Subject: [PATCH 7/9] Cleaned up code --- src/systems/behaviors/wander.rs | 114 ++++++++++++------------- src/systems/collision.rs | 46 +++++----- src/systems/experimental/boids.rs | 60 ++++--------- src/systems/experimental/perception.rs | 2 +- 4 files changed, 97 insertions(+), 125 deletions(-) diff --git a/src/systems/behaviors/wander.rs b/src/systems/behaviors/wander.rs index bb68b6d..bddce6a 100644 --- a/src/systems/behaviors/wander.rs +++ b/src/systems/behaviors/wander.rs @@ -1,57 +1,57 @@ -use amethyst::core::transform::Transform; -use amethyst::core::Time; -use amethyst::ecs::*; -use amethyst::renderer::*; - -use crate::components::creatures; -use rand::{thread_rng, Rng}; - -pub struct WanderSystem; -impl<'s> System<'s> for WanderSystem { - type SystemData = ( - WriteStorage<'s, creatures::Wander>, - WriteStorage<'s, creatures::Movement>, - ReadStorage<'s, Transform>, - Write<'s, DebugLines>, - Read<'s, Time>, - ); - - fn run( - &mut self, - (mut wanders, mut movements, locals, mut debug_lines, time): Self::SystemData, - ) { - let delta_time = time.delta_seconds(); - let mut rng = thread_rng(); - - for (wander, movement, local) in (&mut wanders, &mut movements, &locals).join() { - let position = local.translation(); - let future_position = position + movement.velocity * 0.5; - - let direction = wander.get_direction(); - let target = future_position + direction; - - let desired_velocity = target - position; - - movement.velocity += desired_velocity * delta_time; - - let change = 10.0; - if rng.gen::() { - wander.angle += change * delta_time; // Radians per second - } else { - wander.angle -= change * delta_time; - } - - debug_lines.draw_line( - [position.x, position.y, position.z].into(), - [future_position.x, future_position.y, future_position.z].into(), - [1.0, 0.05, 0.65, 1.0].into(), - ); - - debug_lines.draw_direction( - [future_position.x, future_position.y, future_position.z].into(), - [direction.x, direction.y, direction.z].into(), - [1.0, 0.05, 0.65, 1.0].into(), - ); - } - } -} +//use amethyst::core::transform::Transform; +//use amethyst::core::Time; +//use amethyst::ecs::*; +//use amethyst::renderer::*; + +//use crate::components::creatures; +//use rand::{thread_rng, Rng}; + +//pub struct WanderSystem; +//impl<'s> System<'s> for WanderSystem { +//type SystemData = ( +//WriteStorage<'s, creatures::Wander>, +//WriteStorage<'s, creatures::Movement>, +//ReadStorage<'s, Transform>, +//Write<'s, DebugLines>, +//Read<'s, Time>, +//); + +//fn run( +//&mut self, +//(mut wanders, mut movements, locals, mut debug_lines, time): Self::SystemData, +//) { +//let delta_time = time.delta_seconds(); +//let mut rng = thread_rng(); + +//for (wander, movement, local) in (&mut wanders, &mut movements, &locals).join() { +//let position = local.translation(); +//let future_position = position + movement.velocity * 0.5; + +//let direction = wander.get_direction(); +//let target = future_position + direction; + +//let desired_velocity = target - position; + +//movement.velocity += desired_velocity * delta_time; + +//let change = 10.0; +//if rng.gen::() { +//wander.angle += change * delta_time; // Radians per second +//} else { +//wander.angle -= change * delta_time; +//} + +//debug_lines.draw_line( +//[position.x, position.y, position.z].into(), +//[future_position.x, future_position.y, future_position.z].into(), +//[1.0, 0.05, 0.65, 1.0].into(), +//); + +//debug_lines.draw_direction( +//[future_position.x, future_position.y, future_position.z].into(), +//[direction.x, direction.y, direction.z].into(), +//[1.0, 0.05, 0.65, 1.0].into(), +//); +//} +//} +//} diff --git a/src/systems/collision.rs b/src/systems/collision.rs index 38fe361..dfcf6e6 100644 --- a/src/systems/collision.rs +++ b/src/systems/collision.rs @@ -8,29 +8,29 @@ use thread_profiler::profile_scope; use crate::components::collider; use crate::components::creatures; -use crate::resources::world_bounds::*; - -pub struct EnforceBoundsSystem; - -impl<'s> System<'s> for EnforceBoundsSystem { - type SystemData = (WriteStorage<'s, Transform>, Read<'s, WorldBounds>); - - fn run(&mut self, (mut locals, bounds): Self::SystemData) { - for local in (&mut locals).join() { - if local.translation().x > bounds.right { - local.translation_mut().x = bounds.right; - } else if local.translation().x < bounds.left { - local.translation_mut().x = bounds.left; - } - - if local.translation().y > bounds.top { - local.translation_mut().y = bounds.top; - } else if local.translation().y < bounds.bottom { - local.translation_mut().y = bounds.bottom; - } - } - } -} +//use crate::resources::world_bounds::*; + +//pub struct EnforceBoundsSystem; + +//impl<'s> System<'s> for EnforceBoundsSystem { +//type SystemData = (WriteStorage<'s, Transform>, Read<'s, WorldBounds>); + +//fn run(&mut self, (mut locals, bounds): Self::SystemData) { +//for local in (&mut locals).join() { +//if local.translation().x > bounds.right { +//local.translation_mut().x = bounds.right; +//} else if local.translation().x < bounds.left { +//local.translation_mut().x = bounds.left; +//} + +//if local.translation().y > bounds.top { +//local.translation_mut().y = bounds.top; +//} else if local.translation().y < bounds.bottom { +//local.translation_mut().y = bounds.bottom; +//} +//} +//} +//} #[derive(Debug, Clone)] pub struct CollisionEvent { diff --git a/src/systems/experimental/boids.rs b/src/systems/experimental/boids.rs index 35380b8..7bb1e3f 100644 --- a/src/systems/experimental/boids.rs +++ b/src/systems/experimental/boids.rs @@ -4,10 +4,7 @@ use amethyst::{ transform::GlobalTransform, Named, Time, }, - ecs::{ - BitSet, Entities, Join, Read, ReadExpect, ReadStorage, System, Write, WriteExpect, - WriteStorage, - }, + ecs::{Entities, Join, Read, ReadStorage, System, WriteStorage}, }; use std::collections::HashMap; @@ -34,7 +31,7 @@ impl<'s> System<'s> for FlockingSystem { (names, flocking_rules, detected_entities, globals, mut movements, time): Self::SystemData, ) { let delta_time = time.delta_seconds(); - for (name, rule, detected, global, mut movement) in ( + for (name, rule, detected, global, movement) in ( &names, &flocking_rules, &detected_entities, @@ -55,7 +52,7 @@ impl<'s> System<'s> for FlockingSystem { } if count >= 2 { average_position /= count as f32; - let diff_vector = (average_position - pos); + let diff_vector = average_position - pos; movement.velocity += delta_time * rule.strength * diff_vector; } } @@ -70,34 +67,23 @@ impl<'s> System<'s> for MatchVelocitySystem { ReadStorage<'s, Named>, ReadStorage<'s, MatchVelocityRule>, ReadStorage<'s, DetectedEntities>, - ReadStorage<'s, GlobalTransform>, WriteStorage<'s, Movement>, Read<'s, Time>, ); fn run( &mut self, - (entities, names, velocity_rules, detected_entities, globals, mut movements, time): Self::SystemData, + (entities, names, velocity_rules, detected_entities, mut movements, time): Self::SystemData, ) { let delta_time = time.delta_seconds(); let mut perceived_velocities = HashMap::new(); - for (entity, name, rule, detected, global, movement) in ( - &entities, - &names, - &velocity_rules, - &detected_entities, - &globals, - &movements, - ) - .join() + for (entity, name, _, detected) in + (&entities, &names, &velocity_rules, &detected_entities).join() { - let pos = Vector4::from(global.as_ref()[3]).xyz(); let mut average_velocity = Vector3::new(0.0, 0.0, 0.0); let mut count = 0; - for (other_name, other_global, other_movement, _) in - (&names, &globals, &movements, &detected.entities).join() - { + for (other_name, other_movement, _) in (&names, &movements, &detected.entities).join() { if other_name.name == name.name { average_velocity += other_movement.velocity; count += 1; @@ -107,12 +93,11 @@ impl<'s> System<'s> for MatchVelocitySystem { perceived_velocities.insert(entity, average_velocity / count as f32); } } - for (entity, _, rule, _, _, mut movement) in ( + for (entity, _, rule, _, movement) in ( &entities, &names, &velocity_rules, &detected_entities, - &globals, &mut movements, ) .join() @@ -129,7 +114,6 @@ pub struct MinimumDistanceSystem; impl<'s> System<'s> for MinimumDistanceSystem { type SystemData = ( - ReadStorage<'s, Named>, ReadStorage<'s, MinimumDistanceRule>, ReadStorage<'s, DetectedEntities>, ReadStorage<'s, GlobalTransform>, @@ -139,22 +123,16 @@ impl<'s> System<'s> for MinimumDistanceSystem { fn run( &mut self, - (names, min_distances, detected_entities, globals, mut movements, time): Self::SystemData, + (min_distances, detected_entities, globals, mut movements, time): Self::SystemData, ) { let delta_time = time.delta_seconds(); - for (name, min_distance, detected, global, mut movement) in ( - &names, - &min_distances, - &detected_entities, - &globals, - &mut movements, - ) - .join() + for (min_distance, detected, global, movement) in + (&min_distances, &detected_entities, &globals, &mut movements).join() { let sq_min_dist = min_distance.minimum * min_distance.minimum; let pos = Vector4::from(global.as_ref()[3]).xyz(); let mut total_diff = Vector3::new(0.0, 0.0, 0.0); - for (other_name, other_global, _) in (&names, &globals, &detected.entities).join() { + for (other_global, _) in (&globals, &detected.entities).join() { let other_pos = Vector4::from(other_global.as_ref()[3]).xyz(); let diff = pos - other_pos; let dist = diff.norm_squared(); @@ -184,14 +162,8 @@ impl<'s> System<'s> for AvoidSystem { (names, avoid_rules, detected_entities, globals, mut movements, time): Self::SystemData, ) { let delta_time = time.delta_seconds(); - for (name, rule, detected, global, mut movement) in ( - &names, - &avoid_rules, - &detected_entities, - &globals, - &mut movements, - ) - .join() + for (rule, detected, global, movement) in + (&avoid_rules, &detected_entities, &globals, &mut movements).join() { let pos = Vector4::from(global.as_ref()[3]).xyz(); let mut average_position = Vector3::new(0.0, 0.0, 0.0); @@ -205,7 +177,7 @@ impl<'s> System<'s> for AvoidSystem { } if count >= 1 { average_position /= count as f32; - let diff_vector = (average_position - pos); + let diff_vector = average_position - pos; movement.velocity -= delta_time * rule.strength * diff_vector; } } @@ -225,7 +197,7 @@ impl<'s> System<'s> for WorldBoundsSystem { fn run(&mut self, (bounds, bounds_rules, globals, mut movements, time): Self::SystemData) { let delta_time = time.delta_seconds(); - for (rule, global, mut movement) in (&bounds_rules, &globals, &mut movements).join() { + for (rule, global, movement) in (&bounds_rules, &globals, &mut movements).join() { let pos = Vector4::from(global.as_ref()[3]).xyz(); if pos[0] < bounds.left { diff --git a/src/systems/experimental/perception.rs b/src/systems/experimental/perception.rs index 2ff20e3..b9c64d6 100644 --- a/src/systems/experimental/perception.rs +++ b/src/systems/experimental/perception.rs @@ -84,7 +84,7 @@ impl<'s> System<'s> for DebugEntityDetectionSystem { fn run(&mut self, (detected_entities, globals, mut debug_lines): Self::SystemData) { for (detected, global) in (&detected_entities, &globals).join() { let pos = Vector4::from(global.as_ref()[3]).xyz(); - for (other_global, other_entity) in (&globals, &detected.entities).join() { + for (other_global, _) in (&globals, &detected.entities).join() { let other_pos = Vector4::from(other_global.as_ref()[3]).xyz(); debug_lines.draw_line( [pos[0], pos[1], 0.0].into(), From 9644b7653287762752500d6beaa0de2196086f71 Mon Sep 17 00:00:00 2001 From: Victor Cornillere Date: Fri, 24 May 2019 14:43:15 +0200 Subject: [PATCH 8/9] Fix grid test --- src/resources/experimental/spatial_grid.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/resources/experimental/spatial_grid.rs b/src/resources/experimental/spatial_grid.rs index 9c8c8ab..e4d68e2 100644 --- a/src/resources/experimental/spatial_grid.rs +++ b/src/resources/experimental/spatial_grid.rs @@ -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 + ); } } From 334924e43f6c05857af5b9483991028721c426b3 Mon Sep 17 00:00:00 2001 From: Victor Cornillere Date: Mon, 27 May 2019 09:52:00 +0200 Subject: [PATCH 9/9] Disable conflicting behavior systems and add Boids seek system --- resources/prefabs/creatures/carnivore.ron | 9 +-- resources/prefabs/creatures/herbivore.ron | 14 ++-- src/components/experimental/boids.rs | 15 +++- src/states/main_game.rs | 93 +++++++++++----------- src/systems/experimental/boids.rs | 97 +++++++++++++++++------ src/systems/spawner.rs | 2 +- 6 files changed, 147 insertions(+), 83 deletions(-) diff --git a/resources/prefabs/creatures/carnivore.ron b/resources/prefabs/creatures/carnivore.ron index 080cfe3..03c41a8 100644 --- a/resources/prefabs/creatures/carnivore.ron +++ b/resources/prefabs/creatures/carnivore.ron @@ -50,14 +50,13 @@ Prefab ( faction: "Carnivores", ), ), - intelligence_tag: (), perception: ( - range: 5.0, + range: 6.0, ), boids: ( - avoid: ( - names: ["Carnivore"], - strength: 2.0, + seek: ( + names: ["Herbivore"], + strength: 10.0, ), ), ), diff --git a/resources/prefabs/creatures/herbivore.ron b/resources/prefabs/creatures/herbivore.ron index 8055cdf..873bb9b 100644 --- a/resources/prefabs/creatures/herbivore.ron +++ b/resources/prefabs/creatures/herbivore.ron @@ -51,22 +51,26 @@ Prefab ( ), ), perception: ( - range: 5.0, + range: 4.0, ), boids: ( flocking: ( strength: 0.4, ), match_velocity: ( - strength: 2.0, + strength: 3.0, ), avoid: ( names: ["Carnivore"], - strength: 30.0, + strength: 20.0, + ), + seek: ( + names: ["Plant"], + strength: 10.0, ), minimum_distance: ( - minimum: 1.3, - strength: 15.0, + minimum: 1.5, + strength: 5.0, ), ), ), diff --git a/src/components/experimental/boids.rs b/src/components/experimental/boids.rs index 0c9e448..437e29d 100644 --- a/src/components/experimental/boids.rs +++ b/src/components/experimental/boids.rs @@ -53,6 +53,18 @@ impl Component for AvoidRule { type Storage = DenseVecStorage; } +#[derive(Default, Clone, Serialize, Deserialize, PrefabData)] +#[prefab(Component)] +#[serde(default)] +pub struct SeekRule { + pub names: Vec, + pub strength: f32, +} + +impl Component for SeekRule { + type Storage = DenseVecStorage; +} + #[derive(Clone, Serialize, Deserialize, PrefabData)] #[prefab(Component)] pub struct WorldBoundsRule { @@ -61,7 +73,7 @@ pub struct WorldBoundsRule { impl Default for WorldBoundsRule { fn default() -> Self { - WorldBoundsRule { strength: 10.0 } + WorldBoundsRule { strength: 20.0 } } } @@ -76,6 +88,7 @@ pub struct BoidsPrefabData { flocking: Option, match_velocity: Option, avoid: Option, + seek: Option, minimum_distance: Option, #[serde(skip)] diff --git a/src/states/main_game.rs b/src/states/main_game.rs index 08eb956..ee91c4c 100644 --- a/src/states/main_game.rs +++ b/src/states/main_game.rs @@ -50,6 +50,7 @@ impl MainGameState { &["entity_detection"], ) .with(boids::AvoidSystem, "avoid", &["entity_detection"]) + .with(boids::SeekSystem, "seek", &["entity_detection"]) .with( boids::MinimumDistanceSystem, "minimum_distance", @@ -60,52 +61,52 @@ impl MainGameState { "world_bounds", &["entity_detection"], ) - .with( - QueryPredatorsAndPreySystem, - "query_predators_and_prey_system", - &[], - ) - .with(ClosestObstacleSystem, "closest_obstacle_system", &[]) - .with( - ClosestSystem::::default(), - "closest_prey_system", - &["query_predators_and_prey_system"], - ) - .with( - ClosestSystem::::default(), - "closest_predator_system", - &["query_predators_and_prey_system"], - ) - .with( - SeekSystem::::new( - Rotation3::from_axis_angle(&Vector3::z_axis(), 0.0), - 1.0, - ), - "seek_prey_system", - &["closest_prey_system"], - ) - .with( - SeekSystem::::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::::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( + //QueryPredatorsAndPreySystem, + //"query_predators_and_prey_system", + //&[], + //) + //.with(ClosestObstacleSystem, "closest_obstacle_system", &[]) + //.with( + //ClosestSystem::::default(), + //"closest_prey_system", + //&["query_predators_and_prey_system"], + //) + //.with( + //ClosestSystem::::default(), + //"closest_predator_system", + //&["query_predators_and_prey_system"], + //) + //.with( + //SeekSystem::::new( + //Rotation3::from_axis_angle(&Vector3::z_axis(), 0.0), + //1.0, + //), + //"seek_prey_system", + //&["closest_prey_system"], + //) + //.with( + //SeekSystem::::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::::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", diff --git a/src/systems/experimental/boids.rs b/src/systems/experimental/boids.rs index 7bb1e3f..b19b949 100644 --- a/src/systems/experimental/boids.rs +++ b/src/systems/experimental/boids.rs @@ -8,6 +8,7 @@ use amethyst::{ }; use std::collections::HashMap; +use std::f32; use crate::{ components::{boids::*, creatures::Movement, perception::DetectedEntities}, @@ -114,6 +115,7 @@ pub struct MinimumDistanceSystem; impl<'s> System<'s> for MinimumDistanceSystem { type SystemData = ( + ReadStorage<'s, Named>, ReadStorage<'s, MinimumDistanceRule>, ReadStorage<'s, DetectedEntities>, ReadStorage<'s, GlobalTransform>, @@ -123,21 +125,29 @@ impl<'s> System<'s> for MinimumDistanceSystem { fn run( &mut self, - (min_distances, detected_entities, globals, mut movements, time): Self::SystemData, + (names, min_distances, detected_entities, globals, mut movements, time): Self::SystemData, ) { let delta_time = time.delta_seconds(); - for (min_distance, detected, global, movement) in - (&min_distances, &detected_entities, &globals, &mut movements).join() + for (name, min_distance, detected, global, movement) in ( + &names, + &min_distances, + &detected_entities, + &globals, + &mut movements, + ) + .join() { let sq_min_dist = min_distance.minimum * min_distance.minimum; let pos = Vector4::from(global.as_ref()[3]).xyz(); let mut total_diff = Vector3::new(0.0, 0.0, 0.0); - for (other_global, _) in (&globals, &detected.entities).join() { - let other_pos = Vector4::from(other_global.as_ref()[3]).xyz(); - let diff = pos - other_pos; - let dist = diff.norm_squared(); - if dist < sq_min_dist { - total_diff += diff; + for (other_name, other_global, _) in (&names, &globals, &detected.entities).join() { + if name.name == other_name.name { + let other_pos = Vector4::from(other_global.as_ref()[3]).xyz(); + let diff = pos - other_pos; + let dist = diff.norm_squared(); + if dist < sq_min_dist { + total_diff += diff; + } } } movement.velocity += delta_time * min_distance.strength * total_diff; @@ -166,20 +176,59 @@ impl<'s> System<'s> for AvoidSystem { (&avoid_rules, &detected_entities, &globals, &mut movements).join() { let pos = Vector4::from(global.as_ref()[3]).xyz(); - let mut average_position = Vector3::new(0.0, 0.0, 0.0); - let mut count = 0; + let mut min_sq_dist = std::f32::MAX; + let mut min_pos = pos; for (other_name, other_global, _) in (&names, &globals, &detected.entities).join() { if rule.names.contains(&(&*other_name.name).to_string()) { let other_pos = Vector4::from(other_global.as_ref()[3]).xyz(); - average_position += other_pos; - count += 1; + let sq_dist = (other_pos - pos).norm_squared(); + if sq_dist < min_sq_dist { + min_sq_dist = sq_dist; + min_pos = other_pos; + } } } - if count >= 1 { - average_position /= count as f32; - let diff_vector = average_position - pos; - movement.velocity -= delta_time * rule.strength * diff_vector; + let diff_vector = min_pos - pos; + movement.velocity -= delta_time * rule.strength * diff_vector; + } + } +} + +pub struct SeekSystem; + +impl<'s> System<'s> for SeekSystem { + type SystemData = ( + ReadStorage<'s, Named>, + ReadStorage<'s, SeekRule>, + ReadStorage<'s, DetectedEntities>, + ReadStorage<'s, GlobalTransform>, + WriteStorage<'s, Movement>, + Read<'s, Time>, + ); + + fn run( + &mut self, + (names, seek_rules, detected_entities, globals, mut movements, time): Self::SystemData, + ) { + let delta_time = time.delta_seconds(); + for (rule, detected, global, movement) in + (&seek_rules, &detected_entities, &globals, &mut movements).join() + { + let pos = Vector4::from(global.as_ref()[3]).xyz(); + let mut min_sq_dist = std::f32::MAX; + let mut min_pos = pos; + for (other_name, other_global, _) in (&names, &globals, &detected.entities).join() { + if rule.names.contains(&(&*other_name.name).to_string()) { + let other_pos = Vector4::from(other_global.as_ref()[3]).xyz(); + let sq_dist = (other_pos - pos).norm_squared(); + if sq_dist < min_sq_dist { + min_sq_dist = sq_dist; + min_pos = other_pos; + } + } } + let diff_vector = min_pos - pos; + movement.velocity += delta_time * rule.strength * diff_vector; } } } @@ -201,16 +250,14 @@ impl<'s> System<'s> for WorldBoundsSystem { let pos = Vector4::from(global.as_ref()[3]).xyz(); if pos[0] < bounds.left { - movement.velocity[0] += delta_time * rule.strength; - } - if pos[0] > bounds.right { - movement.velocity[0] -= delta_time * rule.strength; + movement.velocity[0] += delta_time * rule.strength * (bounds.left - pos[0]); + } else if pos[0] > bounds.right { + movement.velocity[0] -= delta_time * rule.strength * (pos[0] - bounds.right); } if pos[1] < bounds.bottom { - movement.velocity[1] += delta_time * rule.strength; - } - if pos[1] > bounds.top { - movement.velocity[1] -= delta_time * rule.strength; + movement.velocity[1] += delta_time * rule.strength * (bounds.bottom - pos[1]); + } else if pos[1] > bounds.top { + movement.velocity[1] -= delta_time * rule.strength * (pos[1] - bounds.top); } } } diff --git a/src/systems/spawner.rs b/src/systems/spawner.rs index 8c923cf..45e502a 100644 --- a/src/systems/spawner.rs +++ b/src/systems/spawner.rs @@ -29,7 +29,7 @@ impl Distribution for Standard { 0 => CreatureTypeDistribution { creature_type: "Carnivore".to_string(), }, - 1 => CreatureTypeDistribution { + 1 | 2 | 3 => CreatureTypeDistribution { creature_type: "Plant".to_string(), }, _ => CreatureTypeDistribution {