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
queries.js
It will contain Queries that our systems will use.
export const Renderable = ['@position', '@size', '@shape'];
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';
}
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
);
}
});
});
}
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();
Output
Yeah! We have successfully rendered a Square.
In the next article, things will start moving.
Jai Shree Ram!
Top comments (2)
Alt title: Bevy in JavaScript.
/j
Yes π. Please do checkout the framework's github page.