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