Skip to content

Commit

Permalink
new create_entity_batch() method + adhere more to Rust API guidelines
Browse files Browse the repository at this point in the history
  • Loading branch information
DynamicGoose committed Nov 2, 2024
1 parent d085551 commit 3fec086
Show file tree
Hide file tree
Showing 8 changed files with 136 additions and 7 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "magma_ecs"
version = "0.2.0-beta.1"
version = "0.2.0-beta.2"
edition = "2021"
license = "MIT"
description = "Entity-Component-System for the Magma3D game engine"
Expand Down
102 changes: 102 additions & 0 deletions src/entities.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,87 @@ impl Entities {
}
}

pub(crate) fn create_entity_batch(
&self,
components: impl ComponentSet,
mut num: usize,
) -> Result<(), EntityError> {
let mut map = self.map.write();
let mut result = Ok(());

let reuse: Vec<usize> = map
.par_iter()
.enumerate()
.filter(|(_, mask)| mask.is_empty())
.map(|(index, _)| index)
.collect();

let mut component_vecs = vec![];
components.for_components(|type_id, component| {
if let Some(component_vec) = self.components.get(&type_id) {
component_vecs.push((
component_vec,
component,
*self.bit_masks.get(&type_id).unwrap(),
));
} else {
result = Err(EntityError::ComponentNotRegistered);
}
});

result?;

if reuse.len() <= num {
num -= reuse.len();
reuse.par_iter().for_each(|index| {
component_vecs
.par_iter()
.for_each(|(component_vec, component, _)| {
*component_vec.write().get_mut(*index).unwrap() =
Some(component.to_owned());
});
});
reuse.iter().for_each(|index| {
component_vecs.iter().for_each(|(_, _, bit_mask)| {
map[*index].insert(*bit_mask);
});
});

for _ in 0..num {
self.components
.par_iter()
.for_each(|(_, components)| components.write().push(None));
map.push(RoaringBitmap::new());

let index = map.len() - 1;
component_vecs
.par_iter()
.for_each(|(component_vec, component, _)| {
*component_vec.write().get_mut(index).unwrap() = Some(component.to_owned());
});
component_vecs.iter().for_each(|(_, _, bit_mask)| {
map[index].insert(*bit_mask);
});
}
Ok(())
} else {
reuse.par_iter().skip(reuse.len() - num).for_each(|index| {
component_vecs
.par_iter()
.for_each(|(component_vec, component, _)| {
*component_vec.write().get_mut(*index).unwrap() =
Some(component.to_owned());
});
});
reuse.iter().skip(reuse.len() - num).for_each(|index| {
component_vecs.iter().for_each(|(_, _, bit_mask)| {
map[*index].insert(*bit_mask);
});
});
Ok(())
}
}

pub(crate) fn get_bitmask(&self, type_id: &TypeId) -> Option<&u32> {
self.bit_masks.get(type_id)
}
Expand Down Expand Up @@ -170,6 +251,27 @@ mod test {
assert!(speed.read()[0].is_none());
}

#[test]
fn create_entity_batch() {
let mut entities = Entities::default();
entities.register_component::<Health>();

entities.create_entity_batch((Health(10),), 100).unwrap();
{
let health = entities.components.get(&TypeId::of::<Health>()).unwrap();
assert_eq!(health.read().len(), 100);
}
for i in 0..50 {
entities.delete_entity_by_id(i).unwrap();
}
entities.create_entity_batch((Health(10),), 10).unwrap();
assert!(entities.map.read()[39].is_empty());
entities.create_entity_batch((Health(10),), 60).unwrap();
assert!(!entities.map.read()[0].is_empty());
let health = entities.components.get(&TypeId::of::<Health>()).unwrap();
assert_eq!(health.read().len(), 120);
}

#[test]
fn entity_with_component() {
let mut entities = Entities::default();
Expand Down
21 changes: 20 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ pub mod resources;
pub mod systems;

/// The [`World`] struct holds all the data of our world.
#[derive(Default)]
#[derive(Default, Debug)]
pub struct World {
resources: Resources,
entities: Entities,
Expand Down Expand Up @@ -120,6 +120,25 @@ impl World {
self.entities.create_entity(components)
}

/// Spawn a batch of entities with the same components. This is more efficient if you have to spawn large amounts of entities.
/// ```
/// use magma_ecs::World;
///
/// let mut world = World::new();
/// world.register_component::<u32>();
/// world.register_component::<f32>();
///
/// // spawn 100 entities
/// world.create_entity_batch((30_u32, 60_f32), 100).unwrap();
/// ```
pub fn create_entity_batch(
&self,
components: impl ComponentSet,
num: usize,
) -> Result<(), EntityError> {
self.entities.create_entity_batch(components, num)
}

/// Get a [`Query`] on the [`World`]'s [`Entities`].
pub fn query(&self) -> Query {
self.entities.query()
Expand Down
2 changes: 1 addition & 1 deletion src/resources.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use std::{

use crate::error::ResourceError;

#[derive(Default)]
#[derive(Default, Debug)]
pub struct Resources {
data: RwLock<HashMap<TypeId, Arc<RwLock<dyn Any + Send + Sync>>>>,
}
Expand Down
4 changes: 2 additions & 2 deletions src/systems.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::World;
/// The [`Dispatcher`] is used to dispatch [`Systems`] in parallel on a [`World`].
pub mod dispatcher;

#[derive(Clone, PartialEq)]
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub(crate) struct System {
pub run: fn(&World),
pub name: &'static str,
Expand All @@ -19,7 +19,7 @@ impl System {
}

/// Holds systems and their dependencies
#[derive(Default, Clone)]
#[derive(Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Systems(pub(crate) Vec<System>);

impl Systems {
Expand Down
2 changes: 1 addition & 1 deletion src/systems/dispatcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::World;
use super::{System, Systems};

/// Used to dispatch [`Systems`] on a [`World`] in parallel
#[derive(Default)]
#[derive(Default, Debug, Clone)]
pub struct Dispatcher(Vec<Vec<fn(&World)>>);

impl Dispatcher {
Expand Down
8 changes: 8 additions & 0 deletions tests/entities.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@ fn create_entity() {
world.create_entity((400_u64,)).unwrap();
}

#[test]
fn create_entity_batch() {
let mut world = World::new();
world.register_component::<u64>();

world.create_entity_batch((400_u64,), 1000).unwrap();
}

#[test]
fn query() {
let mut world = World::new();
Expand Down

0 comments on commit 3fec086

Please sign in to comment.