Vexel's ECS library for Nim👑, heavily inspired by Beef🥩's yeacs, a lot of his ideas were used, and some of his macros were directly copied.
vecs's API aims to be mostly the same, with minor differences.
The main design differences between vecs and yeacs are in the implementation:
vecsavoids manually copying memory, erasure is implemented by using abstract types, then casting to concrete types when needed. This simplifies book-keeping a bit, and goes easier on references, not needing to track move semantics.vecsapproaches ECS with a collection for each component in the archetype, whileyeacsinstead uses a single collection of tuples of components for each archetype.
The API reference is available here.
# Import the library
import vecs# Declare some components, components are regular value objects.
type Charcter = object
name*: string
class*: string
type Health = object
current*: int
max*: int
type Weapon = object
name*: string
attack*: int# Create a world
var world = World()# Add an entity with components
let entityId = world.add (
Character(name: "Marcus", class: "Warrior"),
Health(current: 120, max: 120)
)# Get a component from an entity to read its values
let health = world.read(entityId, Health)
echo health.current " / " & health.max# Get a component from an entity with write access
for health in world.write(entityId, Health):
health.current += 75# Read multiple components from an entity
let (character, health) = world.read(entityId, (Character, Health))
echo character.name & "'s health is: " & health.current# Write to multiple components from an entity
for (character, health) in world.components(entityId, (Write[Character], Write[Health])):
character.name = "Happy " & character.name
health.current += 75# Query for components
var characterWithSwordsQuery = Query[(Character, Write[Sword])]()
for (character, sword) in world.query(characterWithSwordsQuery):
sword.attack += 10
echo character.name, "'s weapon ", sword.name, " reforged!"# Removing an entity
world.remove entityId# Adding a component
world.add(entityId, Shield(name: "Steel Shield", defense: 15))# Removing a component
world.remove(entityId, Shield)# The `Meta` component is automatically added, and holds the `Id` of the entity.
# This is useful for embedding references to other entities into components.
let entityId = world.add((Character(name: "Leon", class: "Paladin"),), Immediate)
let meta = world.read(entityId, Meta):
assert entityId == meta.id# Query components for writting
var charactersWithHealth = Query[(Character, Write[Health])]()
for (character, health) in world.query(charactersWithHealth):
health.current += 10# Query for optional components
var charactersWithWeapons = Query[(Character, Opt[Weapon])]()
for (character, weapon) in world.query(charactersWithWeapons):
weapon.isSmoething:
echo character.name, " has a weapon, ", weapon.name
weapon.isNothing:
echo character.name, " has no weapon"# Exclude components from a query
var disarmedCharacters = Query[(Character, Not[Weapon])]()
for (character,) in world.query(disarmedCharacters):
echo character.name, " has no weapon"- Add entities
- Archetypes
- Queries
- Remove entities
- Support dynamic archetypes
- Add component
- Remove component
- Special Id component
- Restrict generic T on queries and components procs to be tuples
- 'Not' Queries
- 'Opt' Queries
- 'Write' Queries
- Stable ids for components
- Polish console output
- Convenience procs and checks
- Refactor
Idcomponent to aMetadatacomponent with id, removal, and addition info - Additions and removals should be enqueued and consolidated in order later
- Text serialization
- Binary serialization
- Integrate with reploid
- Convenience procs
-
componentandcomponentsaccept a list of entity ids - Add and Remove multiple components
-
- Concurrency support
- Zero-allocation?
- Spatial and custom queries