├── .gitignore ├── .travis.yml ├── Cargo.toml ├── README.md ├── examples ├── multiple_fields.rs ├── process_macro.rs ├── process_types.rs ├── render_gui_system.rs ├── spawn_system.rs └── systems.rs ├── src ├── aspect.rs ├── component.rs ├── entity.rs ├── fast_dict.rs ├── lib.rs ├── system.rs └── world.rs └── tests └── remove.rs /.gitignore: -------------------------------------------------------------------------------- 1 | # -*- mode: gitignore; -*- 2 | *~ 3 | target/* 4 | Cargo.lock 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: rust 3 | addons: 4 | apt: 5 | packages: 6 | - libcurl4-openssl-dev 7 | - libelf-dev 8 | - libdw-dev 9 | - binutils-dev 10 | rust: 11 | - stable 12 | # - beta 13 | # - stable 14 | # - 1.7.0 15 | before_script: 16 | - | 17 | pip install 'travis-cargo<0.2' --user && 18 | export PATH=$HOME/.local/bin:$PATH 19 | script: 20 | - | 21 | travis-cargo build && 22 | travis-cargo test && 23 | travis-cargo bench && 24 | travis-cargo --only stable doc 25 | after_success: 26 | - travis-cargo --only stable doc-upload 27 | - travis-cargo coveralls --no-sudo --verify 28 | env: 29 | global: 30 | - TRAVIS_CARGO_NIGHTLY_FEATURE="" 31 | - secure: '...' 32 | - secure: medqeiHVdoJTK5IVaT8XVbw9JaxKY4Tz6qaZqDIEkEo2U2ks/n6dv3dtSnDGiF4UGhYel/9j+q1uBB8KhFJqaBfpO/6EE6FD5vonrrfiJd/D8z3xwYeWMUA/vgURzLvxw4ts6CNFUwTk36VNuKZESXv2eTytBH15Ho0INh5J8SLsuJn0Yx+MWq5aqMunMEnAvKFvF7zaOG+EvU8GbW01pdbqH+ZW3a0eVsCb1VKgz1TkzP8S+//c0IYgS4R6hhyYLSN7zXKEnFRZ5ox+HGbwtePHOLbnt1owzltdNjwEiMJaI60m/vhP+kasc9l55xNCMtN0XQCYrpUhfnptgtilnFpbn4IEWzeT/f22uMCfPbFFKkskztGzSsPCSTGVZ8YbJ2YEEwXFC7ZA/gQtkD3o+P5cy1sKuCnzHAPTlPIKFlAi4cucImMFUuIF2PcUciQiofRUyso2TuJmkXHumzcgwMFrQw7yE45+93b1cKvia2sAhxorMEtzdAn259XwAqhAv8MDnfl1NvGY0prFZX+vXHzl9A8U2FOd07Rre/Hbg+eccGSOI0evjX1VDJuyqneL0pgDvONJVIPZpMl24ufA0xFnBX8o964rxsch1aNCGCBpGgVz7l6YsdViq98hwH8SYEvckqoDxMCmWr0MMFYu0HtnDiWnAHdyGqE3Ij8y88A= 33 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tinyecs" 3 | description = "Tiny entity component system" 4 | repository = "https://github.com/not-fl3/tinyecs.git" 5 | documentation = "http://not-fl3.github.io/tinyecs/tinyecs/" 6 | keywords = ["ecs","entity-system"] 7 | authors = ["not.fl3@gmail.com"] 8 | license = "MIT" 9 | version = "0.0.4" 10 | 11 | [features] 12 | prof = [] 13 | 14 | [dependencies] 15 | time = "0.1" 16 | tinyprof = "0.0.1" 17 | vec_map = "0.6.0" 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | TinyECS 2 | ======= 3 | 4 | [![Build Status](https://travis-ci.org/not-fl3/tinyecs.svg?branch=master)](https://travis-ci.org/not-fl3/tinyecs) 5 | 6 | Another Entity-Component-System, written in Rust. 7 | 8 | Usage 9 | ----- 10 | 11 | Usage 12 | 13 | Add the following to the Cargo.toml in your project: 14 | ``` 15 | [dependencies] 16 | tinyecs = "*" 17 | # features = ["prof"] <- for monitoring each system performance, but working only on nightly rust 18 | 19 | ``` 20 | and import using: 21 | ``` 22 | extern crate tinyecs; 23 | use tinyecs::*; 24 | ``` 25 | 26 | # Why another ecs? 27 | 28 | - mutliple mutable components access 29 | - no boilerplate for entity and systems creation/accessing 30 | - no restrictions on component content - non-copyable non-clonable structs is OK 31 | - entity creation possible almost everywhere 32 | - data aspects - possibility to view some additional entities while processing 33 | 34 | 35 | # Overview: 36 | 37 | - Entity is set of components identified by unique ID. 38 | - Component is struct with data. 39 | - System is behaviour working with components. 40 | 41 | - components: 42 | ```rust 43 | struct Position { 44 | x : i32, 45 | y : i32, 46 | z : i32 47 | } 48 | impl Component for Position {} 49 | ``` 50 | 51 | Entities: 52 | 53 | ```rust 54 | let mut entity_manager = world.entity_manager(); 55 | let entity = entity_manager.create_entity(); 56 | 57 | entity.add_component(Position {x : 0, y : 0, z : 0}); 58 | entity.add_component(Velocity {x : 1}); 59 | entity.refresh(); 60 | ``` 61 | 62 | Systems: 63 | ```rust 64 | process_entities!((MoveSystem): |pos: Position, vel: Velocity| => { 65 | pos.x += vel.x; 66 | println!("Moving! position: {}, velocity: {}", pos.x, vel.x); 67 | }); 68 | ``` 69 | 70 | Or without macroses: 71 | ```rust 72 | pub struct MoverSystem; 73 | impl System for MoverSystem { 74 | fn aspect(&self) -> Aspect { 75 | aspect_all![Position, Velocity] 76 | } 77 | 78 | fn process_one(&mut self, entity : &mut Entity) { 79 | let mut pos = entity.get_component::(); 80 | let vel = entity.get_component::(); // no problems with multiple mutable components 81 | 82 | pos.pos.x += vel.x; 83 | } 84 | } 85 | ``` 86 | 87 | More features, described only in /examples atm: 88 | - Aspects 89 | - Entity creation from system's process 90 | - Data aspects - for additional kind of entities in process 91 | - Different process styles 92 | 93 | 94 | [Online documentation](http://not-fl3.github.io/tinyecs/tinyecs/) 95 | -------------------------------------------------------------------------------- /examples/multiple_fields.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] extern crate tinyecs; 2 | 3 | use tinyecs::*; 4 | 5 | struct Health { 6 | hp : i32 7 | } 8 | impl Component for Health {} 9 | 10 | struct Position { 11 | x : i32 12 | } 13 | impl Component for Position {} 14 | 15 | struct Alive; 16 | impl Component for Alive {} 17 | 18 | pub struct BleedZoneSystem; 19 | impl System for BleedZoneSystem { 20 | fn aspect(&self) -> Aspect { 21 | aspect_all![Position, Health, Alive] 22 | } 23 | 24 | fn process_one(&mut self, entity : &mut Entity) { 25 | let pos = entity.get_component::(); 26 | let mut health = entity.get_component::(); 27 | 28 | if pos.x == 5 { 29 | health.hp -= 10; 30 | println!("You are in bleeding zone, hp: {}", health.hp); 31 | } 32 | if health.hp <= 0 { 33 | entity.remove_component::(); 34 | entity.refresh(); 35 | } 36 | } 37 | } 38 | 39 | fn main() { 40 | let mut world = World::new(); 41 | 42 | { 43 | let mut manager = world.entity_manager(); 44 | let entity = manager.create_entity(); 45 | entity.add_component(Health {hp : 100}); 46 | entity.add_component(Position {x : 5}); 47 | entity.add_component(Alive); 48 | entity.refresh(); 49 | } 50 | world.set_system(BleedZoneSystem); 51 | 52 | for _ in 0..100 { 53 | world.update(); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /examples/process_macro.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] extern crate tinyecs; 2 | 3 | use tinyecs::*; 4 | 5 | struct Position { 6 | x : i32 7 | } 8 | impl Component for Position {} 9 | 10 | struct Velocity { 11 | x : i32 12 | } 13 | impl Component for Velocity {} 14 | 15 | struct Player; 16 | impl Component for Player {} 17 | 18 | struct Bot; 19 | impl Component for Bot {} 20 | 21 | struct SomeTarget; 22 | impl Component for SomeTarget {} 23 | 24 | register_system!((MoveSystem): |_pos: Position, _vel: Velocity| => { 25 | _pos.x += _vel.x; 26 | println!("Moving! position: {}, velocity: {}", _pos.x, _vel.x); 27 | }); 28 | 29 | register_system!((AiSystem): |_bot: Bot, _pos: Position, _vel: Velocity| 30 | with (_players: aspect_all!(Player, Position), 31 | _targets: aspect_all!(SomeTarget, Position)) => { 32 | _pos.x += _vel.x; 33 | for target in _targets { 34 | let Position {x, ..} = *target.get_component::(); 35 | if _pos.x >= x { 36 | println!("Maybe attack this target?"); 37 | } 38 | } 39 | println!("Moving! position: {}, velocity: {}", _pos.x, _vel.x); 40 | }); 41 | 42 | 43 | fn main() { 44 | let mut world = World::new(); 45 | 46 | { 47 | let mut entity_manager = world.entity_manager(); 48 | let entity = entity_manager.create_entity(); 49 | 50 | entity.add_component(Position {x : 0}); 51 | entity.add_component(Velocity {x : 1}); 52 | entity.refresh(); 53 | } 54 | world.set_system(MoveSystem); 55 | world.update(); 56 | 57 | world.update(); 58 | world.update(); 59 | } 60 | -------------------------------------------------------------------------------- /examples/process_types.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] extern crate tinyecs; 2 | 3 | use tinyecs::*; 4 | 5 | pub struct Position { 6 | pub pos : [f32; 3] 7 | } 8 | impl Component for Position {} 9 | 10 | pub struct Mesh { 11 | pub mesh : String 12 | } 13 | impl Component for Mesh {} 14 | 15 | pub struct DeferMesh { 16 | pub order : i32 17 | } 18 | impl Component for DeferMesh {} 19 | 20 | pub struct Camera { 21 | pub pos : [f32; 3] 22 | } 23 | impl Component for Camera {} 24 | 25 | pub struct RenderSystem; 26 | 27 | impl System for RenderSystem { 28 | fn aspect(&self) -> Aspect { 29 | aspect_all![Position, Mesh] 30 | } 31 | fn data_aspects(&self) -> Vec { 32 | vec![Aspect::all::()] 33 | } 34 | fn process_d(&mut self, entity : &mut Entity, data : &mut DataList) { 35 | let cam = data.unwrap_entity(); 36 | let cam = cam.get_component::(); 37 | 38 | let pos = entity.get_component::(); 39 | let mesh = entity.get_component::(); 40 | 41 | println!("{}, {}, seen from camera pos: {:?}", mesh.mesh, pos.pos[0], cam.pos); 42 | } 43 | } 44 | 45 | pub struct DeferRenderSystem; 46 | 47 | impl System for DeferRenderSystem { 48 | fn aspect(&self) -> Aspect { 49 | aspect_all![Position, Mesh] 50 | } 51 | fn data_aspects(&self) -> Vec { 52 | vec![aspect_all![Camera]] 53 | } 54 | fn process_all(&mut self, 55 | entities : &mut Vec<&mut Entity>, 56 | _ : &mut WorldHandle, 57 | data : &mut DataList) { 58 | entities.sort_by(|e1, e2| { 59 | let defer1 = e1.get_component::(); 60 | let defer2 = e2.get_component::(); 61 | defer1.order.cmp(&defer2.order) 62 | }); 63 | for entity in entities { 64 | let cam = data.unwrap_entity(); 65 | let cam = cam.get_component::(); 66 | 67 | let mesh = entity.get_component::(); 68 | 69 | println!("{}, seen from camera pos: {:?}", mesh.mesh, cam.pos); 70 | } 71 | } 72 | } 73 | 74 | fn main() { 75 | let mut world = World::new(); 76 | 77 | { 78 | let mut entity_manager = world.entity_manager(); 79 | let entity = entity_manager.create_entity(); 80 | 81 | entity.add_component(Position {pos : [0.0, 0.0, 0.0]}); 82 | entity.add_component(Mesh {mesh : "player".to_string()}); 83 | entity.refresh(); 84 | } 85 | 86 | { 87 | let mut entity_manager = world.entity_manager(); 88 | let entity = entity_manager.create_entity(); 89 | entity.add_component(Camera {pos : [0.0, 0.0, 0.0]}); 90 | entity.refresh(); 91 | } 92 | // will process all entities with Position and Mesh, 93 | // and in this process all entities with Camera will be accessable 94 | world.set_system(RenderSystem); 95 | 96 | 97 | // same system, but we will use another implementetion inside it, for processing all entities at once 98 | world.set_system(DeferRenderSystem); 99 | 100 | world.update(); 101 | world.update(); 102 | } 103 | -------------------------------------------------------------------------------- /examples/render_gui_system.rs: -------------------------------------------------------------------------------- 1 | use std::rc::Rc; 2 | use std::cell::RefCell; 3 | 4 | #[macro_use] extern crate tinyecs; 5 | 6 | use tinyecs::*; 7 | 8 | pub struct GlutinFacade; 9 | impl GlutinFacade { 10 | pub fn new() -> GlutinFacade { 11 | GlutinFacade 12 | } 13 | pub fn draw_something(&mut self, some : &str) { 14 | println!("{}", some); 15 | } 16 | } 17 | pub struct HeavyGuiData; 18 | impl Component for HeavyGuiData {} 19 | impl HeavyGuiData { 20 | pub fn new(_ : &GlutinFacade) -> HeavyGuiData { 21 | HeavyGuiData 22 | } 23 | } 24 | 25 | transit_system!(Glutin2HeavySystem: RenderData => HeavyGuiData, 26 | |render_data| { HeavyGuiData::new(&render_data.facade.borrow_mut() ) }); 27 | 28 | pub struct Renderable; 29 | impl Component for Renderable {} 30 | 31 | pub struct GuiWindow; 32 | impl Component for GuiWindow {} 33 | 34 | pub struct RenderData { 35 | facade : Rc> 36 | } 37 | impl Component for RenderData {} 38 | 39 | 40 | pub struct RenderSystem { 41 | facade : Rc> 42 | } 43 | impl RenderSystem { 44 | pub fn new() -> RenderSystem { 45 | RenderSystem { 46 | facade : Rc::new(RefCell::new(GlutinFacade::new())) 47 | } 48 | } 49 | } 50 | 51 | impl System for RenderSystem { 52 | fn aspect(&self) -> Aspect { 53 | aspect_all!(Renderable) 54 | } 55 | fn on_created(&mut self, entity_manager : &mut EntityManager) { 56 | let data_entity = entity_manager.create_entity(); 57 | data_entity.add_component(RenderData {facade : self.facade.clone()}); 58 | } 59 | fn process_one(&mut self, _ : &mut Entity) { 60 | self.facade.borrow_mut().draw_something("triangles triangles"); 61 | } 62 | } 63 | 64 | pub struct GuiSystem; 65 | 66 | impl System for GuiSystem { 67 | fn aspect(&self) -> Aspect { 68 | aspect_all!(GuiWindow) 69 | } 70 | fn data_aspects(&self) -> Vec { 71 | vec![aspect_all!(RenderData, HeavyGuiData)] 72 | } 73 | 74 | fn process_d(&mut self, _ : &mut Entity, data : &mut DataList) { 75 | let render_data = data.unwrap_entity().get_component::(); 76 | let _gui_data = data.unwrap_entity().get_component::(); 77 | render_data.facade.borrow_mut().draw_something("gui gui gui"); 78 | } 79 | } 80 | 81 | fn main() { 82 | let mut world = World::new(); 83 | world.set_system(RenderSystem::new()); 84 | world.set_system(GuiSystem); 85 | world.set_system(Glutin2HeavySystem); 86 | 87 | { 88 | let mut entity_manager = world.entity_manager(); 89 | 90 | { 91 | let mesh = entity_manager.create_entity(); 92 | 93 | mesh.add_component(Renderable); 94 | mesh.refresh(); 95 | } 96 | { 97 | let window = entity_manager.create_entity(); 98 | 99 | window.add_component(GuiWindow); 100 | window.refresh(); 101 | } 102 | } 103 | world.update(); 104 | world.update(); 105 | world.update(); 106 | } 107 | -------------------------------------------------------------------------------- /examples/spawn_system.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] extern crate tinyecs; 2 | 3 | use tinyecs::*; 4 | 5 | pub struct Position; 6 | impl Component for Position {} 7 | 8 | pub struct SomeComponent { 9 | _some_data : String 10 | } 11 | impl Component for SomeComponent {} 12 | 13 | pub struct SpawnPoint { 14 | data : &'static str, 15 | count : i32 16 | } 17 | impl Component for SpawnPoint {} 18 | 19 | pub struct SpawnSystem; 20 | impl System for SpawnSystem { 21 | fn aspect(&self) -> Aspect { 22 | aspect_all!(SpawnPoint, Position) 23 | } 24 | 25 | fn process_w(&mut self, entity : &mut Entity, world : &mut WorldHandle) { 26 | { 27 | let mut spawn_point = entity.get_component::(); 28 | 29 | if spawn_point.count > 0 { 30 | let spawned = world.entity_manager.create_entity(); 31 | spawned.add_component(SomeComponent { _some_data : spawn_point.data.to_string() }); 32 | spawned.refresh(); 33 | 34 | spawn_point.count -= 1; 35 | } 36 | } 37 | entity.remove_component::(); 38 | entity.refresh(); 39 | } 40 | } 41 | 42 | fn main() { 43 | let mut world = World::new(); 44 | 45 | { 46 | let mut w = world.entity_manager(); 47 | let entity = w.create_entity(); 48 | 49 | entity.add_component(SpawnPoint {data : "player", count : 5}); 50 | entity.add_component(Position); 51 | entity.refresh(); 52 | } 53 | 54 | world.set_system(SpawnSystem); 55 | 56 | world.update(); 57 | world.update(); 58 | world.update(); 59 | world.update(); 60 | world.update(); 61 | world.update(); 62 | world.update(); 63 | world.update(); 64 | world.update(); 65 | world.update(); 66 | world.update(); 67 | world.update(); 68 | } 69 | -------------------------------------------------------------------------------- /examples/systems.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] extern crate tinyecs; 2 | 3 | use tinyecs::*; 4 | 5 | pub struct Dead; 6 | impl Component for Dead {} 7 | 8 | pub struct Position { 9 | pub pos : [f32; 3] 10 | } 11 | impl Component for Position {} 12 | 13 | 14 | pub struct DrawerSystem; 15 | impl System for DrawerSystem { 16 | fn aspect(&self) -> Aspect { 17 | aspect_all![Position] 18 | } 19 | 20 | fn on_added(&mut self, entity : &mut Entity) { 21 | println!("drawer added {}", entity.id); 22 | } 23 | 24 | fn process_one(&mut self, entity : &mut Entity) { 25 | let pos = entity.get_component::(); 26 | println!("{}", pos.pos[0]); 27 | } 28 | } 29 | 30 | pub struct DeadDrawerSystem; 31 | impl System for DeadDrawerSystem { 32 | fn aspect(&self) -> Aspect { 33 | aspect_all![Position].except::() 34 | } 35 | fn process_one(&mut self, entity : &mut Entity) { 36 | let pos = entity.get_component::(); 37 | println!("is dead {}", pos.pos[0]); 38 | } 39 | } 40 | 41 | pub struct MoverSystem; 42 | impl System for MoverSystem { 43 | fn aspect(&self) -> Aspect { 44 | aspect_all![Position, Dead] 45 | } 46 | 47 | fn process_one(&mut self, entity : &mut Entity) { 48 | let mut pos = entity.get_component::(); 49 | pos.pos[0] += 0.1; 50 | println!("Moved! {}", pos.pos[0]); 51 | } 52 | } 53 | 54 | 55 | fn main() { 56 | let mut world = World::new(); 57 | 58 | { 59 | let mut entity_manager = world.entity_manager(); 60 | let entity = entity_manager.create_entity(); 61 | entity.add_component(Position {pos : [0.0, 0.0, 0.0]}); 62 | entity.refresh(); 63 | } 64 | 65 | // if you have position, you will be drawn 66 | world.set_system(DrawerSystem); 67 | // except you are dead 68 | world.set_system(MoverSystem); 69 | // but only if you are dead your corpse will be draw, too 70 | world.set_system(DeadDrawerSystem); 71 | 72 | world.update(); 73 | world.update(); 74 | } 75 | -------------------------------------------------------------------------------- /src/aspect.rs: -------------------------------------------------------------------------------- 1 | use component::*; 2 | use entity::*; 3 | use std::any::{Any, TypeId}; 4 | 5 | /// data for systems, storing which components they should be intrested in 6 | pub struct Aspect { 7 | pub accept_types : Vec, 8 | pub not_accept_types : Vec 9 | } 10 | impl Aspect { 11 | pub fn check(&self, entity : &Entity) -> bool { 12 | self.accept_types.iter().all(|ty| { entity.components.borrow().keys().any(|t| t == ty )}) && 13 | self.not_accept_types.iter().any(|ty| { entity.components.borrow().keys().any(|t| t == ty) }) == false 14 | } 15 | } 16 | 17 | /// make aspect for all of this types 18 | #[macro_export] 19 | macro_rules! aspect_all{( $ ($aspect:ty), * ) => { 20 | { 21 | use std::any::TypeId; 22 | Aspect { 23 | accept_types : vec![$( TypeId::of::<$aspect>() ),*], 24 | not_accept_types : Vec::new() 25 | } 26 | } 27 | }} 28 | 29 | impl Aspect { 30 | pub fn all() -> Aspect { 31 | Aspect { 32 | accept_types : vec![TypeId::of::()], 33 | not_accept_types : Vec::new() 34 | } 35 | } 36 | pub fn all2() -> Aspect { 37 | Aspect { 38 | accept_types : vec![TypeId::of::(), TypeId::of::()], 39 | not_accept_types : Vec::new() 40 | } 41 | } 42 | pub fn all3() -> Aspect { 43 | Aspect { 44 | accept_types : vec![TypeId::of::(), TypeId::of::(), TypeId::of::()], 45 | not_accept_types : Vec::new() 46 | } 47 | } 48 | pub fn all4() -> Aspect { 52 | Aspect { 53 | accept_types : vec![TypeId::of::(), TypeId::of::(), TypeId::of::(), TypeId::of::()], 54 | not_accept_types : Vec::new() 55 | } 56 | } 57 | 58 | pub fn all5() -> Aspect { 63 | Aspect { 64 | accept_types : vec![TypeId::of::(), TypeId::of::(), TypeId::of::(), TypeId::of::(), TypeId::of::()], 65 | not_accept_types : Vec::new() 66 | } 67 | } 68 | 69 | pub fn except(mut self) -> Aspect { 70 | self.not_accept_types.push(TypeId::of::()); 71 | Aspect { 72 | accept_types : self.accept_types, 73 | not_accept_types : self.not_accept_types 74 | } 75 | } 76 | pub fn except2(mut self) -> Aspect { 77 | self.not_accept_types.push(TypeId::of::()); 78 | self.not_accept_types.push(TypeId::of::()); 79 | Aspect { 80 | accept_types : self.accept_types, 81 | not_accept_types : self.not_accept_types 82 | } 83 | } 84 | pub fn except3(mut self) -> Aspect { 85 | self.not_accept_types.push(TypeId::of::()); 86 | self.not_accept_types.push(TypeId::of::()); 87 | self.not_accept_types.push(TypeId::of::()); 88 | Aspect { 89 | accept_types : self.accept_types, 90 | not_accept_types : self.not_accept_types 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/component.rs: -------------------------------------------------------------------------------- 1 | use std::any::Any; 2 | 3 | pub trait Component : Any { 4 | } 5 | -------------------------------------------------------------------------------- /src/entity.rs: -------------------------------------------------------------------------------- 1 | use std::collections::{HashMap, HashSet}; 2 | use std::ops::{Deref, DerefMut, Drop}; 3 | use std::any::{Any, TypeId}; 4 | 5 | use std::cell::RefCell; 6 | use component::*; 7 | 8 | pub struct Entity { 9 | pub id : i32, 10 | pub components : RefCell>>, 11 | pub removed_components : RefCell>, 12 | fresh : RefCell 13 | } 14 | 15 | pub struct ComponentGuard<'a, T : Any> { 16 | component : Option>, 17 | collection : &'a RefCell>> 18 | } 19 | impl <'a, T : Any> Deref for ComponentGuard<'a, T> { 20 | type Target = T; 21 | fn deref(&self) -> &T { 22 | self.component.as_ref().unwrap() 23 | } 24 | } 25 | 26 | impl <'a, T : Any> DerefMut for ComponentGuard<'a, T> { 27 | fn deref_mut(&mut self) -> &mut T { 28 | self.component.as_mut().unwrap() 29 | } 30 | } 31 | impl<'a, T : Any> Drop for ComponentGuard<'a, T> { 32 | fn drop(&mut self) { 33 | self.component.take().and_then(|component| { 34 | self.collection.borrow_mut().insert(TypeId::of::(), component) 35 | }); 36 | } 37 | } 38 | 39 | impl Entity { 40 | pub fn new(id : i32) -> Entity { 41 | Entity { 42 | id : id, 43 | components : RefCell::new(HashMap::new()), 44 | removed_components : RefCell::new(HashSet::new()), 45 | fresh : RefCell::new(false) 46 | } 47 | } 48 | 49 | /// Mark this entity as not refreshed. 50 | /// On beginning of next frame new registered components will affect their systems. 51 | pub fn refresh(&self) { 52 | *self.fresh.borrow_mut() = false; 53 | } 54 | 55 | pub fn set_fresh(&self) { 56 | *self.fresh.borrow_mut() = true; 57 | } 58 | 59 | pub fn is_fresh(&self) -> bool { 60 | *self.fresh.borrow() == true 61 | } 62 | pub fn add_component(&self, component : T) { 63 | self.components.borrow_mut().insert(TypeId::of::(), Box::new(component)); 64 | } 65 | 66 | /// Remove component of given type from entity 67 | /// Be carefull, if this component is borrowed at this moment, it will not be really deleted. 68 | pub fn remove_component(&self) { 69 | if self.removed_components.borrow_mut().insert(TypeId::of::()) == false { 70 | panic!("Removing of removed components"); 71 | } 72 | } 73 | 74 | pub fn has_component(&self) -> bool { 75 | self.components.borrow().contains_key(&TypeId::of::()) 76 | } 77 | 78 | /// Move component from entity to CompoentGuard. In general case, it behaves like &mut T. 79 | /// While component is borrowed, second get_component() with same type will cause panic 80 | pub fn get_component(&self) -> ComponentGuard { 81 | let component = self.components.borrow_mut().remove(&TypeId::of::()).unwrap(); 82 | let c : Box = component.downcast().unwrap(); 83 | 84 | ComponentGuard { 85 | component: Some(c), 86 | collection: &self.components, 87 | } 88 | } 89 | 90 | #[doc(hidden)] 91 | pub fn get_components(&self) -> (ComponentGuard, ComponentGuard) { 92 | (self.get_component::(), self.get_component::()) 93 | } 94 | #[doc(hidden)] 95 | pub fn get_components3(&self) -> (ComponentGuard, ComponentGuard, ComponentGuard) { 96 | (self.get_component::(), self.get_component::(), self.get_component::()) 97 | } 98 | } 99 | 100 | -------------------------------------------------------------------------------- /src/fast_dict.rs: -------------------------------------------------------------------------------- 1 | use std::ptr; 2 | use std::slice::*; 3 | 4 | /// May be replaced by vect_map 5 | pub struct FastDictionary { 6 | pub vec : Vec> 7 | } 8 | #[allow(dead_code)] 9 | impl FastDictionary { 10 | pub fn new(max_index : i32) -> FastDictionary { 11 | FastDictionary { 12 | vec : { 13 | let mut vec = vec![]; 14 | vec.extend((0..max_index).map(|_| None)); 15 | vec 16 | } 17 | } 18 | } 19 | 20 | pub fn insert(&mut self, key : usize, value : T) { 21 | if key >= self.vec.len() { 22 | let len = self.vec.len(); 23 | let reserve = key - len + 1; 24 | self.vec.extend((0..reserve).map(|_| None)); 25 | } 26 | unsafe { 27 | ptr::write(self.vec.as_mut_ptr().offset(key as isize), Some(value)) 28 | } 29 | } 30 | 31 | #[inline] 32 | pub fn insert_no_check(&mut self, key : usize, value : T) { 33 | self.vec[key as usize] = Some(value); 34 | } 35 | #[inline] 36 | pub fn get_no_check(&self, key: isize) -> &T { 37 | unsafe {&(*self.vec.as_ptr().offset(key as isize)).as_ref().unwrap()} 38 | } 39 | 40 | #[inline] 41 | pub fn get_mut_no_check<'a>(&mut self, key: isize) -> &'a mut T { 42 | let ptr = self.vec.as_mut_ptr(); 43 | unsafe { 44 | use std::slice::from_raw_parts_mut; 45 | 46 | from_raw_parts_mut(ptr.offset(key as isize), 1).get_mut(0). 47 | unwrap().as_mut().unwrap() 48 | } 49 | } 50 | 51 | 52 | #[inline] 53 | pub fn get2_mut_no_check(&mut self, key: isize, key2 : isize) -> 54 | (&mut T, &mut T) { 55 | let ptr = self.vec.as_mut_ptr(); 56 | unsafe { 57 | use std::slice::from_raw_parts_mut; 58 | 59 | (from_raw_parts_mut(ptr.offset(key as isize), 1).get_mut(0).unwrap().as_mut().unwrap(), 60 | from_raw_parts_mut(ptr.offset(key2 as isize), 1).get_mut(0).unwrap().as_mut().unwrap()) 61 | } 62 | } 63 | 64 | 65 | #[inline] 66 | pub fn get_mut(&mut self, key: usize) -> Option<&mut T> { 67 | self.vec.get_mut(key).map(|x| { 68 | x.as_mut().unwrap() 69 | }) 70 | } 71 | 72 | pub fn iter(&self) -> FastDictIter { 73 | FastDictIter { 74 | vec : (&self.vec[..]).iter() 75 | } 76 | } 77 | 78 | pub fn iter_mut(&mut self) -> FastDictIterMut { 79 | FastDictIterMut { 80 | vec : (&mut self.vec[..]).iter_mut() 81 | } 82 | } 83 | 84 | pub fn traverse(&mut self, n : isize, mut f: F) 85 | where F: FnMut(&mut T) 86 | { 87 | f(self.get_mut_no_check(n)); 88 | } 89 | } 90 | 91 | pub struct FastDictIterMut<'a, T : 'a> { 92 | vec : IterMut<'a, Option> 93 | } 94 | 95 | impl<'a, T> Iterator for FastDictIterMut<'a, T> { 96 | type Item = &'a mut T; 97 | 98 | fn next(&mut self) -> Option<&'a mut T> { 99 | let item = self.vec.next(); 100 | match item { 101 | None => None, 102 | Some(mut item) => match item { 103 | &mut None => self.next(), 104 | &mut Some(ref mut item) => Some(item) 105 | } 106 | } 107 | } 108 | } 109 | 110 | pub struct FastDictIter<'a, T : 'a> { 111 | vec : Iter<'a, Option> 112 | } 113 | 114 | impl<'a, T> Iterator for FastDictIter<'a, T> { 115 | type Item = &'a T; 116 | fn next(&mut self) -> Option { 117 | let item = self.vec.next(); 118 | match item { 119 | None => None, 120 | Some( item) => match item { 121 | &None => self.next(), 122 | &Some(ref item) => Some(item) 123 | } 124 | } 125 | } 126 | } 127 | #[test] 128 | fn test_insert() { 129 | use std::collections::HashSet; 130 | 131 | let mut foo : FastDictionary> = FastDictionary::new(0); 132 | foo.insert(0, HashSet::new()); 133 | } 134 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | Easy to use entity component system. 3 | 4 | # Overview 5 | 6 | - Entity is set of components identified by unique ID. 7 | - Component is struct with data. 8 | - System is behaviour working with components. 9 | 10 | All components must implement Component trait: 11 | 12 | ```ignore 13 | struct Position { 14 | x : i32 15 | } 16 | impl Component for Position {} 17 | ``` 18 | 19 | Entity can be created almost everywhere: 20 | 21 | 22 | ```ignore 23 | let entity = entity_manager.create_entity(); 24 | 25 | entity.add_component(Position {x : 0, y : 0, z : 0}); 26 | entity.add_component(Velocity {x : 1}); 27 | entity.refresh(); 28 | ``` 29 | 30 | Simplest system can be created with macro. 31 | typically, you will need only process some components, like this: 32 | 33 | ```ignore 34 | process_entities!((MoveSystem): |pos: Position, vel: Velocity| => { 35 | pos.x += vel.x; 36 | println!("Moving! position: {}, velocity: {}", pos.x, vel.x); 37 | }); 38 | ``` 39 | 40 | This system now must be added to world like this: 41 | 42 | ```ignore 43 | world.set_system(MoveSystem::new()); 44 | ``` 45 | 46 | this macro will be expanded to this: 47 | 48 | ```ignore 49 | pub struct MoveSystem; 50 | impl System for MoveSystem { 51 | fn aspect() -> Aspect { 52 | aspect_all![Position, Velocity] 53 | } 54 | 55 | fn process_one(&mut self, entity: &mut Entity) { 56 | let mut pos = entity.get_component::(); 57 | let mut vel = entity.get_component::(); 58 | pos.x += vel.x; 59 | println!("Moving! position: {}, velocity: {}", pos.x, vel.x); 60 | } 61 | } 62 | ``` 63 | 64 | */ 65 | #![cfg_attr(feature = "prof", feature(core_intrinsics))] 66 | 67 | #[cfg(feature = "prof")] 68 | #[macro_use] extern crate tinyprof; 69 | 70 | #[cfg(not(feature = "prof"))] 71 | macro_rules! profile_region { ($name:expr) => { } } 72 | 73 | extern crate time; 74 | extern crate vec_map; 75 | 76 | mod entity; 77 | mod component; 78 | mod world; 79 | mod system; 80 | mod aspect; 81 | 82 | pub use world::*; 83 | -------------------------------------------------------------------------------- /src/system.rs: -------------------------------------------------------------------------------- 1 | use entity::*; 2 | use aspect::Aspect; 3 | use world::{WorldHandle, EntityManager}; 4 | use std::collections::HashSet; 5 | 6 | #[doc(hidden)] 7 | #[macro_export] 8 | macro_rules! impl_aspect { 9 | ( $( $t:ty ),* ) => { 10 | fn aspect(&self) -> Aspect { 11 | use std::any::TypeId; 12 | Aspect { 13 | accept_types : vec!($(TypeId::of::<$t>()),*), 14 | not_accept_types : Vec::new() 15 | } 16 | } 17 | } 18 | } 19 | 20 | #[doc(hidden)] 21 | #[macro_export] 22 | macro_rules! impl_data_aspect { 23 | ( $( $dataaspect:expr ),* ) => { 24 | fn data_aspects(&self) -> Vec { 25 | vec!($($dataaspect),*) 26 | } 27 | } 28 | } 29 | #[doc(hidden)] 30 | #[macro_export] 31 | macro_rules! impl_new { 32 | ($name:ident) => { 33 | impl $name { 34 | pub fn new() -> $name { 35 | $name 36 | } 37 | } 38 | } 39 | } 40 | 41 | /// Create struct and impl System trait for it 42 | /// 43 | /// ```ignore 44 | /// register_system!((MoveSystem): |_pos: Position, _vel: Velocity| => { 45 | /// }); 46 | /// ``` 47 | /// 48 | /// ```ignore 49 | /// register_system!((AiSystem): |_bot: Bot, _pos: Position, _vel: Velocity| 50 | /// with (_players: aspect_all!(Player, Position), 51 | /// _targets: aspect_all!(SomeTarget, Position)) => { 52 | /// }); 53 | /// ``` 54 | /// 55 | /// ```ignore 56 | /// register_system!((BotControlSystem 57 | /// aspect aspect_all!(Position, Bot).except2::()): 58 | /// |bot : Bot, pos : Position| 59 | /// with (scores: aspect_all!(ScoreTarget, Position), 60 | /// players: aspect_all!(Player, Position), 61 | /// objects: aspect_all!(ScoreObject, RigidBody)) => { 62 | /// }); 63 | /// ``` 64 | #[macro_export] 65 | macro_rules! register_system { 66 | ( ($name:ident aspect $aspect:expr): $entity:ident |$( $varname:ident: $t:ty ), *| with ($( $datavar:ident: $dataaspect:expr ), *) => $code:expr) => { 67 | pub struct $name; 68 | impl_new!($name); 69 | impl System for $name { 70 | fn aspect(&self) -> Aspect { 71 | $aspect 72 | } 73 | impl_data_aspect!($($dataaspect),*); 74 | 75 | fn process_d(&mut self, $entity : &mut Entity, data : &mut DataList) { 76 | let mut _n = 0; 77 | $( let mut $datavar = data.unwrap_nth(_n); _n += 1; )* 78 | $( let mut $varname = $entity.get_component::<$t>(); )* 79 | $code 80 | } 81 | } 82 | }; 83 | 84 | ( ($name:ident): |$( $varname:ident: $t:ty ), *| with ($( $datavar:ident: $aspect:expr ), *) => $code:expr) => { 85 | pub struct $name; 86 | impl_new!($name); 87 | impl System for $name { 88 | impl_aspect!($($t),*); 89 | impl_data_aspect!($($aspect),*); 90 | 91 | fn process_d(&mut self, entity : &mut Entity, data : &mut DataList) { 92 | let mut _n = 0; 93 | $( let mut $datavar = data.unwrap_nth(_n); _n += 1; )* 94 | $( let mut $varname = entity.get_component::<$t>(); )* 95 | $code 96 | } 97 | } 98 | }; 99 | 100 | ( ($name:ident): |$( $varname:ident: $t:ty ), *| => $code:expr) => { 101 | pub struct $name; 102 | impl_new!($name); 103 | impl System for $name { 104 | impl_aspect!($($t),*); 105 | 106 | fn process_one(&mut self, entity : &mut Entity) { 107 | $( let mut $varname = entity.get_component::<$t>(); )* 108 | $code 109 | } 110 | } 111 | }; 112 | } 113 | 114 | /// Usefull when you have one component, and want to have another, built on given one. 115 | /// Like this: 116 | /// 117 | /// ```ignore 118 | /// transit_system!(Glutin2HeavySystem: RenderData => HeavyGuiData, 119 | /// |render_data| { HeavyGuiData::new(&mut render_data.facade.borrow_mut() ) }); 120 | /// ``` 121 | /// With this system, HeavyGuiData will automatically be added to entities with RenderData. 122 | #[macro_export] 123 | macro_rules! transit_system { 124 | ($name:ident: $from:ty => $to:ty, |$var:ident| $how:expr) => { 125 | pub struct $name; 126 | impl_new!($name); 127 | impl System for $name { 128 | fn aspect(&self) -> Aspect { 129 | aspect_all!($from).except::<$to>() 130 | } 131 | 132 | fn process_one(&mut self, entity : &mut Entity) { 133 | let $var = entity.get_component::<$from>(); 134 | entity.add_component($how); 135 | entity.refresh(); 136 | } 137 | } 138 | }; 139 | } 140 | 141 | #[macro_export] 142 | macro_rules! transit_with_data_system { 143 | ($name:ident: $from:ty => $to:ty, ($( $datavar:ident: $datatype:ty ), *), |$var:ident, ($( $evar:ident: $etype:ty ), *)| $how:expr) => { 144 | pub struct $name; 145 | impl_new!($name); 146 | impl System for $name { 147 | fn aspect(&self) -> Aspect { 148 | aspect_all!($from).except::<$to>() 149 | } 150 | fn data_aspects(&self) -> Vec { 151 | vec![$(aspect_all!($datatype),)*] 152 | } 153 | fn process_d(&mut self, entity : &mut Entity, data : &mut DataList) { 154 | let $var = entity.get_component::<$from>(); 155 | $( let mut $evar = entity.get_component::<$etype>(); )* 156 | $( let mut $datavar = data.unwrap_entity().get_component::<$datatype>(); )* 157 | 158 | entity.add_component($how); 159 | entity.refresh(); 160 | } 161 | } 162 | }; 163 | } 164 | 165 | #[macro_export] 166 | macro_rules! transit_sync_system { 167 | ($name:ident: $from:ty => $to:ty, |$var:ident| $how:expr) => { 168 | pub struct $name; 169 | impl_new!($name); 170 | impl System for $name { 171 | fn aspect(&self) -> Aspect { 172 | aspect_all!($from).except::<$to>() 173 | } 174 | 175 | fn process_one(&mut self, entity : &mut Entity) { 176 | let $var = entity.read_sync_component::<$from>(); 177 | entity.add_component($how); 178 | entity.refresh(); 179 | } 180 | } 181 | }; 182 | } 183 | 184 | /// list with additional entitiy packs from data aspect 185 | /// 186 | /// Strongly recommends not use this ever, only for macroses! 187 | pub struct DataList<'a> { 188 | data : Vec> 189 | } 190 | impl<'b> DataList<'b> { 191 | pub fn unwrap_entity<'a>(&'a self) -> &'a Entity { 192 | &self.data[0][0] 193 | } 194 | 195 | pub fn unwrap_entity_nth<'a>(&'a self, n : usize) -> &'a Entity { 196 | &self.data[n][0] 197 | } 198 | pub fn unwrap_entity_mut<'a>(&'a mut self) -> &'a mut Entity { 199 | &mut self.data[0][0] 200 | } 201 | 202 | pub fn unwrap_all<'a>(&'a mut self) -> &'a mut Vec<&'b mut Entity> { 203 | &mut self.data[0] 204 | } 205 | 206 | pub fn unwrap_nth<'a>(&'a self, n : usize) -> &'a Vec<&'b mut Entity> { 207 | &self.data[n] 208 | } 209 | pub fn unwrap_mut_nth<'a>(&'a mut self, n : usize) -> &'a mut Vec<&'b mut Entity> { 210 | &mut self.data[n] 211 | } 212 | 213 | 214 | pub fn new(entity_manager : &mut EntityManager<'b>, ids : &Vec>) -> DataList<'b> { 215 | DataList { 216 | data : ids.iter().map(|i| {entity_manager.get_entities_by_ids(&i)}).collect() 217 | } 218 | } 219 | } 220 | 221 | /// System traits 222 | /// 223 | /// You can implement one of those processes, but if you implement process_all - only it will be called, and if you dont implement process_all - all process_* will be called. 224 | /// 225 | /// Most common case - implement only process_one. 226 | pub trait System { 227 | /// System will subscribe only on components, sutisfied by this aspect. 228 | fn aspect(&self) -> Aspect; 229 | 230 | /// For each returned aspect, one additional entity pack DataList will be received. 231 | /// Strongly recomends use it only with registration macro. 232 | fn data_aspects(&self) -> Vec { 233 | Vec::new() 234 | } 235 | 236 | #[cfg(feature = "prof")] 237 | fn get_name(&self) -> String { 238 | use std::intrinsics::*; 239 | 240 | let type_name = 241 | unsafe { 242 | type_name::() 243 | }; 244 | type_name.to_string() 245 | 246 | } 247 | fn on_created(&mut self, _ : &mut EntityManager) { 248 | 249 | } 250 | fn on_begin_frame(&mut self) { 251 | } 252 | 253 | fn on_added(&mut self, _ : &mut Entity) { 254 | } 255 | 256 | fn on_removed(&self, _ : &mut Entity) { 257 | } 258 | 259 | fn on_end_frame(&mut self) { 260 | } 261 | #[inline] 262 | fn process_w(&mut self, _ : &mut Entity, _ : &mut WorldHandle) { 263 | } 264 | 265 | #[inline] 266 | fn process_d(&mut self, _ : &mut Entity, _ : &mut DataList) { 267 | } 268 | #[inline] 269 | fn process_wd(&mut self, _ : &mut Entity, _ : &mut WorldHandle, _ : &mut DataList) { 270 | } 271 | 272 | #[inline] 273 | fn process_one(&mut self, _ : &mut Entity) { 274 | } 275 | 276 | fn process_all(&mut self, entities : &mut Vec<&mut Entity>, world: &mut WorldHandle, data : &mut DataList) { 277 | for e in entities.iter_mut() { 278 | self.process_one(e); 279 | self.process_w(e, world); 280 | self.process_d(e, data); 281 | self.process_wd(e, world, data); 282 | } 283 | } 284 | } 285 | -------------------------------------------------------------------------------- /src/world.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashSet; 2 | use time::PreciseTime; 3 | use vec_map::VecMap; 4 | 5 | 6 | pub use entity::*; 7 | pub use component::*; 8 | pub use system::*; 9 | pub use aspect::*; 10 | 11 | type EntityIdSet = HashSet; 12 | 13 | struct SystemData { 14 | pub system : Box, 15 | pub aspect : Aspect, 16 | pub data_aspects : Vec 17 | } 18 | 19 | impl SystemData { 20 | pub fn new(system : Box, aspect : Aspect, data_aspects : Vec) -> SystemData { 21 | SystemData { 22 | system : system, 23 | aspect : aspect, 24 | data_aspects : data_aspects 25 | } 26 | } 27 | } 28 | struct SelectedEntities { 29 | pub entity_set : EntityIdSet, 30 | pub data_set : Vec 31 | } 32 | 33 | pub struct World { 34 | entities : VecMap, 35 | systems : Vec<(SystemData, SelectedEntities)>, 36 | update_time : PreciseTime, 37 | last_id : i32, 38 | } 39 | 40 | /// part of the world, manipulating entities 41 | pub struct EntityManager<'a> { 42 | entities : &'a mut VecMap, 43 | last_id : &'a mut i32 44 | } 45 | impl<'a> EntityManager<'a> { 46 | pub fn create_entity_with_id(&mut self, id : i32) -> &mut Entity { 47 | (*self.last_id) = id; 48 | 49 | let e = Entity::new(id); 50 | 51 | let old = self.entities.insert(id as usize, e); 52 | if let Some(_) = old { 53 | panic!("inserting to existing id"); 54 | } 55 | 56 | self.entities.get_mut(*self.last_id as usize).unwrap() 57 | } 58 | 59 | pub fn create_entity(&mut self) -> &mut Entity { 60 | *self.last_id += 1; 61 | let id = self.last_id.clone(); 62 | self.create_entity_with_id(id) 63 | } 64 | 65 | pub fn try_get_entity(&mut self, id : i32) -> Option<&mut Entity> { 66 | self.entities.get_mut(id as usize) 67 | } 68 | 69 | pub fn get_entities_by_ids(&mut self, ids : &HashSet) -> Vec<&'a mut Entity> { 70 | ids.iter().map(|id| { 71 | let e : &mut Entity = self.entities.get_mut(*id as usize).unwrap(); 72 | unsafe { 73 | ::std::mem::transmute(e) 74 | } 75 | }).collect::>() 76 | } 77 | } 78 | 79 | /// World handle, that will be accesible in system's process 80 | pub struct WorldHandle<'a> { 81 | /// Delta from last world tick. 82 | pub delta : f32, 83 | /// Entity manager with access to all worlds entities 84 | pub entity_manager : EntityManager<'a> 85 | } 86 | 87 | impl World { 88 | pub fn new() -> World { 89 | World { 90 | last_id : 0, 91 | update_time : PreciseTime::now(), 92 | entities : VecMap::with_capacity(3000), 93 | systems : Vec::new() 94 | } 95 | } 96 | 97 | /// Get entity manager for manupalating with entities. 98 | /// 99 | /// # Examples 100 | /// ```ignore 101 | /// use tinyecs::*; 102 | /// let mut world = World::new(); 103 | /// 104 | /// { 105 | /// let mut entity_manager = world.entity_manager(); 106 | /// let _entity = entity_manager.create_entity(); 107 | /// // _entity.add_component(); or something 108 | /// } 109 | /// ``` 110 | pub fn entity_manager<'a>(&'a mut self) -> EntityManager<'a> { 111 | EntityManager { 112 | last_id : &mut self.last_id, 113 | entities : &mut self.entities 114 | } 115 | } 116 | 117 | /// Add new active system. 118 | pub fn set_system(&mut self, mut system : TSys) 119 | where TSys : 'static + System { 120 | let aspect = system.aspect(); 121 | let data_aspects = system.data_aspects(); 122 | 123 | system.on_created(&mut EntityManager { 124 | last_id : &mut self.last_id, 125 | entities : &mut self.entities 126 | }); 127 | self.systems.push((SystemData::new(Box::new(system), aspect, data_aspects), 128 | SelectedEntities { 129 | entity_set : HashSet::new(), 130 | data_set : vec![HashSet::new(); 0] 131 | })); 132 | for (_, e) in self.entities.iter_mut() { 133 | e.set_fresh(); 134 | } 135 | } 136 | 137 | /// Tick all systems in world. 138 | /// All on_added and on_removed will passed inside this method. 139 | pub fn update(&mut self) { 140 | let delta = self.update_time.to(PreciseTime::now()); 141 | let float_delta = delta.num_seconds() as f32 + delta.num_milliseconds() as f32 / 1000.0; 142 | 143 | self.update_time = PreciseTime::now(); 144 | 145 | let mut systems = &mut self.systems; 146 | 147 | { 148 | profile_region!("refresh entities"); 149 | for (_, e) in self.entities.iter_mut() { 150 | //.filter(|&(_, ref e)| {*e.fresh.borrow_mut() == false}) 151 | Self::refresh_entity(e, systems); 152 | e.set_fresh(); 153 | } 154 | } 155 | 156 | let mut world_data = WorldHandle { 157 | delta : float_delta, 158 | entity_manager : EntityManager { 159 | last_id : &mut self.last_id, 160 | entities : &mut self.entities 161 | } 162 | }; 163 | 164 | 165 | { 166 | profile_region!("all begin frames"); 167 | for &mut (ref mut system, ref entities) in systems.iter_mut() { 168 | if entities.entity_set.len() != 0 { 169 | profile_region!(&format!("on_begin_frame: {}", system.system.get_name())); 170 | (*system.system).on_begin_frame(); 171 | } 172 | } 173 | } 174 | { 175 | profile_region!("all updates"); 176 | for &mut (ref mut system, ref mut entities) in systems.iter_mut() { 177 | if entities.entity_set.len() != 0 { 178 | let mut refs = world_data.entity_manager.get_entities_by_ids(&entities.entity_set); 179 | 180 | { 181 | profile_region!(&system.system.get_name()); 182 | if system.data_aspects.len() == 0 || 183 | (entities.data_set.len() != 0 && 184 | entities.data_set[0].len() != 0) { 185 | let mut some_data = DataList::new(&mut world_data.entity_manager, &entities.data_set); 186 | (*system.system).process_all(&mut refs, &mut world_data, &mut some_data); 187 | } 188 | } 189 | } 190 | } 191 | } 192 | 193 | { 194 | profile_region!("all end frames"); 195 | for &mut(ref mut system, ref entities) in systems.iter_mut() { 196 | if entities.entity_set.len() != 0 { 197 | profile_region!(&format!("end_frame: {}", system.system.get_name())); 198 | (*system.system).on_end_frame(); 199 | } 200 | } 201 | } 202 | 203 | } 204 | 205 | fn refresh_entity(e : &mut Entity, 206 | systems : &mut Vec<(SystemData, SelectedEntities)>) { 207 | { 208 | let mut deleted = e.removed_components.borrow_mut(); 209 | let mut components = e.components.borrow_mut(); 210 | 211 | for del in deleted.drain() { 212 | components.remove(&del); 213 | } 214 | } 215 | 216 | for & mut(SystemData { ref mut system, ref mut aspect, ref mut data_aspects }, ref mut entities) in systems.iter_mut() { 217 | if aspect.check(e) { 218 | if entities.entity_set.contains(&e.id) == false { 219 | profile_region!(&format!("on_added: {}", system.get_name())); 220 | 221 | entities.entity_set.insert(e.id); 222 | system.on_added(e); 223 | } 224 | } else { 225 | if entities.entity_set.contains(&e.id) { 226 | profile_region!(&format!("on_removed: {}", system.get_name())); 227 | entities.entity_set.remove(&e.id); 228 | system.on_removed(e); 229 | } 230 | } 231 | 232 | if entities.data_set.len() != data_aspects.len() { 233 | entities.data_set.resize(data_aspects.len(), HashSet::new()); 234 | } 235 | for (data_aspect, mut entities) in data_aspects.iter(). 236 | zip(entities.data_set.iter_mut()) 237 | { 238 | if data_aspect.check(e) { 239 | if entities.contains(&e.id) == false { 240 | entities.insert(e.id); 241 | } 242 | } else { 243 | if entities.contains(&e.id) { 244 | entities.remove(&e.id); 245 | } 246 | } 247 | } 248 | } 249 | 250 | } 251 | 252 | } 253 | -------------------------------------------------------------------------------- /tests/remove.rs: -------------------------------------------------------------------------------- 1 | extern crate tinyecs; 2 | 3 | use tinyecs::*; 4 | 5 | pub struct ComponentA; 6 | impl Component for ComponentA {} 7 | 8 | pub struct SystemA; 9 | impl System for SystemA { 10 | fn aspect(&self) -> Aspect { 11 | Aspect::all::() 12 | } 13 | fn process_one(&mut self, e : &mut Entity) { 14 | e.remove_component::(); 15 | e.refresh(); 16 | } 17 | } 18 | 19 | pub struct SystemB; 20 | impl System for SystemB { 21 | fn aspect(&self) -> Aspect { 22 | Aspect::all::() 23 | } 24 | fn process_one(&mut self, e : &mut Entity) { 25 | e.get_component::(); 26 | } 27 | } 28 | 29 | #[test] 30 | fn test_remove_component() { 31 | let mut world = World::new(); 32 | let id = { 33 | let mut entity_manager = world.entity_manager(); 34 | let e = entity_manager.create_entity(); 35 | e.add_component(ComponentA); 36 | e.refresh(); 37 | e.id 38 | }; 39 | 40 | world.set_system(SystemA); 41 | world.set_system(SystemB); 42 | 43 | for _ in 0 .. 10 { 44 | world.update(); 45 | } 46 | 47 | { 48 | let mut entity_manager = world.entity_manager(); 49 | let entity = entity_manager.try_get_entity(id).unwrap(); 50 | assert_eq!(entity.has_component::(), false); 51 | } 52 | } 53 | --------------------------------------------------------------------------------