1 module main; 2 3 import std.stdio; 4 import Engine.all; 5 import std.conv; 6 import std.random; 7 import Engine.Material; 8 import Engine.UI.all; 9 import std.math; 10 import std.variant; 11 import Engine.math; 12 import Engine.Systems.SimpleSystem; 13 import Engine.Tree.BBTree; 14 15 Shader shader; 16 Camera camera; 17 Texture ballTexture; 18 19 import std.parallelism; 20 import std.datetime; 21 import Engine.util; 22 23 class CollisionSystem : System { 24 BBTree tree; 25 26 @property override Timing timing() { 27 return Timing.Update; 28 } 29 30 31 override void start() { 32 tree = new BBTree(null); 33 } 34 35 override void process() { 36 tree.ReindexQuery(&checkCollision); 37 } 38 39 ulong checkCollision(Indexable a,Indexable b, ulong collisionID) { 40 auto ac = cast(Collider)a; 41 auto bc = cast(Collider)b; 42 if (ac.Collide(bc)) { 43 ac.entity.SendMessage("OnCollision", bc); 44 bc.entity.SendMessage("OnCollision", ac); 45 } 46 return 0; 47 } 48 49 override void onEntityEnter(Entity e) { 50 foreach(c2 ; e.Components) { 51 auto c = cast(Component)c2; 52 auto collider = c.Cast!Collider(); 53 if (collider !is null) { 54 tree.Insert(collider); 55 } 56 } 57 } 58 59 override void onEntityLeave(Entity e) { 60 61 } 62 } 63 64 65 struct CollisionData { 66 public: 67 vec2 point; 68 vec2 normal; 69 Entity a; 70 Entity b; 71 } 72 73 class Collider : Indexable { 74 mixin ComponentBase; 75 enum Type { 76 Box, 77 Circle, 78 } 79 80 abstract @property Type type(); 81 rect BB() { 82 return rect(); 83 } 84 abstract bool Collide(Collider collider); 85 } 86 87 class BoxCollider : Collider { 88 rect bb; 89 90 void Awake() { 91 bb = rect(transform.scale.x,transform.scale.y); 92 } 93 94 override @property Type type() { 95 return Type.Box; 96 } 97 98 public override rect BB() { 99 auto bc = bb; 100 bc.add(transform.position.xy); 101 return bc; 102 } 103 104 public override bool Collide(Collider collider) { 105 if (collider.type() == Type.Box) { 106 return BB.Intersects(BB(),collider.BB()); 107 } else { 108 assert(0); 109 } 110 //return false; 111 } 112 } 113 114 class CircleCollider : Collider { 115 float radius; 116 117 void Update() { 118 if (transform.scale.x > transform.scale.y) 119 radius = transform.scale.x/2f; 120 else 121 radius = transform.scale.y/2f; 122 } 123 124 override @property Type type() { 125 return Type.Circle; 126 } 127 128 public override rect BB() { 129 auto bc = rect(vec2(-radius,-radius),vec2(radius,radius)); 130 bc.add(transform.position.xy); 131 return bc; 132 } 133 134 public override bool Collide(Collider collider) { 135 if (collider.type() == Type.Circle) { 136 auto c = cast(CircleCollider)collider; 137 auto dp = (c.transform.position.xy-transform.position.xy); 138 auto d = ((c.radius+radius)*(c.radius+radius)) - (dp.x*dp.x + dp.y*dp.y); 139 if (d >= 0) { 140 return true; 141 } 142 } else { 143 assert(0); 144 } 145 return false; 146 } 147 } 148 149 class LifeController { 150 mixin ComponentBase; 151 152 Life life; 153 154 void Awake() { 155 life = entity.GetComponent!Life(); 156 writeln(life is null); 157 } 158 159 void Update() { 160 if (Input.KeyPressDown(Key.MOUSE_BUTTON_1)) { 161 auto mpos = vec3(Core.camera.MouseWorldPosition(),0); 162 auto dir = (transform.position - mpos).xy; 163 dir.normalize(); 164 life.Push(dir.xy, 100f); 165 } 166 } 167 } 168 169 class Rigidbody { 170 mixin ComponentBase; 171 vec2 velocity = vec2(0,0); 172 173 void Update() { 174 auto pos = transform.position; 175 auto bounds = camera.bounds(); 176 auto min = bounds.min; 177 auto max = bounds.max; 178 if (pos.x < min.x) { 179 velocity.x = -velocity.x; 180 pos.x = min.x; 181 } else if (pos.x > max.x) { 182 velocity.x = -velocity.x; 183 pos.x = max.x; 184 } 185 if (pos.y < min.y) { 186 velocity.y = -velocity.y; 187 pos.y = min.y; 188 } else if (pos.y > max.y) { 189 velocity.y = -velocity.y; 190 pos.y = max.y; 191 } 192 transform.position = pos; 193 transform.position += vec3(velocity,0) * Core.DeltaTime; 194 } 195 } 196 197 class Life { 198 mixin ComponentBase; 199 Energy energy; 200 Rigidbody rigidbody; 201 Collider collider; 202 203 void Awake() { 204 energy = entity.GetComponent!Energy(); 205 rigidbody = entity.GetComponent!Rigidbody(); 206 collider = entity.GetComponent!Collider(); 207 } 208 209 void Push(vec2 dir, float force) { 210 auto e = energy.energy*0.05f; 211 energy.AddEnergy(-e); 212 213 auto ball = new Entity(); 214 ball.AddComponent!(Sprite)(ballTexture); 215 auto en = ball.AddComponent!(Energy)(e); 216 ball.AddComponent!(CircleCollider)(); 217 auto rigid = ball.AddComponent!(Rigidbody)(); 218 ball.transform.position = transform.position + ((energy.Size()/2f)+en.Size()/2f) * vec3(-dir,0); 219 ball.sprite.color = vec4(1,0,0,1); 220 ball.AddComponent!(Life)(); 221 Core.AddEntity(ball); 222 223 rigid.velocity = -dir * force; 224 rigidbody.velocity += dir * (force/10f); 225 226 } 227 228 void OnCollision(Collider c) { 229 auto e = c.entity.GetComponent!Energy(); 230 if (e) { 231 if (energy.Size() > e.Size()) { 232 if (e.energy > 0) { 233 auto cc = cast(CircleCollider)c; 234 auto cc2 = cast(CircleCollider)collider; 235 236 //Calculate circle collision distance 237 auto dp = (cc.transform.position.xy-transform.position.xy); 238 auto d = ((cc.radius+cc2.radius)*(cc.radius+cc2.radius)) - (dp.x*dp.x + dp.y*dp.y); 239 auto amount = sqrt(d) * Core.DeltaTime * 5f; 240 241 //move the target circle 242 c.transform.position += vec3(dp.normalized*amount,0); 243 244 //exchange sizes 245 e.AddSize(-amount); 246 energy.AddSize(amount); 247 } 248 } 249 } 250 } 251 } 252 253 class Energy { 254 mixin ComponentBase; 255 float energy; 256 float sizeRatio = 1; 257 258 this(float energy, float ratio = 1.5f) { 259 this.energy = energy; 260 this.sizeRatio = ratio; 261 } 262 263 void Awake() { 264 SetEnergy(energy); 265 } 266 267 void AddSize(float size) { 268 AddEnergy(size / sizeRatio); 269 } 270 271 void SetSize(float size) { 272 SetEnergy(size / sizeRatio); 273 } 274 275 void AddEnergy(float energy) { 276 SetEnergy(this.energy + energy); 277 } 278 279 void SetEnergy(float energy) { 280 this.energy = energy; 281 if (this.energy <= 0) { 282 this.energy = 0; 283 this.entity.Destory(); 284 } 285 auto size = Size(); 286 transform.scale = vec3(size,size,size); 287 } 288 289 void SetSizeRatio(float sizeRatio) { 290 this.sizeRatio = sizeRatio; 291 auto size = Size(); 292 transform.scale = vec3(size,size,size); 293 } 294 295 float Size() { 296 return energy * sizeRatio; 297 } 298 } 299 300 struct InputHandle { 301 void Update() { 302 if (Input.MouseScroll().y > 0) { 303 Core.camera.size += 3*Core.DeltaTime; 304 Core.camera.UpdateResolution(); 305 } 306 if (Input.MouseScroll().y < 0) { 307 Core.camera.size -= 3*Core.DeltaTime; 308 Core.camera.UpdateResolution(); 309 } 310 } 311 } 312 313 void main(string[] args) { 314 try { 315 run(); 316 } 317 catch (Exception e) { 318 writeln(e.msg); 319 scanf("\n"); 320 } 321 } 322 323 void run() { 324 Core.Start(); 325 326 Font t = new Font("./public/arial.ttf\0", 32, Font.ASCII); 327 ballTexture = new Texture("./public/sprite2.png\0"); 328 //ballTexture.SetFiltering(GL_LINEAR,GL_LINEAR); 329 330 //Core.AddSystem(new GravitySystem()); 331 Core.AddSystem(new CollisionSystem()); 332 333 auto mmouse = new Entity(); 334 //mmouse.AddComponent!(Sprite)(ballTexture); 335 mmouse.transform.scale = vec3(100, 100, 1); 336 337 float entities = 20; 338 float m = sqrt(entities/(Core.width*Core.height)); 339 for (int x=0;x<Core.width*m;x++) { 340 for (int y=0;y<Core.height*m;y++) { 341 auto ship = new Entity(); 342 ship.AddComponent!(Sprite)(ballTexture); 343 ship.AddComponent!(CircleCollider)(); 344 ship.AddComponent!(Life)(); 345 ship.name = to!string(x) ~ " " ~ to!string(y); 346 ship.transform.position = vec3(x/m,y/m,0); 347 ship.AddComponent!(Energy)(20); 348 Core.AddEntity(ship); 349 } 350 } 351 352 auto cam = new Entity(); 353 camera = cam.AddComponent(new Camera()); 354 cam.AddComponent(new InputHandle()); 355 Core.AddEntity(cam); 356 Core.camera = cam.GetComponent!Camera(); 357 358 cam.transform.position = vec3(Core.width/2,+Core.height/2,0); 359 360 auto player = new Entity(); 361 player.AddComponent!(Sprite)(ballTexture); 362 auto energy = player.AddComponent!(Energy)(60); 363 player.AddComponent!(Life)(); 364 player.AddComponent!(LifeController)(); 365 player.AddComponent!(CircleCollider)(); 366 player.AddComponent!(Rigidbody)(); 367 player.name = "Player"; 368 369 player.transform.position = camera.transform.position; 370 player.sprite.color = vec4(1,0,0,1); 371 Core.AddEntity(player); 372 373 auto e3 = new Entity(); 374 e3.transform.scale.x = 32; 375 e3.transform.scale.y = 32; 376 e3.transform.position = vec3(100,Core.height-50,0); 377 auto fps = e3.AddComponent!Label(t,"FPS"); 378 Core.AddEntity(e3); 379 380 StartCoroutine( { 381 float time = 0; 382 int frames = 0; 383 while (true) { 384 time += Core.DeltaTime; 385 frames++; 386 if (time >= 1) { 387 fps.SetText("FPS: " ~ to!string(frames)); 388 writeln(to!string(frames)); 389 time -= 1; 390 frames = 0; 391 } 392 auto camRect = Core.camera.bounds(); 393 fps.entity.transform.position = vec3((camRect.max.x+camRect.min.x) / 2, camRect.max.y - 50*Core.camera.size,0); 394 fps.entity.transform.scale = vec3(1,1,1) * Core.camera.size * 34; 395 mmouse.transform.position = vec3(Core.camera.MouseWorldPosition(),0); 396 397 Coroutine.yield(); 398 } 399 }); 400 401 Core.Run(); 402 }