Neurosis Prevention Mechanism
    Wondering what’s next for npm?Check out our public roadmap! »

    ecs

    0.11.0 • Public • Published

    ecs

    data oriented, functional entity component system.

    Build Status

    what?

    Many games and simulations are built around object oriented classes and scene graphs.

    Scene graphs and object hierarchies often end up being a mess, where eventually one ends up facing difficult decisions around questions like "where does this particular piece of logic/state live?" There are certain bits of logic/state that cross-cut and don't fit cleanly in one place, and code and logic become entangled in various places in the graph. Not all relationships in a game/simulation are modeled appropriately with parent/child relationships which is why we get into these situations.

    ECS is an alternative architecture:

    • Entities have 0 or more components
    • Components are pure data and have no behavior
    • Systems have behavior but no data

    The entire ecs module here is about 100 lines of code, and offers an O(1) iterator at run time for querying for entities within systems.

    usage example

    This is a minimal example of what you can do with ecs, illustrating how to declare your entities, components, and systems.

    Requires node version 12.17.1 or higher.

    import ECS      from 'ecs'
    import Keyboard from './my/game/keyboard.js'
    import clamp    from 'clamp'
    
    
    // generates a new entity component system
    const world = ECS.createWorld()
    
    
    // set up the player
    const PLAYER = ECS.createEntity(world)
    ECS.addComponentToEntity(world, PLAYER, 'position', { x: 15, y: 23 })
    ECS.addComponentToEntity(world, PLAYER, 'moveable', { dx: 0, dy: 0 })
    
    
    
    // update entity velocity based on key pressed
    function keyboardControlSystem (world) {
        // called each game loop
        const onUpdate = function (dt) {
            // get all of the entities in the world that pass the filter
            for (const entity of ECS.getEntities(world, [ 'moveable' ])) {
                // update the entity position according to what is pressed
                if (Keyboard.keyPressed('up'))
                    entity.moveable.dy -= 1
                if (Keyboard.keyPressed('down'))
                    entity.moveable.dy += 1
                if (Keyboard.keyPressed('left'))
                    entity.moveable.dx -= 1
                if (Keyboard.keyPressed('right'))
                    entity.moveable.dx += 1
    
               entity.moveable.dx = clamp(entity.moveable.dx, -10, 10)
               entity.moveable.dy = clamp(entity.moveable.dy, -10, 10)
            }
        }
    
        return { onUpdate }
    }
    
    
    function movementSystem (world) {
        const onUpdate = function (dt) {
            for (const entity of ECS.getEntities(world, [ 'position', 'moveable' ])) {
                entity.position.x += entity.moveable.dx
                entity.position.y += entity.moveable.dy
            }
        }
    
        return { onUpdate }
    }
    
    
    function rendererSystem (world) {
        const onUpdate = function (dt) {
    
            // optional 3rd parameter, can be 'added' or 'removed'. provides the list of entities that were
            // added/removed since the last system call which match the filter
            for (const entity of ECS.getEntities(world, [ 'renderable' ], 'added')) {
                // do whatever setup you need for newly created renderable here
            }
    
        }
    
        return { onUpdate }
    }
    
    
    ECS.addSystem(world, keyboardControlSystem)
    ECS.addSystem(world, movementSystem)
    ECS.addSystem(world, rendererSystem)
    
    
    let currentTime = performance.now()
    
    function gameLoop () {
        const newTime = performance.now()
        const frameTime = newTime - currentTime  // in milliseconds, e.g. 16.64356
        currentTime = newTime
    
        // run onUpdate for all added systems
        ECS.update(world, frameTime)
    
        // necessary cleanup step at the end of each frame loop
        ECS.cleanup(world)
    
        requestAnimationFrame(gameLoop)
    }
    
    
    // finally start the game loop
    gameLoop()

    not filter

    Sometimes it's useful to query by components that are not present:

    // get all entities that have a transform component but lack a hero component
    const entities = ECS.getEntities(world, [ 'transform', '!hero' ])

    devtools chrome extension

    If you'd like to see a real time view of the data in your ECS powered program, there is a dev tools extension!

    alt text

    Assuming you have the devtools-extension/ unpacked in chrome, you should see this icon appear colored. clicking it will show this popup, indicating the page is using an ecs instance:

    alt text

    Install

    npm i ecs

    DownloadsWeekly Downloads

    32

    Version

    0.11.0

    License

    MIT

    Unpacked Size

    482 kB

    Total Files

    43

    Last publish

    Collaborators

    • avatar
    • avatar