1 module prova.entities.entity; 2 3 import prova; 4 5 /// 6 class Entity 7 { 8 /// 9 Vector3 position; 10 /// 11 Quaternion rotation; 12 /// 13 Vector3 scale = Vector3(1, 1, 1); 14 /// 15 Vector3 velocity; 16 /// Velocity is multiplied by (1 - friction) every tick 17 float friction = 0; 18 package(prova) bool isSetup = false; 19 package(prova) Scene _scene; 20 package(prova) Entity[] children; 21 package(prova) Collider2D[] colliders2d; 22 package(prova) AudioSource[] audioSources; 23 private Renderable[] renderables; 24 private Entity _parent; 25 private int[] tags; 26 27 /// 28 final @property Scene scene() 29 { 30 assert(_scene, "Entity should be attached to a scene"); 31 32 return _scene; 33 } 34 35 /// Shortcut for scene.game 36 final @property Game game() 37 { 38 return scene.game; 39 } 40 41 /// 42 final @property Entity parent() 43 { 44 return _parent; 45 } 46 47 package(prova) final @property void parent(Entity parent) 48 { 49 _parent = parent; 50 } 51 52 /// Shortcut for scene.game.input 53 final @property Input input() 54 { 55 return scene.game.input; 56 } 57 58 /// 59 final void addTag(int tag) 60 { 61 tags ~= tag; 62 } 63 64 /// 65 final void removeTag(int tag) 66 { 67 tags = tags.removeElement(tag); 68 } 69 70 /// 71 final bool hasTag(int tag) 72 { 73 import std.algorithm : canFind; 74 75 return tags.canFind(tag); 76 } 77 78 /// Makes the passed entity a child of this entity 79 final void attach(Entity entity) 80 { 81 assert(!entity.parent, "Entity should not already be attached to an entity"); 82 83 // marked as parent before Scene.addEntity, scene needs to find 84 // entities without parents for it's root entities list 85 entity.parent = this; 86 children ~= entity; 87 88 if(_scene) 89 _scene.addEntity(entity); 90 } 91 92 /// Detach a child entity or the parent entity 93 final void detach(Entity entity) 94 { 95 if(parent == entity) { 96 parent.detach(this); 97 return; 98 } 99 100 assert(entity.parent == this, "Entity should be attached to this entity"); 101 102 if(_scene && entity._scene) 103 _scene.removeEntity(entity); 104 105 // abandon child after Scene.removeEntity, so that the scene 106 // knows that the entity removed is not a root entity 107 entity.parent = null; 108 children = children.removeElement(entity); 109 } 110 111 /// 112 final void attach(Collider2D collider) 113 { 114 assert(!collider.entity, "Collider should not already be attached to an entity"); 115 116 if(_scene) 117 _scene.collider2DMap.add(collider); 118 119 colliders2d ~= collider; 120 collider.entity = this; 121 } 122 123 /// 124 final void detach(Collider2D collider) 125 { 126 assert(collider.entity == this, "Collider should be attached to this entity"); 127 128 if(_scene) 129 _scene.collider2DMap.remove(collider); 130 131 colliders2d = colliders2d.removeElement(collider); 132 collider.entity = null; 133 } 134 135 /// 136 final void attach(AudioSource source) 137 { 138 assert(!source.entity, "AudioSource should not already be attached to an entity"); 139 140 assert(source.channels == 1, "Source must use a mono format"); 141 142 if(_scene) 143 _scene.audioSources ~= source; 144 145 audioSources ~= source; 146 source.entity = this; 147 } 148 149 /// 150 final void detach(AudioSource source) 151 { 152 assert(source.entity == this, "AudioSource should be attached to this entity"); 153 154 if(_scene) 155 _scene.audioSources = _scene.audioSources.removeElement(source); 156 157 audioSources = audioSources.removeElement(source); 158 source.entity = null; 159 } 160 161 /// 162 final void attach(Renderable renderable) 163 { 164 renderables ~= renderable; 165 } 166 167 /// 168 final void detach(Renderable renderable) 169 { 170 import std.algorithm : canFind; 171 172 assert(renderables.canFind(renderable), "Renderable should be attached to this entity"); 173 174 renderables = renderables.removeElement(renderable); 175 } 176 177 /// 178 final void lookAt(Entity entity) 179 { 180 lookAt(entity.getWorldPosition()); 181 } 182 183 /// 184 final void lookAt(Vector3 position) 185 { 186 Vector3 difference = position - getWorldPosition(); 187 rotation = difference.getDirection(); 188 } 189 190 /// 191 final Matrix getLocalTransformMatrix() 192 { 193 Matrix transform = Matrix.identity; 194 transform = transform.scale(scale); 195 transform = transform.rotate(rotation); 196 transform = transform.translate(position); 197 198 return transform; 199 } 200 201 /// 202 final Matrix getWorldTransformMatrix() 203 { 204 Matrix transform = getLocalTransformMatrix(); 205 206 if(parent) 207 transform = parent.getWorldTransformMatrix() * transform; 208 209 return transform; 210 } 211 212 /// 213 final Vector3 getWorldPosition() 214 { 215 Matrix transform = getWorldTransformMatrix(); 216 217 return transform * Vector3(); 218 } 219 220 /// 221 final Quaternion getWorldRotation() 222 { 223 Quaternion worldRotation; 224 225 if(parent) 226 worldRotation = parent.getWorldRotation(); 227 228 return rotation * worldRotation; 229 } 230 231 /// 232 final Vector3 getWorldScale() 233 { 234 Vector3 worldScale = Vector3(1, 1, 1); 235 236 if(parent) 237 worldScale = parent.getWorldScale(); 238 239 return scale * worldScale; 240 } 241 242 /// 243 final Vector2 getScreenPosition() 244 { 245 return scene.camera.getScreenPosition(position); 246 } 247 248 /// Called every draw tick (skipped if update loop is behind) 249 void draw(RenderTarget renderTarget, Matrix transform) 250 { 251 import std.algorithm : sort; 252 253 const auto sortDelegate = scene.camera.getSortDelegate(); 254 auto sortedChildren = children.sort!(sortDelegate); 255 256 foreach(Entity child; sortedChildren) 257 child.draw(renderTarget, transform * child.getLocalTransformMatrix()); 258 259 foreach(Renderable renderable; renderables) 260 renderable.draw(renderTarget, transform); 261 } 262 263 /// Called when first attached to a scene 264 void setup(){} 265 /// Called when attached to a scene 266 void start(){} 267 /// Called every update tick 268 void update(){} 269 270 /// 271 void onCollisionEnter2D(Collider2D collider, Collider2D other){} 272 /// 273 void onCollision2D(Collider2D collider, Collider2D other){} 274 /// 275 void onCollisionExit2D(Collider2D collider, Collider2D other){} 276 }