1 module prova.core.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 bool isSetup = false; 19 package Scene _scene; 20 package LinkedList!(Entity) children; 21 package LinkedList!(Collider2D) colliders2d; 22 package LinkedList!(AudioSource) audioSources; 23 private Entity _parent; 24 private LinkedList!(int) tags; 25 26 /// 27 this() 28 { 29 children = new LinkedList!(Entity); 30 colliders2d = new LinkedList!(Collider2D); 31 audioSources = new LinkedList!(AudioSource); 32 tags = new LinkedList!(int); 33 } 34 35 /// 36 final @property Scene scene() 37 { 38 if(!_scene) 39 throw new Exception("Entity is not attached to a scene"); 40 return _scene; 41 } 42 43 /// Shortcut for scene.game 44 final @property Game game() 45 { 46 return scene.game; 47 } 48 49 /// 50 final @property Entity parent() 51 { 52 return _parent; 53 } 54 55 package(prova) final @property void parent(Entity parent) 56 { 57 _parent = parent; 58 } 59 60 /// Shortcut for scene.game.input 61 final @property Input input() 62 { 63 return scene.game.input; 64 } 65 66 /// 67 final void addTag(int tag) 68 { 69 tags.insertBack(tag); 70 } 71 72 /// 73 final void removeTag(int tag) 74 { 75 tags.remove(tag); 76 } 77 78 /// 79 final bool hasTag(int tag) 80 { 81 return tags.contains(tag); 82 } 83 84 /// Makes the passed entity a child of this entity 85 final void attach(Entity entity) 86 { 87 if(entity.parent || entity.parent == this) 88 throw new Exception("Entity already attached"); 89 90 if(_scene) 91 _scene.addEntity(entity); 92 93 children.insertBack(entity); 94 entity.parent = this; 95 } 96 97 /// Detach a child entity or the parent entity 98 final void detach(Entity entity) 99 { 100 if(parent == entity) { 101 parent.detach(this); 102 return; 103 } 104 105 if(entity.parent != this) 106 throw new Exception("Entity was not attached"); 107 108 if(_scene && entity._scene) 109 _scene.removeEntity(entity); 110 111 children.remove(entity); 112 entity.parent = null; 113 } 114 115 /// 116 final void attach(Collider2D collider) 117 { 118 if(collider.entity || collider.entity == this) 119 throw new Exception("Collider already attached"); 120 121 if(_scene) 122 _scene.collider2DMap.add(collider); 123 124 colliders2d.insertBack(collider); 125 collider.entity = this; 126 } 127 128 /// 129 final void detach(Collider2D collider) 130 { 131 if(collider.entity != this) 132 throw new Exception("Collider was not attached"); 133 134 if(_scene) 135 _scene.collider2DMap.remove(collider); 136 137 colliders2d.remove(collider); 138 collider.entity = null; 139 } 140 141 /// 142 final void attach(AudioSource source) 143 { 144 if(source.entity || source.entity == this) 145 throw new Exception("AudioSource already attached"); 146 147 if(source.channels == 2) 148 throw new Exception("Source must use a mono format"); 149 150 if(_scene) 151 _scene.audioSources.insertBack(source); 152 153 audioSources.insertBack(source); 154 source.entity = this; 155 } 156 157 /// 158 final void detach(AudioSource source) 159 { 160 if(source.entity != this) 161 throw new Exception("Collider was not attached"); 162 163 if(_scene) 164 _scene.audioSources.remove(source); 165 166 audioSources.remove(source); 167 source.entity = null; 168 } 169 170 /// 171 final Matrix getLocalTransformMatrix() 172 { 173 Matrix transform = Matrix.identity; 174 transform = transform.scale(scale); 175 transform = transform.rotate(rotation); 176 transform = transform.translate(position); 177 178 return transform; 179 } 180 181 /// 182 final Matrix getWorldTransformMatrix() 183 { 184 Matrix transform = getLocalTransformMatrix(); 185 186 if(parent) 187 transform = parent.getWorldTransformMatrix() * transform; 188 189 return transform; 190 } 191 192 /// 193 final Vector3 getWorldPosition() 194 { 195 Matrix transform = getWorldTransformMatrix(); 196 197 return transform.transpose() * Vector3(); 198 } 199 200 /// 201 final Quaternion getWorldRotation() 202 { 203 Quaternion worldRotation; 204 205 if(parent) 206 worldRotation = parent.getWorldRotation(); 207 208 return rotation * worldRotation; 209 } 210 211 /// 212 final Vector3 getWorldScale() 213 { 214 Vector3 worldScale = Vector3(1, 1, 1); 215 216 if(parent) 217 worldScale = parent.getWorldScale(); 218 219 return scale * worldScale; 220 } 221 222 /// Called every draw tick (skipped if update loop is behind) 223 void draw(RenderTarget renderTarget, Matrix transform) 224 { 225 foreach(Entity child; children) 226 child.draw(renderTarget, transform * child.getLocalTransformMatrix()); 227 } 228 229 /// Called when first attached to a scene 230 void setup(){} 231 /// Called when attached to a scene 232 void start(){} 233 /// Called every update tick 234 void update(){} 235 236 /// 237 void onCollisionEnter2D(Collider2D collider, Collider2D other){} 238 /// 239 void onCollision2D(Collider2D collider, Collider2D other){} 240 /// 241 void onCollisionExit2D(Collider2D collider, Collider2D other){} 242 }