1 module demos.snake; 2 3 import app; 4 5 import bindbc.sdl; 6 7 import bubel.ecs.attributes; 8 import bubel.ecs.core; 9 import bubel.ecs.entity; 10 import bubel.ecs.manager; 11 import bubel.ecs.std; 12 import bubel.ecs.vector; 13 14 import cimgui.cimgui; 15 16 import ecs_utils.gfx.texture; 17 import ecs_utils.math.vector; 18 import ecs_utils.utils; 19 20 import game_core.basic; 21 22 import gui.attributes; 23 //import std.array : staticArray; 24 25 enum float px = 1.0/512.0; 26 27 extern(C): 28 29 //Map is simple grid. Every cell has type and id to entity. 30 struct MapElement 31 { 32 enum Type 33 { 34 empty = 0, 35 apple = 1, 36 wall = 2, 37 snake = 3, 38 } 39 Type type; 40 EntityID id; 41 } 42 43 //snake part is corresponding to graphical representation of snake 44 enum SnakePart : ubyte 45 { 46 head_up = 0, 47 head_down = 1, 48 head_left = 2, 49 head_right = 3, 50 tail_up = 4, 51 tail_down = 5, 52 tail_left = 6, 53 tail_right = 7, 54 turn_ld = 8, 55 turn_lu = 9, 56 turn_rd = 10, 57 turn_ru = 11, 58 vertical = 12, 59 horizontal = 13 60 } 61 62 struct Snake 63 { 64 __gshared const (char)* tips = "Use \"WASD\" keys to move. If you loose you can always spawn new snake... or several snakes. 65 This demo is an example that in ECS you can make very \"non-ECS\" game"; 66 67 EntityTemplate* apple_tmpl; 68 EntityTemplate* snake_tmpl; 69 EntityTemplate* snake_destroy_particle; 70 Texture texture; 71 72 vec4[] snake_destroy_particle_frames; 73 vec4[] smoke_frames; 74 75 76 bool move_system = true; 77 bool draw_system = true; 78 79 enum int map_size = 18; 80 81 MapElement[map_size * map_size] map; 82 83 ~this() @nogc nothrow 84 { 85 if(snake_destroy_particle_frames)Mallocator.dispose(snake_destroy_particle_frames); 86 if(smoke_frames)Mallocator.dispose(smoke_frames); 87 if(apple_tmpl)gEntityManager.freeTemplate(apple_tmpl); 88 if(snake_tmpl)gEntityManager.freeTemplate(snake_tmpl); 89 if(snake_destroy_particle)gEntityManager.freeTemplate(snake_destroy_particle); 90 texture.destroy(); 91 } 92 93 MapElement element(ivec2 pos) 94 { 95 uint index = pos.x + pos.y * map_size; 96 if(index >= map.length)index = map.length - 1; 97 return map[index]; 98 } 99 100 void element(MapElement el, ivec2 pos) 101 { 102 uint index = pos.x + pos.y * map_size; 103 if(index >= map.length)index = map.length - 1; 104 map[index] = el; 105 } 106 107 void addApple() 108 { 109 ivec2 random_pos = ivec2(rand()%map_size,rand()%map_size); 110 ivec2 base_pos = random_pos; 111 while(element(random_pos).type != MapElement.Type.empty) 112 { 113 random_pos.x += 1; 114 if(random_pos.x > map_size) 115 { 116 random_pos.x = 0; 117 random_pos.y += 1; 118 if(random_pos.y > map_size)random_pos.y = 0; 119 } 120 if(base_pos.x == random_pos.x && base_pos.y == random_pos.y)return; 121 } 122 gEntityManager.addEntity(apple_tmpl,[CLocation(cast(vec2)(random_pos)*16).ref_].staticArray); 123 } 124 } 125 126 struct Animation 127 { 128 129 } 130 131 //component has array of frames (texture coordinates) and time used for selection frames 132 struct CAnimation 133 { 134 mixin ECS.Component; 135 136 vec4[] frames; 137 @GUIRangeF(0,float.max)float time = 0; 138 } 139 140 //CIlocation is integer location used as grid cell coordination 141 struct CILocation 142 { 143 mixin ECS.Component; 144 145 alias location this; 146 147 ivec2 location; 148 } 149 150 // struct CLocation 151 // { 152 // mixin ECS.Component; 153 154 // alias location this; 155 156 // vec2 location = vec2(0,0); 157 // } 158 159 struct CSnake 160 { 161 void onCreate() 162 { 163 parts.array = Mallocator.makeArray!ivec2(100); 164 } 165 166 void onDestroy() 167 { 168 Mallocator.dispose(parts.array); 169 } 170 171 mixin ECS.Component; 172 173 174 struct Parts 175 { 176 uint length = 0; 177 ivec2[] array; 178 179 ivec2 opIndex(size_t ind) const 180 { 181 return array[ind]; 182 } 183 184 ivec2[] opSlice() 185 { 186 return array[0 .. length]; 187 } 188 189 void opIndexAssign(ivec2 vec, size_t ind) 190 { 191 array[ind] = vec; 192 } 193 194 size_t opDollar() const 195 { 196 return length; 197 } 198 199 void add(ivec2 v) 200 { 201 length++; 202 array[length-1] = v; 203 } 204 } 205 206 Parts parts; 207 @GUIRange(0,3)CMovement.Direction direction; 208 } 209 210 //flag for apple 211 struct CApple 212 { 213 mixin ECS.Component; 214 } 215 216 //particle is removed when life drops below 0 217 struct CParticle 218 { 219 mixin ECS.Component; 220 221 float life = 0; 222 } 223 224 //vector for particle movement 225 struct CParticleVector 226 { 227 mixin ECS.Component; 228 229 vec2 velocity = vec2(0,0); 230 } 231 232 //contains current movement direction for snake 233 struct CMovement 234 { 235 mixin ECS.Component; 236 237 enum Direction : byte 238 { 239 up, 240 down, 241 left, 242 right 243 } 244 245 @GUIRange(0,3)Direction direction; 246 } 247 248 // struct CInput 249 // { 250 // mixin ECS.Component; 251 // } 252 253 //this system has no onUpdate and only processing events. It responsible for adding and removing applce from grid. 254 struct AppleSystem 255 { 256 mixin ECS.System!1; 257 258 struct EntitiesData 259 { 260 uint length; 261 @readonly Entity[] entity; 262 @readonly CApple[] apple; 263 @readonly CILocation[] location; 264 } 265 266 //called when entity was created 267 void onAddEntity(EntitiesData data) 268 { 269 foreach(i;0..data.length) 270 { 271 if(snake.element(data.location[i]).id == EntityID())snake.element(MapElement(MapElement.Type.apple,data.entity[i].id),data.location[i]); 272 else gEntityManager.removeEntity(data.entity[i].id); 273 } 274 } 275 276 //called when entity was removed 277 void onRemoveEntity(EntitiesData data) 278 { 279 foreach(i;0..data.length) 280 { 281 if(snake.element(data.location[i].location).id == data.entity[i].id) 282 snake.element(MapElement(MapElement.Type.empty, EntityID()),data.location[i].location); 283 } 284 } 285 } 286 287 //system is responsible for killing particles when their life drops below 0 288 struct ParticleSystem 289 { 290 mixin ECS.System!1; 291 292 struct EntitiesData 293 { 294 uint length; 295 @readonly Entity[] entities; 296 CParticle[] particle; 297 } 298 299 void onUpdate(EntitiesData data) 300 { 301 foreach(i;0..data.length) 302 { 303 data.particle[i].life -= launcher.deltaTime; 304 if(data.particle[i].life < 0)gEntityManager.removeEntity(data.entities[i].id); 305 } 306 } 307 } 308 309 //system responsible for moving particles 310 struct ParticleMovementSystem 311 { 312 mixin ECS.System!1; 313 314 struct EntitiesData 315 { 316 uint length; 317 @readonly Entity[] entities; 318 @readonly CParticleVector[] movement; 319 CLocation[] location; 320 } 321 322 void onUpdate(EntitiesData data) 323 { 324 foreach(i;0..data.length) 325 { 326 data.location[i] -= data.movement[i].velocity; 327 } 328 } 329 } 330 331 332 struct AnimationSystem 333 { 334 mixin ECS.System!1; 335 336 struct EntitiesData 337 { 338 uint length; 339 CAnimation[] animation; 340 } 341 342 void onUpdate(EntitiesData data) 343 { 344 foreach(i;0..data.length) 345 { 346 data.animation[i].time += launcher.deltaTime * 0.01; 347 while(data.animation[i].time >= data.animation[i].frames.length)data.animation[i].time -= cast(float)data.animation[i].frames.length; 348 } 349 } 350 } 351 352 353 struct AnimationRenderSystem 354 { 355 mixin ECS.System!1; 356 357 struct EntitiesData 358 { 359 uint length; 360 @readonly CAnimation[] animation; 361 @readonly CLocation[] location; 362 } 363 364 void onUpdate(EntitiesData data) 365 { 366 import ecs_utils.gfx.renderer; 367 Renderer.DrawData draw_data; 368 draw_data.size = vec2(16,16); 369 //draw_data.coords = vec4(0,0,1,1)*px; 370 draw_data.color = 0x80808080; 371 draw_data.material_id = 0; 372 draw_data.thread_id = 0; 373 draw_data.texture = snake.texture; 374 draw_data.depth = -1; 375 foreach(i;0..data.length) 376 { 377 uint frame = cast(uint)(data.animation[i].time); 378 if(frame >= data.animation[i].frames.length)frame = cast(uint)data.animation[i].frames.length - 1; 379 draw_data.position = data.location[i]; 380 draw_data.coords = data.animation[i].frames[frame]; 381 launcher.renderer.draw(draw_data); 382 } 383 } 384 } 385 386 struct MoveSystem 387 { 388 mixin ECS.System!64; 389 390 EntityTemplate* destroy_template; 391 392 struct EntitiesData 393 { 394 uint length; 395 @readonly Entity[] entities; 396 @readonly CMovement[] movement; 397 @optional CSnake[] snakes; 398 CILocation[] location; 399 } 400 401 void setTemplates() 402 { 403 //template is used for adding particles when snake will collide with himself 404 destroy_template = snake.snake_destroy_particle; 405 } 406 407 void moveLocation(ref CILocation location, CMovement.Direction direction) 408 { 409 final switch(direction) 410 { 411 case CMovement.Direction.down: 412 location.y -= 1; 413 if(location.y < 0)location.y = snake.map_size - 1; 414 break; 415 case CMovement.Direction.up: 416 location.y += 1; 417 if(location.y >= snake.map_size)location.y = 0; 418 break; 419 case CMovement.Direction.left: 420 location.x -= 1; 421 if(location.x < 0)location.x = snake.map_size - 1; 422 break; 423 case CMovement.Direction.right: 424 location.x += 1; 425 if(location.x >= snake.map_size)location.x = 0; 426 break; 427 } 428 } 429 430 void moveSnake(ref CSnake snake, ivec2 location) 431 { 432 if(snake.parts.length) 433 { 434 .snake.element(MapElement(),snake.parts[0]); 435 foreach(j; 0 .. snake.parts.length - 1) 436 { 437 snake.parts[j] = snake.parts[j + 1]; 438 } 439 snake.parts[$-1] = location; 440 } 441 else .snake.element(MapElement(),location); 442 } 443 444 void onUpdate(EntitiesData data) 445 { 446 if(data.snakes) 447 { 448 foreach(i; 0..data.length) 449 { 450 data.snakes[i].direction = data.movement[i].direction; 451 ivec2 new_location = data.location[i]; 452 moveLocation(data.location[i], data.movement[i].direction); 453 final switch(snake.element(data.location[i].location).type) 454 { 455 case MapElement.Type.snake: 456 foreach(loc; data.snakes[i].parts) 457 { 458 //destroy_location.x = loc.x * 16; 459 //destroy_location.y = loc.y * 16; 460 snake.element(MapElement(MapElement.Type.empty, EntityID()),loc); 461 gEntityManager.addEntity(snake.snake_destroy_particle,[CLocation(cast(vec2)(loc * 16)).ref_].staticArray); 462 463 CLocation destroy_location; 464 foreach(j;0..10) 465 { 466 destroy_location.x = loc.x * 16 + randomf() * 8 - 4; 467 destroy_location.y = loc.y * 16 + randomf() * 8 - 4; 468 //destroy_vector.velocity = vec2(randomf(),randomf())*0.4-0.2; 469 snake.element(MapElement(MapElement.Type.empty, EntityID()),loc); 470 gEntityManager.addEntity(snake.snake_destroy_particle, [destroy_location.ref_, CParticleVector(vec2(randomf(),randomf())*0.4-0.2).ref_].staticArray); 471 } 472 473 } 474 //destroy_location.x = new_location.x * 16; 475 //destroy_location.y = new_location.y * 16; 476 snake.element(MapElement(MapElement.Type.empty, EntityID()),new_location); 477 gEntityManager.addEntity(snake.snake_destroy_particle,[CLocation(cast(vec2)(new_location * 16)).ref_].staticArray); 478 gEntityManager.removeEntity(data.entities[i].id); 479 break; 480 481 case MapElement.Type.wall:break; 482 483 case MapElement.Type.empty: 484 moveSnake(data.snakes[i], new_location); 485 snake.element(MapElement(MapElement.Type.snake, data.entities[i].id),data.location[i].location); 486 if(data.snakes[i].parts.length > 1) 487 { 488 snake.element(MapElement(MapElement.Type.snake, data.entities[i].id),data.snakes[i].parts[$-1]); 489 snake.element(MapElement(MapElement.Type.snake, data.entities[i].id),data.snakes[i].parts[0]); 490 } 491 else if(data.snakes[i].parts.length == 1) 492 { 493 snake.element(MapElement(MapElement.Type.snake, data.entities[i].id),data.snakes[i].parts[0]); 494 } 495 break; 496 case MapElement.Type.apple: 497 gEntityManager.removeEntity(snake.element(data.location[i].location).id); 498 if(data.snakes[i].parts.length >= 99) 499 { 500 snake.addApple(); 501 goto case(MapElement.Type.empty); 502 } 503 data.snakes[i].parts.add(new_location); 504 505 if(data.snakes[i].parts.length > 1) 506 { 507 snake.element(MapElement(MapElement.Type.snake, data.entities[i].id),data.snakes[i].parts[$-1]); 508 } 509 else if(data.snakes[i].parts.length == 1) 510 { 511 snake.element(MapElement(MapElement.Type.snake, data.entities[i].id),new_location); 512 } 513 snake.element(MapElement(MapElement.Type.snake, data.entities[i].id),data.location[i].location); 514 snake.addApple(); 515 break; 516 } 517 } 518 } 519 else 520 { 521 foreach(i; 0..data.length) 522 { 523 final switch(data.movement[i].direction) 524 { 525 case CMovement.Direction.down:data.location[i].y -= 1;break; 526 case CMovement.Direction.up:data.location[i].y += 1;break; 527 case CMovement.Direction.left:data.location[i].x -= 1;break; 528 case CMovement.Direction.right:data.location[i].x += 1;break; 529 } 530 } 531 } 532 } 533 } 534 535 struct SnakeSystem 536 { 537 mixin ECS.System!1; 538 539 struct EntitiesData 540 { 541 uint length; 542 Entity[] entity; 543 @readonly CSnake[] snake; 544 @readonly CILocation[] location; 545 } 546 547 void onAddSystem(EntitiesData data) 548 { 549 foreach(i;0..data.length) 550 { 551 if(snake.element(data.location[i]).id == EntityID())snake.element(MapElement(MapElement.Type.snake,data.entity[i].id),data.location[i]); 552 else gEntityManager.removeEntity(data.entity[i].id); 553 } 554 } 555 556 void onRemoveEntity(EntitiesData data) 557 { 558 foreach(i;0..data.length) 559 { 560 if(snake.element(data.location[i].location).id == data.entity[i].id) 561 snake.element(MapElement(MapElement.Type.empty, EntityID()),data.location[i].location); 562 foreach(part; data.snake[i].parts.array) 563 if(snake.element(part).id == data.entity[i].id) 564 snake.element(MapElement(MapElement.Type.empty, EntityID()),part); 565 } 566 } 567 } 568 569 struct InputSystem 570 { 571 mixin ECS.System!64; 572 573 struct EntitiesData 574 { 575 uint length; 576 CMovement[] movement; 577 @readonly CInput[] input; 578 } 579 580 void onUpdate(EntitiesData data) 581 { 582 foreach(i; 0..data.length) 583 { 584 if(launcher.getKeyState(SDL_SCANCODE_W)) 585 { 586 data.movement[i].direction = CMovement.Direction.up; 587 } 588 else if(launcher.getKeyState(SDL_SCANCODE_S)) 589 { 590 data.movement[i].direction = CMovement.Direction.down; 591 } 592 else if(launcher.getKeyState(SDL_SCANCODE_A)) 593 { 594 data.movement[i].direction = CMovement.Direction.left; 595 } 596 else if(launcher.getKeyState(SDL_SCANCODE_D)) 597 { 598 data.movement[i].direction = CMovement.Direction.right; 599 } 600 } 601 } 602 } 603 604 struct FixSnakeDirectionSystem 605 { 606 mixin ECS.System!64; 607 608 struct EntitiesData 609 { 610 uint length; 611 CMovement[] movement; 612 @readonly CILocation[] location; 613 const (CSnake)[] snake; 614 } 615 616 void onUpdate(EntitiesData data) 617 { 618 foreach(i; 0..data.length) 619 { 620 ivec2 last_location; 621 if(data.snake[i].parts.length)last_location = data.snake[i].parts[$ - 1]; 622 else continue; 623 ivec2 next_location = data.location[i]; 624 625 final switch(data.movement[i].direction) 626 { 627 case CMovement.Direction.up: 628 next_location.y += 1; 629 if(next_location.y >= snake.map_size)next_location.y = 0; 630 if(next_location.x == last_location.x && next_location.y == last_location.y) 631 { 632 data.movement[i].direction = CMovement.Direction.down; 633 } 634 break; 635 case CMovement.Direction.down: 636 next_location.y -= 1; 637 if(next_location.y < 0)next_location.y = snake.map_size - 1; 638 if(next_location.x == last_location.x && next_location.y == last_location.y) 639 { 640 data.movement[i].direction = CMovement.Direction.up; 641 } 642 break; 643 case CMovement.Direction.left: 644 next_location.x -= 1; 645 if(next_location.x < 0)next_location.x = snake.map_size - 1; 646 if(next_location.x == last_location.x && next_location.y == last_location.y) 647 { 648 data.movement[i].direction = CMovement.Direction.right; 649 } 650 break; 651 case CMovement.Direction.right: 652 next_location.x += 1; 653 if(next_location.x >= snake.map_size)next_location.x = 0; 654 if(next_location.x == last_location.x && next_location.y == last_location.y) 655 { 656 data.movement[i].direction = CMovement.Direction.left; 657 } 658 break; 659 } 660 } 661 } 662 } 663 664 struct DrawAppleSystem 665 { 666 mixin ECS.System!1; 667 668 struct EntitiesData 669 { 670 uint length; 671 @readonly CILocation[] location; 672 const (CApple)[] apple; 673 } 674 675 void onUpdate(EntitiesData data) 676 { 677 import ecs_utils.gfx.renderer; 678 Renderer.DrawData draw_data; 679 draw_data.size = vec2(16,16); 680 draw_data.coords = vec4(0,32*px,16*px,16*px); 681 draw_data.color = 0x80808080; 682 draw_data.material_id = 0; 683 draw_data.thread_id = 0; 684 draw_data.texture = snake.texture; 685 foreach(i; 0..data.location.length) 686 { 687 draw_data.position = vec2(data.location[i].x*16,data.location[i].y*16); 688 launcher.renderer.draw(draw_data); 689 //launcher.renderer.draw(snake.texture, vec2(data.location[i].x*16,data.location[i].y*16), vec2(16,16), vec4(0,32*px,16*px,16*px), 0, 0x80808080, 0); 690 } 691 } 692 } 693 694 struct DrawSnakeSystem 695 { 696 mixin ECS.System!1; 697 698 struct EntitiesData 699 { 700 uint length; 701 @readonly CILocation[] location; 702 const (CSnake)[] snake; 703 } 704 705 static CMovement.Direction getDirection(ivec2 p1, ivec2 p2) 706 { 707 if(p1.x - p2.x == -1)return CMovement.direction.right; 708 else if(p1.x - p2.x == 1)return CMovement.direction.left; 709 else if(p1.y - p2.y == -1)return CMovement.direction.up; 710 else if(p1.y - p2.y == 1)return CMovement.direction.down; 711 else if(p1.x - p2.x > 1)return CMovement.direction.right; 712 else if(p1.x - p2.x < -1)return CMovement.direction.left; 713 else if(p1.y - p2.y > 1)return CMovement.direction.up; 714 else return CMovement.direction.down; 715 } 716 717 static SnakePart snakePart(ivec2 p1, ivec2 p2, ivec2 p3) 718 { 719 CMovement.Direction direction = getDirection(p1, p2); 720 CMovement.Direction direction2 = getDirection(p1, p3); 721 uint case_ = direction*4 + direction2; 722 final switch(case_) 723 { 724 case 0:return SnakePart.horizontal; 725 case 1:return SnakePart.horizontal; 726 case 2:return SnakePart.turn_lu; 727 case 3:return SnakePart.turn_ru; 728 case 4:return SnakePart.horizontal; 729 case 5:return SnakePart.horizontal; 730 case 6:return SnakePart.turn_ld; 731 case 7:return SnakePart.turn_rd; 732 case 8:return SnakePart.turn_lu; 733 case 9:return SnakePart.turn_ld; 734 case 10:return SnakePart.vertical; 735 case 11:return SnakePart.vertical; 736 case 12:return SnakePart.turn_ru; 737 case 13:return SnakePart.turn_rd; 738 case 14:return SnakePart.vertical; 739 case 15:return SnakePart.vertical; 740 } 741 } 742 743 static SnakePart snakeTail(ivec2 p1, ivec2 p2) 744 { 745 CMovement.Direction direction = getDirection(p1, p2); 746 final switch(direction) 747 { 748 case CMovement.Direction.up:return SnakePart.tail_up; 749 case CMovement.Direction.down:return SnakePart.tail_down; 750 case CMovement.Direction.left:return SnakePart.tail_left; 751 case CMovement.Direction.right:return SnakePart.tail_right; 752 } 753 } 754 755 static void drawElement(ivec2 loc, SnakePart part) 756 { 757 import ecs_utils.gfx.renderer; 758 Renderer.DrawData draw_data; 759 draw_data.size = vec2(16,16); 760 draw_data.color = 0x80808080; 761 draw_data.texture = snake.texture; 762 draw_data.position = cast(vec2)loc; 763 final switch(cast(ubyte)part) 764 { 765 case SnakePart.tail_up:draw_data.coords = vec4(16,112,16,16)*px;break; 766 case SnakePart.tail_down:draw_data.coords = vec4(0,112,16,16)*px;break; 767 case SnakePart.tail_left:draw_data.coords = vec4(32,112,16,16)*px;break; 768 case SnakePart.tail_right:draw_data.coords = vec4(0,144,16,16)*px;break; 769 case SnakePart.turn_ld:draw_data.coords = vec4(64,128,16,16)*px;break; 770 case SnakePart.turn_lu:draw_data.coords = vec4(32,144,16,16)*px;break; 771 case SnakePart.turn_rd:draw_data.coords = vec4(16,144,16,16)*px;break; 772 case SnakePart.turn_ru:draw_data.coords = vec4(64,112,16,16)*px;break; 773 case SnakePart.vertical:draw_data.coords = vec4(16,128,16,16)*px;break; 774 case SnakePart.horizontal:draw_data.coords = vec4(48,128,16,16)*px;break; 775 } 776 launcher.renderer.draw(draw_data); 777 } 778 779 void onUpdate(EntitiesData data) 780 { 781 import ecs_utils.gfx.renderer; 782 Renderer.DrawData draw_data; 783 draw_data.size = vec2(16,16); 784 draw_data.color = 0x80808080; 785 draw_data.texture = snake.texture; 786 787 foreach(i; 0..data.length) 788 { 789 const (CSnake)* snake = &data.snake[i]; 790 scope vec2 loc = cast(vec2)(data.location[i].location * 16); 791 draw_data.position = loc; 792 final switch(snake.direction) 793 { 794 case CMovement.Direction.up:draw_data.coords = vec4(48,112,16,16)*px;break; 795 case CMovement.Direction.down:draw_data.coords = vec4(48,144,16,16)*px;break; 796 case CMovement.Direction.left:draw_data.coords = vec4(0,128,16,16)*px;break; 797 case CMovement.Direction.right:draw_data.coords = vec4(32,128,16,16)*px;break; 798 } 799 launcher.renderer.draw(draw_data); 800 if(snake.parts.length >1) 801 { 802 foreach(j;1..snake.parts.length - 1)drawElement(snake.parts[j]*16, snakePart(snake.parts[j], snake.parts[j+1], snake.parts[j-1])); 803 drawElement(snake.parts[$-1]*16, snakePart(snake.parts[$-1], data.location[i], snake.parts[$-2])); 804 drawElement(snake.parts[0]*16, snakeTail(snake.parts[1], snake.parts[0])); 805 } 806 else if(snake.parts.length == 1) 807 { 808 drawElement(snake.parts[0]*16, snakeTail(data.location[i], snake.parts[0])); 809 } 810 811 } 812 } 813 } 814 815 struct CleanSystem 816 { 817 mixin ECS.System!64; 818 819 struct EntitiesData 820 { 821 uint length; 822 Entity[] entities; 823 } 824 825 void onUpdate(EntitiesData data) 826 { 827 foreach(i; 0..data.length) 828 { 829 gEntityManager.removeEntity(data.entities[i].id); 830 } 831 } 832 } 833 834 struct CopyLocationSystem 835 { 836 mixin ECS.System!32; 837 838 struct EntitiesData 839 { 840 uint length; 841 const (Entity)[] entity; 842 CLocation[] location; 843 @readonly CILocation[] ilocation; 844 } 845 846 void onAddEntity(EntitiesData data) 847 { 848 foreach(i;0..data.length) 849 { 850 data.ilocation[i] = cast(ivec2)(data.location[i] / 16); 851 } 852 } 853 854 void onUpdate(EntitiesData data) 855 { 856 foreach(i;0..data.length) 857 { 858 data.location[i] = cast(vec2)(data.ilocation[i] * 16); 859 } 860 } 861 } 862 863 __gshared Snake* snake; 864 865 void snakeRegister() 866 { 867 import game_core.rendering; 868 869 snake = Mallocator.make!Snake; 870 871 snake.texture.create(); 872 snake.texture.load("assets/textures/atlas.png"); 873 874 gEntityManager.beginRegister(); 875 876 gEntityManager.registerPass("fixed"); 877 878 registerRenderingModule(gEntityManager); 879 880 gEntityManager.registerComponent!CLocation; 881 gEntityManager.registerComponent!CILocation; 882 gEntityManager.registerComponent!CSnake; 883 gEntityManager.registerComponent!CApple; 884 gEntityManager.registerComponent!CParticle; 885 gEntityManager.registerComponent!CParticleVector; 886 gEntityManager.registerComponent!CMovement; 887 gEntityManager.registerComponent!CInput; 888 gEntityManager.registerComponent!CAnimation; 889 890 gEntityManager.registerSystem!MoveSystem(0,"fixed"); 891 gEntityManager.registerSystem!InputSystem(-100); 892 gEntityManager.registerSystem!FixSnakeDirectionSystem(-1,"fixed"); 893 gEntityManager.registerSystem!AnimationRenderSystem(100); 894 gEntityManager.registerSystem!AnimationSystem(-1); 895 gEntityManager.registerSystem!ParticleSystem(-1); 896 gEntityManager.registerSystem!ParticleMovementSystem(-1); 897 gEntityManager.registerSystem!DrawAppleSystem(99); 898 gEntityManager.registerSystem!DrawSnakeSystem(101); 899 900 gEntityManager.registerSystem!CopyLocationSystem(100); 901 //gEntityManager.registerSystem!AppleRemoveSystem(100); 902 gEntityManager.registerSystem!AppleSystem(101); 903 gEntityManager.registerSystem!SnakeSystem(101); 904 905 gEntityManager.endRegister(); 906 } 907 908 void snakeStart() 909 { 910 launcher.gui_manager.addComponent(CApple(),"Apple"); 911 launcher.gui_manager.addComponent(CSnake(),"Snake"); 912 launcher.gui_manager.addComponent(CParticle(1000),"Particle"); 913 launcher.gui_manager.addComponent(CParticleVector(vec2(0,1)),"Particle Vector"); 914 launcher.gui_manager.addComponent(CInput(),"Input"); 915 launcher.gui_manager.addComponent(CMovement(CMovement.Direction.up),"Movement"); 916 launcher.gui_manager.addComponent(CAnimation(),"Animation"); 917 launcher.gui_manager.addComponent(CILocation(),"Int Location"); 918 launcher.gui_manager.addComponent(CLocation(),"Location"); 919 920 launcher.gui_manager.addSystem(becsID!MoveSystem,"Move System"); 921 launcher.gui_manager.addSystem(becsID!InputSystem,"Input System"); 922 launcher.gui_manager.addSystem(becsID!FixSnakeDirectionSystem,"Fix Direction System"); 923 launcher.gui_manager.addSystem(becsID!AnimationRenderSystem,"Animation Render System"); 924 launcher.gui_manager.addSystem(becsID!AnimationSystem,"Animation System"); 925 launcher.gui_manager.addSystem(becsID!ParticleSystem,"Particle Life System"); 926 launcher.gui_manager.addSystem(becsID!ParticleMovementSystem,"Particle Movement System"); 927 launcher.gui_manager.addSystem(becsID!DrawAppleSystem,"Draw Apple System"); 928 launcher.gui_manager.addSystem(becsID!DrawSnakeSystem,"Draw Snake System"); 929 launcher.gui_manager.addSystem(becsID!CopyLocationSystem,"Copy Location System"); 930 //launcher.gui_manager.addSystem(becsID!AppleSystem,"Apple System"); 931 //launcher.gui_manager.addSystem(becsID!SnakeSystem,"Snake System"); 932 933 snake.snake_destroy_particle_frames = Mallocator.makeArray([vec4(64,144,16,16)*px,vec4(80,144,16,16)*px,vec4(96,144,16,16)*px,vec4(112,144,16,16)*px].staticArray); 934 935 { 936 ushort[5] components = [becsID!CILocation, becsID!CSnake, becsID!CMovement, becsID!CInput, becsID!CLocation]; 937 snake.snake_tmpl = gEntityManager.allocateTemplate(components); 938 gEntityManager.addEntity(snake.snake_tmpl,[CILocation(ivec2(2,2)).ref_].staticArray); 939 } 940 941 { 942 snake.snake_destroy_particle = gEntityManager.allocateTemplate([becsID!CLocation, becsID!CParticle, becsID!CParticleVector, becsID!CAnimation, becsID!CLocation].staticArray); 943 CAnimation* canim = snake.snake_destroy_particle.getComponent!CAnimation; 944 canim.frames = snake.snake_destroy_particle_frames; 945 CParticle* particle = snake.snake_destroy_particle.getComponent!CParticle; 946 particle.life = 400; 947 } 948 949 { 950 ushort[3] components = [becsID!CILocation, becsID!CApple, becsID!CLocation]; 951 snake.apple_tmpl = gEntityManager.allocateTemplate(components); 952 snake.addApple(); 953 } 954 955 launcher.gui_manager.addTemplate(gEntityManager.allocateTemplate(snake.snake_tmpl), "Snake"); 956 launcher.gui_manager.addTemplate(gEntityManager.allocateTemplate(snake.apple_tmpl), "Apple"); 957 launcher.gui_manager.addTemplate(gEntityManager.allocateTemplate(snake.snake_destroy_particle), "Particle"); 958 959 MoveSystem* move_system = gEntityManager.getSystem!MoveSystem(); 960 move_system.setTemplates(); 961 } 962 963 void snakeEnd() 964 { 965 //gEntityManager.freeTemplate(simple.tmpl); 966 Mallocator.dispose(snake); 967 } 968 969 void snakeEvent(SDL_Event* event) 970 { 971 972 } 973 974 bool snakeLoop() 975 { 976 launcher.render_position = (vec2(launcher.window_size.x,launcher.window_size.y)*launcher.scalling - vec2(288,288)) * 0.5; 977 978 // if(launcher.show_demo_wnd) 979 // { 980 // igSetNextWindowPos(ImVec2(800 - 260, 30), ImGuiCond_Once, ImVec2(0,0)); 981 // igSetNextWindowSize(ImVec2(250, 0), ImGuiCond_Once); 982 // if(igBegin("Snake",&launcher.show_demo_wnd,0)) 983 // { 984 985 // } 986 // igEnd(); 987 // } 988 989 gEntityManager.begin(); 990 991 float delta_time = launcher.deltaTime; 992 if(delta_time > 2000)delta_time = 2000; 993 __gshared float time = 0; 994 995 if(launcher.getKeyState(SDL_SCANCODE_SPACE))time += delta_time * 3; 996 else time += delta_time; 997 998 while(time > 200) 999 { 1000 time -= 200; 1001 1002 gEntityManager.update("fixed"); 1003 } 1004 1005 gEntityManager.update(); 1006 1007 gEntityManager.end(); 1008 1009 return true; 1010 } 1011 1012 DemoCallbacks getSnakeDemo() 1013 { 1014 DemoCallbacks demo; 1015 demo.register = &snakeRegister; 1016 demo.initialize = &snakeStart; 1017 demo.deinitialize = &snakeEnd; 1018 demo.loop = &snakeLoop; 1019 demo.tips = snake.tips; 1020 return demo; 1021 }