DEV Community

Cover image for Creating A Game With ECS - Part 1
Harsh Singh
Harsh Singh

Posted on • Edited on

Creating A Game With ECS - Part 1

In this tutorial series I will try to introduce you to the basic concepts of ECS by building a game with an ECS Game Framework.

What is ECS?

ECS or Entity-Component-System is a Game Development Architecture that is used for developing highly efficient games. It comprises of 3 Elements -

1. Entity

it refers to a Game Object/Entity composed of various components. Systems "query" these entities to perform various functions. They store the "data" that is used by the systems to ascertain the current state of the Entity and perform operations accordingly.

2. Component

A Component is nothing but data or property. An Entity can have multiple Components like Health, Position, Shape, etc.

3. System

A System is the place where things start moving. You can think of them as a function which are called on every tick/update. They perform specialised functions like Rendering Entities, Physics, Collision, etc. They "query" the components they are interested in (those who have a certain set of components that the system needs) and update the game.

Query

Another important component of ECS is a Query. You can think of it as a Set/Array of names of components. Systems use these queries to get a list of entities that have those components. It is similar to how you query DOM elements using CSS Queries.

The Beginning

We will be using Square - An ECS Framework written in Javascript.

Project Structure

/game
 - queries.js
 - components.js
 - systems.js
 - main.js
Enter fullscreen mode Exit fullscreen mode

queries.js
It will contain Queries that our systems will use.

export const Renderable = ['@position', '@size', '@shape'];
Enter fullscreen mode Exit fullscreen mode

components.js

export class PositionComponent {
    constructor(x = 0, y = 0) {
        this.x = x;
        this.y = y;
    }
}

export class SizeComponent {
    constructor(width = 0, height = 0) {
        this.width = width;
        this.height = height;
    }
}

export class ShapeComponent {
    static RECTANGLE = 'rectangle';
}
Enter fullscreen mode Exit fullscreen mode

systems.js

import { ShapeComponent } from './components.js';

export function RenderingSystem(app) {
    const canvas = document.createElement('canvas');
    const context = canvas.getContext('2d');

    app.on('init', () => document.body.appendChild(canvas));

    app.on('update', () => {
        context.clearRect(0, 0, canvas.width, canvas.height);

        app.emit('render', canvas, context);
    });
}

export function ShapeRenderer(app) {
    app.on('render', (_canvas, ctx) => {
        const entities = app.query('@shape');

        entities.forEach(entity => {
            if(entity.shape === ShapeComponent.RECTANGLE) {
                ctx.fillRect(
                    entity.position.x,
                    entity.position.y,
                    entity.size.width,
                    entity.size.height
                );
            }
        });
    });
}
Enter fullscreen mode Exit fullscreen mode

main.js

// importing the library
import { Application } from 'https://unpkg.com/square-ecs@latest';

// importing various parts of our game
import { RenderingSystem, ShapeRenderer } from './systems.js';
import { PositionComponent, SizeComponent, ShapeComponent } from './components.js';

// creating an instance of the game
const app = new Application({
    // shared data that our game will use, 
    // it can contain any kind of data
    data: {}, 
    // systems of our game
    systems: [
        RenderingSystem,
        ShapeRenderer
    ]
});

app.on('init', () => {
    const entity = app.entityPool.getEntity(); // borrow an entity from the entity pool

    // attach various components
    entity
        .attach('shape', ShapeComponent.RECTANGLE)
        .attach('position', new PositionComponent(100, 100))
        .attach('size', new SizeComponent(50, 50));

    // add it to the world
    app.add(entity);
});

// start the game
app.start();
Enter fullscreen mode Exit fullscreen mode

Output

Output

Yeah! We have successfully rendered a Square.

In the next article, things will start moving.

Jai Shree Ram!

Top comments (2)

Collapse
 
calinzbaenen profile image
Calin Baenen

Alt title: Bevy in JavaScript.
/j

Collapse
 
theharshsingh profile image
Harsh Singh

Yes 😄. Please do checkout the framework's github page.