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