tecs

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.