TECS is a very simple and tiny ECS implementation. It doesn't try to be fast and productive. This implementation tries to be simple and understandable for both the user and the developer.
Quick Tutorial
At the beginning of use, you should initialize the world. Of course, you can do all this manually, but it's best to use the initworld() procedure
import tecs var world = initWorld() var entity = world.addEntity
This implementation uses tags in addition to components. Tags differ from components in that they do not have fields and do not allocate memory when registering a new tag.
import tecs type # component BasicComponent = object entityId: uint64 # required val: int # tag BasicTag = object # no fields var world = initWorld() var entity = world.addEntity var component = world.addComponent(entity, BasicComponent) component.val = 12 world.addTag(entity, BasicTag)
ECS is written in such a way that you can add/remove/change an entity/component/tag at any time. But be careful with this.
Types
Entity = object version*: uint32 componentBitmask*: uint32 tagBitmask*: uint32
- Base Entity object
World = object entities: seq[Entity] freeEntities: seq[uint32] currentEntityId*: uint32 maxEntityId*: uint32 allocSize*: uint32 components: seq[pointer] componentTypeList: seq[string] tagTypeList: seq[string]
- The World contains all entities and components. In fact, it is a container that is the foundation for the work of everything else. The world is responsible for the storage of entities and for the storage of components, with their subsequent implementation.
Procs
proc initWorld(initAlloc: uint32 = 1000; allocSize: uint32 = 1000): World {...}{. raises: [], tags: [].}
- Initialization of a World object. You should use this procedure to create a world, as it immediately indicates the size of the allocated memory. You can also change initAlloc for initial memory allocation and allocSize to specify how many blocks should be allocated in case of insufficient memory.
proc getEntityId(entity: uint64): uint32 {...}{.inline, raises: [], tags: [].}
- Returns the entity ID. In this implementation, the entity identifier is represented as uint64, which stores the ID in the upper 32 bits, and the version in the lower 32 bits.
proc getEntityVersion(entity: uint64): uint32 {...}{.inline, raises: [], tags: [].}
- Returns the version of the entity. In this implementation, the entity identifier is represented as uint64, which stores the ID in the upper 32 bits, and the version in the lower 32 bits.
proc addEntity(world: var World): uint64 {...}{.raises: [], tags: [].}
- Adding a new entity to the world. Returns the entity identifier.
proc removeComponent(world: var World; entity: uint64; componentType: typedesc)
- Remove a component from an entity located in the world.
proc removeTag(world: var World; entity: uint64; tagType: typedesc)
- Remove a component from an entity located in the world.
proc freeEntity(world: var World; entity: uint64) {...}{.raises: [], tags: [].}
- Remove an entity from the world. In fact, the entity will not be removed, but only clear the bitmask of components and tags, and will also be moved to the list of recently released entities.
proc addComponent[T](world: var World; entity: uint64; componentType: typedesc[T]): ptr T {...}{.discardable.}
- Add a component for an entity located in the world.
proc addComponent[T](world: var World; entity: uint64; component: T): ptr T {...}{. discardable.}
- Add a component for an entity located in the world. This procedure can help in reducing the amount of code.
proc addTag(world: var World; entity: uint64; tagType: typedesc)
- Add a tag for an entity located in the world. Tags, like entities, are objects, but they do not contain fields and no memory is allocated for them. In fact, they are stored only in the entity bitmask. Use them if you need to mark any object. They will help you save performance and memory, unlike if you were using a borderless component.
proc getComponent[T](world: var World; entity: uint64; componentType: typedesc[T]): ptr T
-
Returns a pointer to the component. Used in systems.
Important! This procedure does not check for the presence of a component in an entity, so if used incorrectly, you can get a component from an entity that no longer exists (that is, garbage instead of a component).
proc withTag(world: var World; tagType: typedesc): seq[uint64]
- It is used in systems for initial filtering of entities by tag.
proc withTag(entities: seq[uint64]; world: var World; tagType: typedesc): seq[ uint64]
- It is used in filter chains in systems for initial filtering of entities by tag.
proc withComponent(world: var World; componentType: typedesc): seq[uint64]
- It is used in systems for initial filtering of entities by components.
proc withComponent(entities: seq[uint64]; world: var World; componentType: typedesc): seq[uint64]
- It is used in filter chains in systems for initial filtering of entities by components.