1 /// 2 /// Defines an architecture to manage entities, and several entity-related 3 /// events. Components must be defined as classes (for internal storage). 4 /// 5 /// Copyright: Copyright (c) 2014 James Zhu. 6 /// 7 /// License: MIT License (Expat). See accompanying file LICENSE. 8 /// 9 /// Authors: James Zhu <github.com/jzhu98> 10 /// 11 12 module star.entity.entity; 13 14 import std.container; 15 import std.conv; 16 import std.algorithm : filter; 17 import std.math : abs; 18 19 import star.entity.event; 20 21 /// An id encapsulates an index (unique ulong in an entity manager) 22 /// and a tag (to check if the entity is in sync (valid) with the manager). 23 struct ID 24 { 25 public: 26 /// An invalid id, used for invalidating entities. 27 static immutable ID INVALID = ID(0, 0); 28 29 /// Construct an id from a 64-bit integer: 30 /// A concatenated 32-bit index and 32-bit tag. 31 this(ulong id) pure nothrow @safe 32 { 33 _id = id; 34 } 35 36 /// Construct an id from a 32-bit index and a 32-bit tag. 37 this(uint index, uint tag) pure nothrow @safe 38 { 39 this(cast(ulong) index << 32UL | (cast(ulong) tag)); 40 } 41 42 /// Return the index of the ID. 43 inout(uint) index() inout pure nothrow @property @safe 44 { 45 return cast(uint)(_id >> 32UL); 46 } 47 48 unittest 49 { 50 auto id1 = ID(0x0000000100000002UL); 51 auto id2 = ID(3U, 4U); 52 assert(id1.index == 1U); 53 assert(id2.index == 3U); 54 } 55 56 /// Return the tag of the ID. 57 inout(uint) tag() inout pure nothrow @property @safe 58 { 59 return cast(uint) (_id); 60 } 61 62 unittest 63 { 64 auto id1 = ID((12UL << 32) + 13UL); 65 auto id2 = ID(210U, 5U); 66 assert(id1.tag == 13U); 67 assert(id2.tag == 5U); 68 } 69 70 string toString() const pure @safe 71 { 72 return "ID(" ~ to!string(this.index) ~ ", " ~ to!string(this.tag) ~ ")"; 73 } 74 75 /// Equals operator (check for equality). 76 bool opEquals()(auto ref const ID other) const pure nothrow @safe 77 { 78 return _id == other._id; 79 } 80 81 unittest 82 { 83 auto id1 = ID(12U, 32U); 84 auto id2 = ID(12U, 32U); 85 auto id3 = ID(13U, 32U); 86 assert(id1 == id2); 87 assert(id1 != id3); 88 assert(id2 != id3); 89 } 90 91 /// Comparison operators (check for greater / less than). 92 int opCmp(ref const ID other) const pure nothrow @safe 93 { 94 if (_id > other._id) 95 { 96 return 1; 97 } 98 else if (_id == other._id) 99 { 100 return 0; 101 } 102 else 103 { 104 return -1; 105 } 106 } 107 108 unittest 109 { 110 auto id1 = ID(1U, 10U); 111 auto id2 = ID(1U, 11U); 112 auto id3 = ID(2U, 1U); 113 assert(id1 < id2); 114 assert(id1 <= id3); 115 assert(id3 >= id2); 116 assert(id3 > id1); 117 } 118 119 private: 120 ulong _id; 121 } 122 123 /// An entity an aggregate of components (pure data), accessible with an id. 124 class Entity 125 { 126 public: 127 /// Construct an entity with a manager reference and an ID. 128 this(EntityManager manager, ID id) pure nothrow @safe 129 { 130 _manager = manager; 131 _id = id; 132 } 133 134 /// Return the entity id. 135 inout(ID) id() inout pure nothrow @property @safe 136 { 137 return _id; 138 } 139 140 unittest 141 { 142 auto entity = new Entity(null, ID(1, 2)); 143 assert(entity.id.index == 1); 144 assert(entity.id.tag == 2); 145 } 146 147 /// Return the component added to this entity. 148 inout(C) component(C)() inout pure nothrow @safe 149 { 150 return _manager.component!C(_id); 151 } 152 153 /// Check if the entity has a specific component. 154 bool hasComponent(C)() pure nothrow @safe 155 { 156 return _manager.hasComponent!C(_id); 157 } 158 159 /// Add a component to the entity. 160 void add(C)(C component) pure nothrow @safe 161 { 162 _manager.addComponent!C(_id, component); 163 } 164 165 /// Remove the component if the entity has it. 166 void remove(C)() pure nothrow @safe 167 { 168 _manager.remove!C(_id); 169 } 170 171 /// Destroy this entity and invalidate all handles to this entity. 172 void destroy() pure nothrow @safe 173 { 174 _manager.destroy(_id); 175 invalidate(); 176 } 177 178 /// Check if this handle is valid (points to the entity with the same tag). 179 bool valid() pure nothrow @safe 180 { 181 if (_manager is null) 182 { 183 return false; 184 } 185 else 186 { 187 return _manager.valid(_id); 188 } 189 } 190 191 /// Invalidate this entity handle (but not other handles). 192 void invalidate() pure nothrow @safe 193 { 194 _manager = null; 195 _id = ID.INVALID; 196 } 197 198 unittest 199 { 200 auto entity = new Entity(null, ID.INVALID); 201 assert(!entity.valid()); 202 } 203 204 /// Equals operator (check for equality). 205 override bool opEquals(Object o) const 206 { 207 auto other = cast(Entity) o; 208 return _id == other._id && _manager == other._manager; 209 } 210 211 unittest 212 { 213 auto entity1 = new Entity(null, ID(1, 1)); 214 auto entity2 = new Entity(null, ID(1, 1)); 215 auto entity3 = new Entity(null, ID(1, 2)); 216 auto entity4 = new Entity(null, ID(2, 1)); 217 218 assert(entity1 == entity1); 219 assert(entity1 == entity2); 220 assert(entity1 != entity3); 221 assert(entity1 != entity4); 222 223 assert(entity2 == entity1); 224 assert(entity2 == entity2); 225 assert(entity2 != entity3); 226 assert(entity2 != entity4); 227 228 assert(entity3 != entity1); 229 assert(entity3 != entity2); 230 assert(entity3 == entity3); 231 assert(entity3 != entity4); 232 233 assert(entity4 != entity1); 234 assert(entity4 != entity2); 235 assert(entity4 != entity3); 236 assert(entity4 == entity4); 237 } 238 239 /// Comparison operator. 240 override int opCmp(Object o) const @safe 241 { 242 auto other = cast(Entity) o; 243 return _id.opCmp(other._id); 244 } 245 246 unittest 247 { 248 auto entity1 = new Entity(null, ID(0, 1)); 249 auto entity2 = new Entity(null, ID(10, 230)); 250 auto entity3 = new Entity(null, ID(11, 200)); 251 252 assert(entity1 < entity2); 253 assert(entity1 <= entity3); 254 assert(entity3 > entity2); 255 assert(entity2 >= entity1); 256 } 257 258 private: 259 EntityManager _manager; 260 ID _id; 261 } 262 263 mixin template EntityEvent() 264 { 265 this(Entity entity) 266 { 267 this.entity = entity; 268 } 269 Entity entity; 270 } 271 272 mixin template ComponentEvent(C) 273 { 274 this(Entity entity, C component) 275 { 276 this.entity = entity; 277 this.component = component; 278 } 279 Entity entity; 280 C component; 281 } 282 283 struct EntityCreatedEvent 284 { 285 mixin EntityEvent; 286 } 287 288 struct EntityDestroyedEvent 289 { 290 mixin EntityEvent; 291 } 292 293 struct ComponentAddedEvent(C) 294 { 295 mixin ComponentEvent!C; 296 } 297 298 struct ComponentRemovedEvent(C) 299 { 300 mixin ComponentEvent!C; 301 } 302 303 /// Manages entities and their associated components. 304 class EntityManager 305 { 306 public: 307 /// Construct an empty entity manager. 308 this(EventManager events) 309 { 310 _indexCounter = 0U; 311 _numEntities = 0U; 312 _events = events; 313 } 314 315 /// A range over all entities in the manager. 316 struct Range 317 { 318 /// Construct a range over this manager. 319 private this(EntityManager manager, uint index = 0) pure nothrow @safe 320 { 321 _manager = manager; 322 _index = index; 323 } 324 325 /// Return the number of entities to iterate over (including empty ones). 326 size_t length() const pure nothrow @property @safe 327 { 328 return _manager.capacity; 329 } 330 331 /// Return if an only if the range cannot access any more entities. 332 bool empty() const pure nothrow @property @safe 333 { 334 return _index >= _manager.capacity; 335 } 336 337 /// Return the current entity. 338 Entity front() pure nothrow @property @safe 339 { 340 return _manager.entity(_index); 341 } 342 343 /// Access the next entity. 344 void popFront() pure nothrow @safe 345 { 346 _index++; 347 } 348 349 /// Return a copy of this range. 350 Range save() pure nothrow @safe 351 { 352 return Range(_manager, _index); 353 } 354 355 private EntityManager _manager; 356 private uint _index; 357 } 358 359 /// Return a range over the entities. 360 Range opSlice() pure nothrow @safe 361 { 362 return Range(this); 363 } 364 365 unittest 366 { 367 class Test 368 { 369 this(int x) 370 { 371 y = x; 372 } 373 int y; 374 } 375 376 auto manager = new EntityManager(new EventManager); 377 auto entity1 = manager.create(); 378 auto entity2 = manager.create(); 379 380 entity1.add(new Test(3061)); 381 entity2.add(new Test(2015)); 382 383 foreach(entity; manager[]) 384 { 385 if (entity == entity1) 386 { 387 assert(entity1.component!Test().y == 3061); 388 } 389 else if (entity == entity2) 390 { 391 assert(entity2.component!Test().y == 2015); 392 } 393 else 394 { 395 assert(0); 396 } 397 } 398 } 399 400 /// Create a range with only the entities with the specified components. 401 auto entities(Components...)() pure nothrow @safe 402 { 403 foreach(C; Components) 404 { 405 if (!hasType!C()) 406 { 407 accomodateComponent!C(); 408 } 409 } 410 411 auto mask = componentMask!Components(); 412 413 bool hasComponents(Entity entity) 414 { 415 bool[] combinedMask = new bool[mask.length]; 416 combinedMask[] = componentMask(entity.id)[] & mask[]; 417 return combinedMask[] == mask[]; 418 } 419 420 return this[].filter!(hasComponents)(); 421 } 422 423 unittest 424 { 425 class Position 426 { 427 this(int x, int y) 428 { 429 this.x = x; 430 this.y = y; 431 } 432 int x, y; 433 } 434 435 class Velocity 436 { 437 this(int x, int y) 438 { 439 this.x = x; 440 this.y = y; 441 } 442 int x, y; 443 } 444 445 class Gravity 446 { 447 this(double acc) 448 { 449 accel = acc; 450 } 451 double accel; 452 } 453 454 auto manager = new EntityManager(new EventManager); 455 456 auto entity1 = manager.create(); 457 entity1.add(new Position(2, 1)); 458 entity1.add(new Velocity(15, 4)); 459 460 auto entity2 = manager.create(); 461 entity2.add(new Velocity(-1, -3)); 462 entity2.add(new Gravity(10)); 463 464 auto entity3 = manager.create(); 465 entity3.add(new Gravity(-9.8)); 466 entity3.add(new Position(14, -9)); 467 468 auto positionEntities = manager.entities!Position(); 469 auto velocityEntities = manager.entities!Velocity(); 470 auto gravityEntities = manager.entities!Gravity(); 471 auto physicsEntities = manager.entities!(Position, Velocity, Gravity)(); 472 473 foreach (pos; positionEntities) 474 { 475 assert(pos == entity1 || pos == entity3); 476 assert(pos != entity2); 477 } 478 479 foreach(vel; velocityEntities) 480 { 481 assert(vel == entity1 || vel == entity2); 482 assert(vel != entity3); 483 } 484 485 foreach(grav; gravityEntities) 486 { 487 assert(grav == entity2 || grav == entity3); 488 assert(grav != entity1); 489 } 490 491 assert(physicsEntities.empty()); 492 } 493 494 /// Return the number of entities. 495 size_t count() const pure nothrow @property @safe 496 { 497 return _numEntities; 498 } 499 500 /// Return the number of free entity indices. 501 size_t free() const pure nothrow @property @safe 502 { 503 return capacity - count; 504 } 505 506 /// Return the maximum capacity of entities before needing reallocation. 507 size_t capacity() const pure nothrow @property @safe 508 { 509 return _indexCounter; 510 } 511 512 /// Return if and only if there are no entities. 513 bool empty() const pure nothrow @property @safe 514 { 515 return _numEntities == 0; 516 } 517 518 /// Return the entity with the specified index. 519 Entity entity(uint index) pure nothrow @safe 520 { 521 return entity(id(index)); 522 } 523 524 unittest 525 { 526 auto manager = new EntityManager(new EventManager); 527 auto entity = manager.create(); 528 assert(entity == manager.entity(entity.id.index)); 529 } 530 531 /// Return the entity with the specified (and valid) id. 532 /// Returns null if the id is invalid. 533 Entity entity(ID id) pure nothrow @safe 534 out (result) 535 { 536 if (result !is null) 537 { 538 assert(valid(result.id)); 539 } 540 } 541 body 542 { 543 if (valid(id)) 544 { 545 return new Entity(this, id); 546 } 547 else 548 { 549 return null; 550 } 551 } 552 553 unittest 554 { 555 auto manager = new EntityManager(new EventManager); 556 auto entity = manager.create(); 557 assert(entity == manager.entity(entity.id)); 558 } 559 560 /// Return the id with the specified index. 561 ID id(uint index) pure nothrow @safe 562 { 563 if (index < _indexCounter) 564 { 565 return ID(index, _entityTags[index]); 566 } 567 else 568 { 569 return ID(index, 0); 570 } 571 } 572 573 unittest 574 { 575 auto manager = new EntityManager(new EventManager); 576 auto entity = manager.create(); 577 assert(entity.id == manager.id(entity.id.index)); 578 } 579 580 /// Check if this entity handle is valid - is not invalidated or outdated 581 bool valid(ID id) const pure nothrow @safe 582 { 583 return (id.index < _indexCounter && _entityTags[id.index] == id.tag); 584 } 585 586 unittest 587 { 588 auto manager = new EntityManager(new EventManager); 589 assert(!manager.valid(ID.INVALID)); 590 assert(manager.valid(manager.create().id)); 591 } 592 593 /// Create an entity in a free slot. 594 Entity create() pure nothrow @safe 595 out (result) 596 { 597 assert(valid(result.id)); 598 } 599 body 600 { 601 uint index; 602 603 // Expand containers to accomodate new index 604 if (_freeIndices.empty()) 605 { 606 index = _indexCounter; 607 accomodateEntity(_indexCounter); 608 609 // Uninitialized value is 0, so any entities with tag 0 are invalid 610 _entityTags[index] = 1; 611 } 612 // Fill unused index, no resizing necessary 613 else 614 { 615 // Remove index from free indices list 616 index = _freeIndices.front(); 617 _freeIndices.removeFront(); 618 } 619 620 _numEntities++; 621 _events.emit(EntityCreatedEvent()); 622 return new Entity(this, ID(index, _entityTags[index])); 623 } 624 625 unittest 626 { 627 auto manager = new EntityManager(new EventManager); 628 auto entity1 = manager.create(); 629 auto entity2 = manager.create(); 630 631 assert(entity1.valid()); 632 assert(entity2.valid()); 633 assert(entity1 != entity2); 634 635 entity1.invalidate(); 636 assert(!entity1.valid()); 637 } 638 639 /// Destroy the specified entity and invalidate all handles to it. 640 void destroy(ID id) pure nothrow @safe 641 out 642 { 643 assert(!valid(id)); 644 } 645 body 646 { 647 if (valid(id)) 648 { 649 auto index = id.index; 650 // Invalidate all handles by incrementing tag 651 _entityTags[index]++; 652 653 // Add index to free list 654 _freeIndices.insert(index); 655 656 // Remove all components 657 foreach(component; _components) 658 { 659 component[index] = null; 660 } 661 662 // Clear the component bitmask 663 for (int i = 0; i < _componentMasks[index].length; i++) 664 { 665 _componentMasks[index][i] = false; 666 } 667 668 _numEntities--; 669 _events.emit(EntityDestroyedEvent()); 670 } 671 } 672 673 unittest 674 { 675 auto manager = new EntityManager(new EventManager); 676 auto entity1 = manager.create(); 677 auto entity2 = manager.create(); 678 auto entity3 = manager.create(); 679 680 assert(entity1.id == ID(0, 1)); 681 assert(entity2.id == ID(1, 1)); 682 assert(entity3.id == ID(2, 1)); 683 684 // Two methods of destroying entities 685 manager.destroy(entity1.id); 686 entity2.destroy(); 687 688 assert(!entity1.valid()); 689 assert(!entity2.valid()); 690 assert(entity3.valid()); 691 692 auto entity4 = manager.create(); 693 auto entity5 = manager.create(); 694 695 assert(entity3.valid()); 696 assert(entity4.valid()); 697 assert(entity5.valid()); 698 assert(entity4.id == ID(1, 2)); 699 assert(entity5.id == ID(0, 2)); 700 } 701 702 /// Add a component to the specified entity. 703 void addComponent(C)(ID id, C component) pure nothrow @safe 704 in 705 { 706 assert(valid(id)); 707 } 708 out 709 { 710 assert(hasComponent!C(id)); 711 } 712 body 713 { 714 accomodateComponent!C(); 715 setComponent!C(id, component); 716 setMask!C(id, true); 717 _events.emit(ComponentAddedEvent!C()); 718 } 719 720 /// Remove a component from the entity (no effects if it is not present). 721 void removeComponent(C)(ID id) pure nothrow @safe 722 out 723 { 724 assert(!hasComponent!C(id)); 725 } 726 body 727 { 728 if (hasComponent!C(id)) 729 { 730 setComponent(id, null); 731 setMask!C(id, false); 732 _events.emit(ComponentRemovedEvent!C()); 733 } 734 } 735 736 /// Check if the entity has the specified component. 737 bool hasComponent(C)(const ID id) const pure nothrow @safe 738 in 739 { 740 assert(valid(id)); 741 } 742 body 743 { 744 return (hasType!C() && component!C(id) !is null); 745 } 746 747 /// Return the component associated with this entity. 748 inout(C) component(C)(ID id) inout pure nothrow @safe 749 in 750 { 751 assert(valid(id)); 752 } 753 body 754 { 755 return cast(inout(C)) _components[type!C()][id.index]; 756 } 757 758 unittest 759 { 760 class Position 761 { 762 this(int x, int y) 763 { 764 this.x = x; 765 this.y = y; 766 } 767 int x, y; 768 } 769 770 class Jump 771 { 772 bool onGround = true; 773 } 774 775 auto manager = new EntityManager(new EventManager); 776 auto entity = manager.create(); 777 auto position = new Position(1001, -19); 778 auto jump = new Jump(); 779 780 entity.add(position); 781 manager.addComponent(entity.id, jump); 782 assert(entity.hasComponent!Position()); 783 assert(entity.hasComponent!Jump()); 784 assert(position == entity.component!Position()); 785 assert(jump == manager.component!Jump(entity.id)); 786 } 787 788 /// Delete all entities and components. 789 void clear() pure nothrow @safe 790 { 791 _indexCounter = 0U; 792 _numEntities = 0U; 793 _freeIndices.clear(); 794 _entityTags = null; 795 _components = null; 796 _componentTypes = null; 797 _componentMasks = null; 798 } 799 800 unittest 801 { 802 class Position 803 { 804 this(int x, int y) 805 { 806 this.x = x; 807 this.y = y; 808 } 809 int x, y; 810 } 811 812 class Velocity 813 { 814 this(int x, int y) 815 { 816 this.x = x; 817 this.y = y; 818 } 819 int x, y; 820 } 821 822 class Gravity 823 { 824 this(double acc) 825 { 826 accel = acc; 827 } 828 double accel; 829 } 830 831 auto manager = new EntityManager(new EventManager); 832 833 auto entity1 = manager.create(); 834 entity1.add(new Position(2, 1)); 835 836 auto entity2 = manager.create(); 837 entity2.add(new Velocity(-1, -3)); 838 839 auto entity3 = manager.create(); 840 entity3.add(new Gravity(-9.8)); 841 842 auto position = entity1.component!Position(); 843 auto velocity = entity2.component!Velocity(); 844 auto gravity = entity3.component!Gravity(); 845 846 assert(position.x == 2 && position.y == 1); 847 assert(velocity.x == -1 && velocity.y == -3); 848 assert(abs(-9.8 - gravity.accel) < 1e-9); 849 850 manager.clear(); 851 assert(!entity1.valid()); 852 assert(!entity2.valid()); 853 assert(!entity3.valid()); 854 assert(manager._indexCounter == 0); 855 assert(manager._freeIndices.empty); 856 assert(manager._entityTags.length == 0); 857 assert(manager._components.length == 0); 858 assert(manager._componentMasks.length == 0); 859 860 auto entity4 = manager.create(); 861 assert(entity4.valid()); 862 assert(entity4.id.index == 0); 863 } 864 865 private: 866 // Convenience function to set components. 867 void setComponent(C)(ID id, C component) pure nothrow @safe 868 { 869 _components[type!C()][id.index] = component; 870 } 871 872 // Convenience function to set mask bits. 873 void setMask(C)(ID id, bool value) pure nothrow @safe 874 { 875 _componentMasks[id.index][type!C()] = value; 876 } 877 878 // Reallocate space for a new component. 879 void accomodateComponent(C)() pure nothrow @safe 880 { 881 if (!hasType!C()) 882 { 883 addType!C(); 884 auto type = type!C(); 885 886 // Expand component array (new component - first dimension widens). 887 if (_components.length < type + 1) 888 { 889 _components ~= new Object[_indexCounter]; 890 } 891 892 // Expand all component masks to include new component. 893 if (_componentMasks.length > 0 && _componentMasks[0].length < type + 1) 894 { 895 foreach (ref componentMask; _componentMasks) 896 { 897 componentMask.length = type + 1; 898 } 899 } 900 } 901 } 902 903 // Reallocate space for a new entity. 904 void accomodateEntity(uint index) pure nothrow @safe 905 { 906 if (index >= _indexCounter) 907 { 908 // Expand entity tags. 909 if (_entityTags.length < index + 1) 910 { 911 _entityTags.length = index + 1; 912 } 913 914 // Expand component mask array. 915 if (_componentMasks.length < index + 1) 916 { 917 _componentMasks ~= new bool[_components.length]; 918 } 919 920 // Expand all component arrays (new entity - second dimension widens). 921 if (_components.length > 0 && _components[0].length < index + 1) 922 { 923 foreach (ref component; _components) 924 { 925 component.length = index + 1; 926 } 927 } 928 } 929 _indexCounter = index + 1; 930 } 931 932 // Return a unique integer for every component type. 933 size_t type(C)() inout pure nothrow @safe 934 in 935 { 936 assert(hasType!C()); 937 } 938 body 939 { 940 return _componentTypes[C.classinfo]; 941 } 942 943 // Create a unique id for a new component type. 944 void addType(C)() pure nothrow @trusted 945 { 946 if (!hasType!C()) 947 { 948 _componentTypes[C.classinfo] = _componentTypes.length; 949 _componentTypes.rehash(); 950 } 951 } 952 953 // Return if this component type has already been assigned a unique id. 954 bool hasType(C)() const pure nothrow @safe 955 { 956 return (C.classinfo in _componentTypes) !is null; 957 } 958 959 // Return the component mask (bool array) of this entity. 960 bool[] componentMask(ID id) pure nothrow @safe 961 in 962 { 963 assert(valid(id)); 964 } 965 body 966 { 967 return _componentMasks[id.index]; 968 } 969 970 // Return the component mask with the specified components marked as true. 971 bool[] componentMask(Components...)() pure nothrow @safe 972 in 973 { 974 foreach(C; Components) 975 { 976 assert(type!C() < _components.length); 977 } 978 } 979 body 980 { 981 bool[] mask = new bool[_components.length]; 982 foreach (C; Components) 983 { 984 mask[type!C()] = true; 985 } 986 return mask; 987 } 988 989 unittest 990 { 991 class Position { } 992 class Velocity { } 993 class Gravity { } 994 995 auto manager = new EntityManager(new EventManager); 996 auto entity = manager.create(); 997 entity.add(new Position()); 998 entity.add(new Velocity()); 999 entity.add(new Gravity()); 1000 1001 assert(manager.componentMask!(Position)() == [true, false, false]); 1002 assert(manager.componentMask!(Position, Velocity, Gravity)() == [true, true, true]); 1003 } 1004 1005 // Debugging checks to ensure valid space for new entities and components. 1006 invariant() 1007 { 1008 assert(_numEntities <= _indexCounter); 1009 assert(_entityTags.length == _indexCounter); 1010 assert(_componentMasks.length == _indexCounter); 1011 assert(_components.length == _componentTypes.length); 1012 1013 foreach(componentMask; _componentMasks) 1014 { 1015 assert(componentMask.length == _components.length); 1016 } 1017 1018 foreach(ref componentArray; _components) 1019 { 1020 assert(componentArray.length == _indexCounter); 1021 } 1022 } 1023 1024 // Tracks the actual number of entities. 1025 uint _numEntities; 1026 1027 // Tracks the next unused entity index (i.e. capacity).. 1028 uint _indexCounter; 1029 1030 // Tracks entity indices recently freed. 1031 SList!uint _freeIndices; 1032 1033 // Tracks entity versions (incremented when entity is destroyed) for validity checking. 1034 uint[] _entityTags; 1035 1036 // A nested array of entity components, ordered by component and then entity index. 1037 Object[][] _components; 1038 1039 // A map associating each component class with a unique unsigned integer. 1040 size_t[ClassInfo] _componentTypes; 1041 1042 // Bitmasks of each entity's components, ordered by entity and then by component bit. 1043 bool[][] _componentMasks; 1044 1045 // Event manager. 1046 EventManager _events; 1047 }