1 /** internal orhestration of entity/component system */ 2 3 module re.ecs.manager; 4 5 import re.ecs.entity; 6 import re.ecs.component; 7 import re.ecs.storage; 8 import std.algorithm; 9 import std.array; 10 11 /// manages the entity/component system 12 class EntityManager { 13 /// list of all entities 14 public Entity[] entities; 15 /// helper to store components in an optimized way 16 public ComponentStorage storage; 17 private size_t[] entities_to_remove; 18 private size_t entity_counter; 19 20 /// sets up the ECS 21 this() { 22 storage = new ComponentStorage(this); 23 } 24 25 /// create a fresh entity 26 public Entity create_entity() { 27 auto nt = new Entity(this); 28 nt.initialize(); 29 nt.id = entity_counter++; 30 entities ~= nt; 31 return nt; 32 } 33 34 public Entity get_entity(string name) { 35 auto list = entities.find!(x => x.name == name); 36 if (list.length == 0) { 37 assert(0, "no matching entity was found"); 38 } 39 return list[0]; 40 } 41 42 public bool has_entity(string name) { 43 return entities.any!(x => x.name == name); 44 } 45 46 /// remove an entity 47 public void remove_entity(Entity entity) { 48 entities.remove!(x => x == entity); 49 // TODO: entity pooling 50 } 51 52 /// keeps all the ducks in line 53 public void update() { 54 entities_to_remove = []; 55 for (size_t i = 0; i < entities.length; i++) { 56 auto nt = entities[i]; 57 if (!nt.alive) { 58 entities_to_remove ~= i; 59 } 60 } 61 62 // remove entities 63 foreach (to_remove; entities_to_remove) { 64 entities = remove(entities, to_remove); 65 } 66 } 67 68 /// destroy all entities and components and clean up 69 public void destroy() { 70 foreach (entity; entities) { 71 entity.destroy(); 72 } 73 } 74 } 75 76 @("ecs-basic") 77 unittest { 78 class Food : Component { 79 public bool tasty = true; 80 } 81 82 auto ecs = new EntityManager(); 83 auto nt = ecs.create_entity(); 84 auto food = new Food(); 85 nt.add_component(food); 86 assert(nt.has_component!Food, "component was not properly added"); 87 assert(nt.get_component!Food == food, "component cannot be retrieved"); 88 nt.remove_component!Food(); 89 assert(!nt.has_component!Food, "component cannot be removed"); 90 nt.destroy(); 91 assert(!nt.alive); 92 } 93 94 @("ecs-destroy") 95 unittest { 96 static class Thing1 : Component { 97 } 98 99 static class Thing2 : Component { 100 } 101 102 auto ecs = new EntityManager(); 103 auto nt1 = ecs.create_entity(); 104 nt1.add_component!Thing1(); 105 auto nt2 = ecs.create_entity(); 106 nt2.add_component!Thing2(); 107 108 ecs.destroy(); 109 } 110 111 @("ecs-test1") 112 unittest { 113 class Butter : Component { 114 public bool tasty = true; 115 } 116 117 class Jelly : Component { 118 public int rank = 4; 119 } 120 121 auto ecs = new EntityManager(); 122 auto sandwich1 = ecs.create_entity(); 123 auto sandwich2 = ecs.create_entity(); 124 auto sandwich3 = ecs.create_entity(); 125 126 sandwich1.add_component(new Butter()); 127 sandwich1.add_component(new Jelly()); 128 assert(sandwich1.has_component!Butter); 129 assert(sandwich1.has_component!Jelly); 130 131 sandwich2.add_component(new Butter()); 132 assert(sandwich2.has_component!Butter); 133 134 sandwich3.add_component(new Jelly()); 135 assert(sandwich3.has_component!Jelly); 136 137 sandwich1.remove_component!Butter; 138 139 // make sure everything else is still good 140 enum msg = "component storage is unstable"; 141 assert(!sandwich1.has_component!Butter, msg); 142 assert(sandwich1.has_component!Jelly, msg); 143 assert(sandwich2.has_component!Butter, msg); 144 assert(sandwich3.has_component!Jelly, msg); 145 146 ecs.destroy(); 147 } 148 149 @("ecs-test2") 150 unittest { 151 import re.ecs : Renderable, Updatable; 152 153 static class Brush : Component, Renderable { 154 void render() { 155 } 156 157 void debug_render() { 158 } 159 } 160 161 static class Control : Component { 162 } 163 164 static class Paint : Component, Updatable { 165 void update() { 166 } 167 } 168 169 auto ecs = new EntityManager(); 170 171 auto a1 = ecs.create_entity(); 172 a1.add_component!Brush(); // R 173 a1.add_component!Control(); // B 174 a1.add_component!Paint(); // U 175 176 ecs.destroy(); 177 }