This repository has been archived by the owner on Oct 25, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 33
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add perception system with basic spatial acceleration structure (#68)
* Add perception components * Add skeleton of entity detection system * Add global transform component and system to get an entity absolute position (due to possible hierarchy of entities) * Add spatial grid system * Add spatial acceleration to the perception system * Use bitset for nearby entities detected
- Loading branch information
Showing
16 changed files
with
258 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -51,6 +51,9 @@ Prefab ( | |
), | ||
), | ||
intelligence_tag: (), | ||
perception: ( | ||
range: 5.0, | ||
), | ||
), | ||
), | ||
], | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -51,6 +51,9 @@ Prefab ( | |
), | ||
), | ||
intelligence_tag: (), | ||
perception: ( | ||
range: 5.0, | ||
), | ||
), | ||
), | ||
], | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -27,6 +27,9 @@ Prefab ( | |
value: 10.0, | ||
), | ||
), | ||
perception: ( | ||
range: 0.8, | ||
), | ||
), | ||
), | ||
], | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
pub mod perception; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
use amethyst::{ | ||
assets::{PrefabData, PrefabError}, | ||
ecs::{Component, DenseVecStorage, Entity, WriteStorage}, | ||
}; | ||
use amethyst_inspector::Inspect; | ||
use serde::{Deserialize, Serialize}; | ||
|
||
#[derive(Default, Clone, Debug, Inspect, Serialize, Deserialize, PrefabData)] | ||
#[prefab(Component)] | ||
#[serde(default)] | ||
pub struct Perception { | ||
pub range: f32, | ||
} | ||
|
||
impl Component for Perception { | ||
type Storage = DenseVecStorage<Self>; | ||
} | ||
|
||
#[derive(Default, Clone, Debug)] | ||
pub struct DetectedEntities { | ||
pub entities: Vec<Entity>, | ||
} | ||
|
||
impl Component for DetectedEntities { | ||
type Storage = DenseVecStorage<Self>; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,3 +3,6 @@ pub mod combat; | |
pub mod creatures; | ||
pub mod digestion; | ||
pub mod swarm; | ||
|
||
mod experimental; | ||
pub use experimental::*; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
pub mod spatial_grid; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
use amethyst::{ | ||
core::{nalgebra::Vector4, transform::GlobalTransform}, | ||
ecs::Entity, | ||
}; | ||
|
||
use std::collections::HashMap; | ||
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>>>, | ||
} | ||
|
||
impl SpatialGrid { | ||
pub fn new(cell_size: f32) -> Self { | ||
SpatialGrid { | ||
cell_size, | ||
cells: HashMap::new(), | ||
} | ||
} | ||
|
||
pub fn reset(&mut self) { | ||
self.cells = HashMap::new(); | ||
} | ||
|
||
// Insert an entity in the grid based on its GlobalTransform component. | ||
// This might have to change when upgrading Amethyst to 0.11 as the GlobalTransform component was removed. | ||
pub fn insert(&mut self, entity: Entity, transform: &GlobalTransform) { | ||
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 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); | ||
} | ||
|
||
// 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> { | ||
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(); | ||
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()), | ||
None => (), | ||
}, | ||
None => (), | ||
} | ||
} | ||
} | ||
entities | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
use amethyst::{ | ||
core::transform::Transform, | ||
ecs::{Builder, World}, | ||
}; | ||
|
||
#[test] | ||
fn grid_creation_insertion_and_query() { | ||
let mut world = World::new(); | ||
let mut spatial_grid = SpatialGrid::new(1.0f32); | ||
|
||
let transform = Transform::default(); | ||
let transform_matrix = transform.matrix(); | ||
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); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,3 +2,6 @@ pub mod audio; | |
pub mod debug; | ||
pub mod prefabs; | ||
pub mod world_bounds; | ||
|
||
mod experimental; | ||
pub use experimental::*; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
pub mod perception; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
use amethyst::{ | ||
core::{nalgebra::Vector4, transform::GlobalTransform}, | ||
ecs::{ | ||
BitSet, Entities, Join, ReadExpect, ReadStorage, System, Write, WriteExpect, WriteStorage, | ||
}, | ||
renderer::DebugLines, | ||
}; | ||
|
||
use crate::components::perception::{DetectedEntities, Perception}; | ||
use crate::resources::spatial_grid::SpatialGrid; | ||
|
||
pub struct EntityDetectionSystem; | ||
|
||
impl<'s> System<'s> for EntityDetectionSystem { | ||
type SystemData = ( | ||
Entities<'s>, | ||
ReadStorage<'s, Perception>, | ||
WriteStorage<'s, DetectedEntities>, | ||
ReadExpect<'s, SpatialGrid>, | ||
ReadStorage<'s, GlobalTransform>, | ||
); | ||
|
||
fn run( | ||
&mut self, | ||
(entities, perceptions, mut detected_entities, grid, globals): Self::SystemData, | ||
) { | ||
for (entity, _) in (&entities, &perceptions).join() { | ||
match detected_entities.get(entity) { | ||
Some(_) => (), | ||
None => { | ||
detected_entities | ||
.insert(entity, DetectedEntities::default()) | ||
.expect("Unreachable, we just tested the entity exists"); | ||
} | ||
} | ||
} | ||
|
||
for (entity, perception, mut detected, global) in | ||
(&entities, &perceptions, &mut detected_entities, &globals).join() | ||
{ | ||
detected.entities = Vec::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 { | ||
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); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
pub struct SpatialGridSystem; | ||
|
||
impl<'s> System<'s> for SpatialGridSystem { | ||
type SystemData = ( | ||
Entities<'s>, | ||
ReadStorage<'s, GlobalTransform>, | ||
WriteExpect<'s, SpatialGrid>, | ||
); | ||
|
||
fn run(&mut self, (entities, globals, mut spatial_grid): Self::SystemData) { | ||
spatial_grid.reset(); | ||
for (entity, global) in (&entities, &globals).join() { | ||
spatial_grid.insert(entity, global); | ||
} | ||
} | ||
} | ||
|
||
pub struct DebugEntityDetectionSystem; | ||
|
||
impl<'s> System<'s> for DebugEntityDetectionSystem { | ||
type SystemData = ( | ||
ReadStorage<'s, DetectedEntities>, | ||
ReadStorage<'s, GlobalTransform>, | ||
Write<'s, DebugLines>, | ||
); | ||
|
||
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(); | ||
let other_pos = Vector4::from(other_global.as_ref()[3]).xyz(); | ||
debug_lines.draw_line( | ||
[pos[0], pos[1], 0.0].into(), | ||
[other_pos[0], other_pos[1], 0.0].into(), | ||
[1.0, 1.0, 0.0, 1.0].into(), | ||
); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters