Published
Edited
Oct 8, 2019
3 stars
Insert cell
Insert cell
ecsy = require("https://bundle.run/ecsy/build/ecsy.module.js")
Insert cell
canvas = DOM.canvas(width, height)
Insert cell
loop = {
var lastTime = performance.now();
do {
var time = performance.now();
var delta = time - lastTime;

// Run all the systems
world.execute(delta, time);

lastTime = time;
yield time | 0;
} while (true);
}
Insert cell
World = ecsy.World
Insert cell
TagComponent = ecsy.TagComponent
Insert cell
System = ecsy.System
Insert cell
world = {
const NUM_ELEMENTS = 600;
const SPEED_MULTIPLIER = 0.1;
const SHAPE_SIZE = 20;
const SHAPE_HALF_SIZE = SHAPE_SIZE / 2;

// Initialize canvas
let canvasWidth = canvas.width;
let canvasHeight = canvas.height;
let ctx = canvas.getContext("2d");

//----------------------
// Components
//----------------------

// Velocity component
class Velocity {
constructor() {
this.x = this.y = 0;
}
}

// Position component
class Position {
constructor() {
this.x = this.y = 0;
}
}

// Shape component
class Shape {
constructor() {
this.primitive = 'box';
}
}

// Renderable component
class Renderable extends TagComponent {}

//----------------------
// Systems
//----------------------

// MovableSystem
class MovableSystem extends System {
// This method will get called on every frame by default
execute(delta, time) {
// Iterate through all the entities on the query
this.queries.moving.results.forEach(entity => {
var velocity = entity.getComponent(Velocity);
var position = entity.getMutableComponent(Position);
position.x += velocity.x * delta;
position.y += velocity.y * delta;

if (position.x > canvasWidth + SHAPE_HALF_SIZE)
position.x = -SHAPE_HALF_SIZE;
if (position.x < -SHAPE_HALF_SIZE)
position.x = canvasWidth + SHAPE_HALF_SIZE;
if (position.y > canvasHeight + SHAPE_HALF_SIZE)
position.y = -SHAPE_HALF_SIZE;
if (position.y < -SHAPE_HALF_SIZE)
position.y = canvasHeight + SHAPE_HALF_SIZE;
});
}
}

// Define a query of entities that have "Velocity" and "Position" components
MovableSystem.queries = {
moving: {
components: [Velocity, Position]
}
};

// RendererSystem
class RendererSystem extends System {
// This method will get called on every frame by default
execute(delta, time) {
ctx.globalAlpha = 1;
ctx.fillStyle = "#ffffff";
ctx.fillRect(0, 0, canvasWidth, canvasHeight);
//ctx.globalAlpha = 0.6;

// Iterate through all the entities on the query
this.queries.renderables.results.forEach(entity => {
var shape = entity.getComponent(Shape);
var position = entity.getComponent(Position);
if (shape.primitive === 'box') {
this.drawBox(position);
} else {
this.drawCircle(position);
}
});
}

drawCircle(position) {
ctx.fillStyle = "#888";
ctx.beginPath();
ctx.arc(position.x, position.y, SHAPE_HALF_SIZE, 0, 2 * Math.PI, false);
ctx.fill();
ctx.lineWidth = 1;
ctx.strokeStyle = "#222";
ctx.stroke();
}

drawBox(position) {
ctx.beginPath();
ctx.rect(
position.x - SHAPE_HALF_SIZE,
position.y - SHAPE_HALF_SIZE,
SHAPE_SIZE,
SHAPE_SIZE
);
ctx.fillStyle = "#f28d89";
ctx.fill();
ctx.lineWidth = 1;
ctx.strokeStyle = "#800904";
ctx.stroke();
}
}

// Define a query of entities that have "Renderable" and "Shape" components
RendererSystem.queries = {
renderables: { components: [Renderable, Shape] }
};

// Create world and register the systems on it
var world = new World();
world.registerSystem(MovableSystem).registerSystem(RendererSystem);

// Some helper functions when creating the components
function getRandomVelocity() {
return {
x: SPEED_MULTIPLIER * (2 * Math.random() - 1),
y: SPEED_MULTIPLIER * (2 * Math.random() - 1)
};
}

function getRandomPosition() {
return {
x: Math.random() * canvasWidth,
y: Math.random() * canvasHeight
};
}

function getRandomShape() {
return {
primitive: Math.random() >= 0.5 ? 'circle' : 'box'
};
}

for (let i = 0; i < NUM_ELEMENTS; i++) {
world
.createEntity()
.addComponent(Velocity, getRandomVelocity())
.addComponent(Shape, getRandomShape())
.addComponent(Position, getRandomPosition())
.addComponent(Renderable);
}

return world;
}
Insert cell
height = 500
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more