1 module prova.core.scene; 2 3 import derelict.openal.al, 4 prova, 5 std.algorithm, 6 std.math; 7 8 /// 9 class Scene 10 { 11 /// 12 public Camera camera; 13 package LinkedList!(Audio) audioSources; 14 package SpacialMap2D collider2DMap; 15 package Game _game; 16 package bool isSetup; 17 private LinkedList!(Entity) entities; 18 19 /// 20 this() 21 { 22 camera = new Camera(); 23 collider2DMap = new SpacialMap2D(); 24 entities = new LinkedList!(Entity); 25 audioSources = new LinkedList!(Audio); 26 } 27 28 /// 29 final @property Game game() 30 { 31 if(!_game) 32 throw new Exception("Scene is not attached to a game"); 33 return _game; 34 } 35 36 /// 37 final @property Input input() 38 { 39 return game.input; 40 } 41 42 /// 43 final Entity[] getEntities() 44 { 45 Entity[] clonedArray; 46 clonedArray.length = entities.length; 47 int i = 0; 48 49 foreach(Node!Entity node; entities) 50 clonedArray[i++] = node.getValue(); 51 52 return clonedArray; 53 } 54 55 /// 56 final void addEntity(Entity entity) 57 { 58 entities.insertBack(entity); 59 entity._scene = this; 60 61 foreach(Audio source; entity.audioSources) 62 audioSources.insertBack(source); 63 64 collider2DMap.add(entity.colliders2d); 65 66 if(!entity.isSetup) { 67 entity.setup(); 68 entity.isSetup = true; 69 } 70 71 entity.start(); 72 } 73 74 /// 75 final void removeEntity(Entity entity) 76 { 77 entities.remove(entity); 78 79 if(entity._scene == this) 80 entity._scene = null; 81 82 foreach(Audio source; entity.audioSources) 83 audioSources.remove(source); 84 85 collider2DMap.remove(entity.colliders2d); 86 } 87 88 /// Finds the closest entity to this entity 89 Entity findClosestEntity(Entity entity) 90 { 91 return findClosestEntity(entity, 0, false); 92 } 93 94 /// Finds the closest entity with the matching tag 95 Entity findClosestEntity(Entity entity, int tag) 96 { 97 return findClosestEntity(entity, tag, true); 98 } 99 100 private Entity findClosestEntity(Entity entity, int tag, bool needsTag) 101 { 102 float closestDistance = -1; 103 Entity closestEntity = null; 104 105 foreach(Entity other; entities) 106 { 107 // make sure we aren't matching with self 108 if(other == entity) 109 continue; 110 111 // make sure tag matches 112 if(needsTag && !other.hasTag(tag)) 113 continue; 114 115 const float distance = other.position.distanceTo(entity.position); 116 117 if(distance < closestDistance || closestDistance == -1) { 118 closestDistance = distance; 119 closestEntity = other; 120 } 121 } 122 123 return closestEntity; 124 } 125 126 /// Called when attached to a scene for the first time 127 void setup() { } 128 /// Called when attached to a scene 129 void start() { } 130 131 /// Call super.update() to update entities if overridden 132 void update() 133 { 134 updateEntities(); 135 updateCollisions(); 136 } 137 138 /// Called by Scene.update() 139 void updateEntities() 140 { 141 foreach(Entity entity; entities) { 142 entity.update(); 143 entity.position += entity.velocity; 144 entity.velocity *= 1 - entity.friction; 145 } 146 } 147 148 /// Called by Scene.update() 149 void updateCollisions() 150 { 151 collider2DMap.mapColliders(); 152 collider2DMap.markCollisions(); 153 collider2DMap.resolveCollisions(); 154 } 155 156 package void updateAudio() 157 { 158 Vector3 position = camera.position / Audio.scale; 159 Vector3[] orientation = [ 160 camera.rotation * Vector3(0, 0, -1), // forward 161 camera.rotation * Vector3(0, 1, 0) // up 162 ]; 163 164 alListener3f(AL_POSITION, position.x, position.y, position.z); 165 alListenerfv(AL_ORIENTATION, cast(float*) orientation.ptr); 166 167 foreach(Audio source; audioSources) 168 source.update(); 169 } 170 171 /** 172 * All draw operations performed here will be affected by the camera 173 * 174 * Call super.draw(renderTarget) to render entities if overridden 175 */ 176 void draw(RenderTarget renderTarget) 177 { 178 Entity[][float] distanceMappedEntities; 179 180 foreach(Entity entity; entities) 181 { 182 float distance; 183 184 if(camera.sortingMethod == SortingMethod.Distance) 185 distance = entity.position.distanceTo(camera.position); 186 else 187 distance = camera.position.z - entity.position.z; 188 189 distanceMappedEntities[distance] ~= entity; 190 } 191 192 foreach_reverse(float key; sort(distanceMappedEntities.keys)) 193 foreach(Entity entity; distanceMappedEntities[key]) 194 entity.draw(renderTarget, entity.getLocalTransformMatrix()); 195 } 196 197 /** 198 * All draw operations here will be affected by a static orthographic perspective 199 * 200 * Origin is moved to the bottom left of the window 201 */ 202 void drawStatic(RenderTarget renderTarget) { } 203 }