decs

ECS lib without STD and without the GC


Keywords
application, desktop, game, library, gamedev, optimized_cpu, nogc, d, dlang, dlanguage, ecs
License
Unlicense
Install
dub fetch decs --version 1.0.2

Documentation

/** Work in progress **/

decs

decs is my attempt to build a @nogc D language Entity Component System. I'm not really a game dev, im trying to learn how an ECS works so some portions of this code can be inefficients or bug prone.

It's inspired by the following projects :

How to build

You have to use dub to build the project.

Add this project as a dependency to your dub.json:

"dependencies": {
    "decs": "~>1.0.3"
}

How to use

Entity Manager

import decs;

void main()
{
    auto em = new EntityManager();

    while(game)
    {
        em.update(dt);
    }
    
    // !important!
    em.dispose();
}

Entities

You can create an entity, kill it, invalidate it or activate it (still need some work here).

auto entity = em.createEntity();

// Notifies all systems that a new entity is alive
entity.activate();

// Entity is still alive but will be invalid in current scope
entity.invalidate();

// Poor entity :<
entity.kill();

Components

A component must be represented as a struct.

struct PositionComponent
{
    float x, y, z;
}

You can add a component to an entity :

// add
entity.add!PositionComponent(1, 2, 3);

// update (its handled as a pointer)
entity.get!PositionComponent().y = 50;

// check
if(entity.has!PositionComponent())
{
    // ...
}

Systems

A system must be a class that inherits decs.System.

class MovementSystem : System
{
    public void update(in float delta)
    {

    }
}

There is 2 ways to query entities from your system :

  • 1 => Using entity manager entities!(components...) method
  • 2 => Using the mixin template mixin ComponentsFilter!(components...);

If you provide a components filter, when an entity is activated, it will be automatically added to the systems whose filters match her components.

class MovementSystem : System
{
    // option 2
    mixin ComponentsFilter!(PositionComponent);

    public void update(in float deltaTime)
    {
        // option 1
        auto entities = this.m_entityManager.entities!(PositionComponent);

        // option 2 (should be more efficient)
        foreach(entity; this.m_entities)
        {

        }
    }
}

System's update method will be called automatically by the entity manager in the main loop. If you want to handle the update manually, you can set the System.UpdatePolicy to manual :

class MovementSystem : System
{
    public this()
    {
        super(System.UpdatePolicy.Manual);
    }
}

// ...

while(game)
{
    em.update(dt);

    // you have to call it manually (before or after the em.update call)
    myMovementSystem.update(dt);
}

Events

A receiver is a class that implements Receiver(EventType) interface.

// An event must be a struct
struct MovementEvent
{
    Entity target;
}

// The receiver
class CameraSystem : Receiver!MovementEvent
{
    /**
     * Movement event received from a system
     */
    public void receive(ref MovementEvent event)
    {
        // follow event.entity...
    }
}

// Subscribe to events
void main()
{
    auto cameraSys = new CameraSystem();

    auto em = new EntityManager();
    em.subscribeToEvent!MovementEvent(cameraSys);
}

You can send events from systems by using the m_eventManager.emit method.

// Sending an event from the MovementSystem
class MovementSystem : Receiver!MovementEvent
{
    public void update(in float deltaTime)
    {
        foreach(entity; this.m_entities)
        {
            this.m_eventManager.emit(MovementEvent(entity));
        }
    }
}

TODOs

  • Add @Component and @Event UDAs
  • Add an example project
  • Improve this readme
  • DOCS !