### ECSY Core Example
Source: https://github.com/ecsyjs/ecsy/blob/dev/site/docs/manual/Getting-started.md
This snippet demonstrates the fundamental concepts of ECSY, including defining components (Acceleration, Position), registering them with a World, creating entities, adding components to entities, registering systems (MovableSystem, PositionLogSystem), and running the main execution loop.
```javascript
import { World, System, Component, Types } from 'ecsy';
let world = new World();
class Acceleration extends Component {}
Acceleration.schema = {
value: { type: Types.Number, default: 0.1 }
};
class Position extends Component {}
Position.schema = {
x: { type: Types.Number },
y: { type: Types.Number },
z: { type: Types.Number }
};
world
.registerComponent(Acceleration)
.registerComponent(Position);
class PositionLogSystem extends System {
init() {}
execute(delta, time) {
this.queries.position.results.forEach(entity => {
let pos = entity.getComponent(Position);
console.log(`Entity with ID: ${entity.id} has component Position={x: ${pos.x}, y: ${pos.y}, z: ${pos.z}}`);
});
}
}
PositionLogSystem.queries = {
position: {
components: [Position]
}
}
class MovableSystem extends System {
init() {}
execute(delta, time) {
this.queries.moving.results.forEach(entity => {
let acceleration = entity.getComponent(Acceleration).value;
let position = entity.getMutableComponent(Position);
position.x += acceleration * delta;
position.y += acceleration * delta;
position.z += acceleration * delta;
});
}
}
MovableSystem.queries = {
moving: {
components: [Acceleration, Position]
}
}
world
.registerSystem(MovableSystem)
.registerSystem(PositionLogSystem)
world
.createEntity()
.addComponent(Position);
for (let i = 0; i < 10; i++) {
world
.createEntity()
.addComponent(Acceleration)
.addComponent(Position, { x: Math.random() * 10, y: Math.random() * 10, z: 0});
}
let lastTime = performance.now();
function run() {
let time = performance.now();
let delta = time - lastTime;
world.execute(delta, time);
lastTime = time;
requestAnimationFrame(run);
}
run();
```
--------------------------------
### PositionLogSystem Example
Source: https://github.com/ecsyjs/ecsy/blob/dev/site/docs/manual/Getting-started.md
Defines a system that logs the position of entities possessing a 'Position' component. It demonstrates the 'init' and 'execute' methods, and how to define a query for entities with specific components.
```javascript
class PositionLogSystem extends System {
init() { /* Do whatever you need here */ }
// This method will get called on every frame
execute(delta, time) {
// Iterate through all the entities on the query
this.queries.position.results.forEach(entity => {
// Access the component `Position` on the current entity
let pos = entity.getComponent(Position);
console.log(`Entity with ID: ${entity.id} has component Position={x: ${pos.x}, y: ${pos.y}, z: ${pos.z}}`);
});
}
}
// Define a query of entities that have the "Position" component
PositionLogSystem.queries = {
position: {
components: [Position]
}
}
```
--------------------------------
### Registering Systems with the World
Source: https://github.com/ecsyjs/ecsy/blob/dev/site/docs/manual/Getting-started.md
Shows the process of registering custom systems (MovableSystem and PositionLogSystem) with an ECSY world instance.
```javascript
world
.registerSystem(MovableSystem)
.registerSystem(PositionLogSystem);
```
--------------------------------
### Create ECSY World
Source: https://github.com/ecsyjs/ecsy/blob/dev/site/docs/manual/Getting-started.md
Initializes a new ECSY World instance, which acts as a container for entities, components, and systems.
```javascript
world = new World();
```
--------------------------------
### Manual Game Loop Execution
Source: https://github.com/ecsyjs/ecsy/blob/dev/site/docs/manual/Getting-started.md
Provides an example of a manual game loop using `requestAnimationFrame` to continuously execute ECSY systems, calculating delta time and passing it to the world's execute method.
```javascript
let lastTime = performance.now();
function run() {
// Compute delta and elapsed time
let time = performance.now();
let delta = time - lastTime;
// Run all the systems
world.execute(delta, time);
lastTime = time;
requestAnimationFrame(run);
}
run();
```
--------------------------------
### Multiple Queries in a System
Source: https://github.com/ecsyjs/ecsy/blob/dev/site/docs/manual/Getting-started.md
Demonstrates how a single system can define and process multiple, distinct queries for different sets of entities.
```javascript
class SystemDemo extends System {
execute() {
this.queries.boxes.results.forEach(entity => { /* do things */});
this.queries.balls.results.forEach(entity => { /* do things */});
}
}
SystemDemo.queries = {
boxes: { components: [Box] },
balls: { components: [Ball] },
};
```
--------------------------------
### MovableSystem Example
Source: https://github.com/ecsyjs/ecsy/blob/dev/site/docs/manual/Getting-started.md
Illustrates a system that modifies the position of entities based on their 'Acceleration' and 'Position' components. It shows how to access components as read-only and mutable, and defines a query for entities with both components.
```javascript
class MovableSystem extends System {
init() { /* Do whatever you need here */ }
// 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 => {
// Get the `Acceleration` component as Read-only
let acceleration = entity.getComponent(Acceleration).value;
// Get the `Position` component as Writable
let position = entity.getMutableComponent(Position);
position.x += acceleration * delta;
position.y += acceleration * delta;
position.z += acceleration * delta;
});
}
}
// Define a query of entities that have "Acceleration" and "Position" components
MovableSystem.queries = {
moving: {
components: [Acceleration, Position]
}
}
```
--------------------------------
### Create Entities and Add Components
Source: https://github.com/ecsyjs/ecsy/blob/dev/site/docs/manual/Getting-started.md
Shows how to create new entities in the ECSY world and attach components to them. Components can be added with default or custom initial data.
```javascript
var entityA = world
.createEntity()
.addComponent(Position);
for (let i = 0; i < 10; i++) {
world
.createEntity()
.addComponent(Acceleration)
.addComponent(Position, { x: Math.random() * 10, y: Math.random() * 10, z: 0});
}
```
--------------------------------
### Register Components with World
Source: https://github.com/ecsyjs/ecsy/blob/dev/site/docs/manual/Getting-started.md
Registers previously defined components with the ECSY world, making them available for use when creating entities.
```javascript
world
.registerComponent(Acceleration)
.registerComponent(Position);
```
--------------------------------
### ECSY Example Navigation and Display
Source: https://github.com/ecsyjs/ecsy/blob/dev/site/examples/index.html
Handles the dynamic creation of an example list, including links to demos and source code. It manages the selection of examples, updates the viewer iframe, and handles hash changes in the URL for deep linking.
```javascript
const repoUrl = "https://github.com/MozillaReality/ecsy/blob/master/site/examples/";
let examples = [
{name: "Circles and boxes", url: "circles-boxes/index.html"},
{name: "Intersecting circles", url: "canvas/index.html"},
{name: "Three.js ball", url: "ball-example/three/index.html"},
{name: "Babylon.js ball", url: "ball-example/babylon/index.html"},
{name: "Factory", url: "factory/index.html"},
{name: "System State Components", url: "systemstatecomponents/index.html"}
];
let hash = window.location.hash;
let selectedIndex = -1;
if(hash){
selectedIndex = examples.findIndex(example => "#"+encodeURI(example.name) == hash);
}
let container = document.getElementsByClassName("content")[0];
let selectedLine = null;
//make example list with this template
//Name (source)
for(let i=0; i= 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);
}
```
--------------------------------
### ECSY World Setup and Registration
Source: https://github.com/ecsyjs/ecsy/blob/dev/site/examples/ball-example/three/index.html
Initializes an ECSY world and registers various components and systems. Components define data, and systems define logic that operates on entities with specific components.
```javascript
import {World} from '../../../build/ecsy.module.js';
import {Object3D, Collidable, Collider, Recovering, Moving, PulsatingScale, Timeout, PulsatingColor, Colliding, Rotating} from './components.mjs';
import {RotatingSystem, ColliderSystem, PulsatingColorSystem, PulsatingScaleSystem, MovingSystem,TimeoutSystem} from './systems.mjs';
var world = new World();
world
.registerComponent(Object3D)
.registerComponent(Collidable)
.registerComponent(Collider)
.registerComponent(Recovering)
.registerComponent(Moving)
.registerComponent(PulsatingScale)
.registerComponent(Timeout)
.registerComponent(PulsatingColor)
.registerComponent(Colliding)
.registerComponent(Rotating);
world
.registerSystem(RotatingSystem)
.registerSystem(PulsatingColorSystem)
.registerSystem(PulsatingScaleSystem)
.registerSystem(TimeoutSystem)
.registerSystem(ColliderSystem)
.registerSystem(MovingSystem);
```
--------------------------------
### ECSY.js NPC Management Example
Source: https://github.com/ecsyjs/ecsy/blob/dev/site/examples/factory/index.html
Core ECSY.js setup for managing NPC entities. Defines components (NPC, Name, Tshirt) and systems (NameSystem, TshirtSystem) to automatically add properties to new NPCs. Includes event listeners for UI interactions to create NPCs and remove their name or t-shirt components.
```javascript
import {World, System, Component, TagComponent, Not, Types} from '../../build/ecsy.module.js';
const names = [
'Isabel',
'Scot',
'Francene',
'Robert',
'Kizzie',
'Leroy',
'Layla',
'Stella',
'Marianela',
'Devorah'
];
const sizes = [
'XS',
'S',
'M',
'L',
'XL'
];
const colors = [
'white',
'red',
'yellow',
'purple',
'pink',
'blue',
'cyan',
'black'
];
// Components
class NPC extends TagComponent {}
class Name extends Component {}
Name.schema = {
value: { type: Types.String }
};
class Tshirt extends Component {}
Tshirt.schema = {
color: { type: Types.String, default: 'white' },
size: { type: Types.String, default: 'XL' }
};
// Systems
class NameSystem extends System {
execute() {
this.queries.entities.results.forEach(entity => {
var name = randomFromArray(names);
log(`Added name '${name}' to player id=${entity.id}`);
entity.addComponent(Name, {value: name});
});
}
}
NameSystem.queries = {
entities: {
components: [NPC, Not(Name)]
}
};
class TshirtSystem extends System {
execute() {
this.queries.entities.results.forEach(entity => {
var size = randomFromArray(sizes);
var color = randomFromArray(colors);
log(`Added '${color}' '${size}' tshirt to player id=${entity.id}`);
entity.addComponent(Tshirt, {color: color, size: size});
});
}
}
TshirtSystem.queries = {
entities: {
components: [NPC, Not(Tshirt)]
}
};
// Initialize our world
var world = new World();
world
.registerComponent(NPC)
.registerComponent(Tshirt)
.registerComponent(Name)
.registerSystem(NameSystem)
.registerSystem(TshirtSystem);
// HTML Code to interact with the world
document.getElementById('createNPC').addEventListener('click', () => {
var npc = world.createEntity();
npc.addComponent(NPC);
log(`> Created NPC, (id = ${npc.id})`);
});
document.getElementById('removeName').addEventListener('click', () => {
var entity = randomEntity();
if (!entity) return;
log(`> Removing name '${entity.getComponent(Name).value}' from player id=${entity.id}`);
entity.removeComponent(Name);
});
document.getElementById('removeTshirt').addEventListener('click', () => {
var entity = randomEntity();
if (!entity) return;
var tshirt = entity.getComponent(Tshirt);
log(`> Removing '${tshirt.color}' '${tshirt.size}' from player id=${entity.id}`);
entity.removeComponent(Tshirt);
});
// Utils
function randomEntity() {
return randomFromArray(world.entityManager._entities);
}
function randomFromArray(array) {
var idx = Math.floor(Math.random() * array.length);
return array[idx];
}
function animate() {
var time = performance.now();
var delta = time - lastTime;
world.execute(delta, time);
lastTime = time;
requestAnimationFrame(animate);
}
var lastTime = performance.now();
animate();
window.world = world;
```
--------------------------------
### ECSY Core Example
Source: https://github.com/ecsyjs/ecsy/blob/dev/README.md
A complete example demonstrating ECSY's Entity-Component-System architecture. It includes defining components like Velocity, Position, Shape, and Renderable, implementing systems for movement and rendering, and setting up the ECSY world to manage entities.
```javascript
import { World, System, Component, TagComponent, Types } from "https://ecsyjs.github.io/ecsy/build/ecsy.module.js";
const NUM_ELEMENTS = 50;
const SPEED_MULTIPLIER = 0.3;
const SHAPE_SIZE = 50;
const SHAPE_HALF_SIZE = SHAPE_SIZE / 2;
// Initialize canvas
let canvas = document.querySelector("canvas");
let canvasWidth = canvas.width = window.innerWidth;
let canvasHeight = canvas.height = window.innerHeight;
let ctx = canvas.getContext("2d");
//----------------------
// Components
//----------------------
// Velocity component
class Velocity extends Component {}
Velocity.schema = {
x: { type: Types.Number },
y: { type: Types.Number }
};
// Position component
class Position extends Component {}
Position.schema = {
x: { type: Types.Number },
y: { type: Types.Number }
};
// Shape component
class Shape extends Component {}
Shape.schema = {
primitive: { type: Types.String, default: '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.fillStyle = "#d4d4d4";
ctx.fillRect(0, 0, canvasWidth, canvasHeight);
// 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.beginPath();
ctx.arc(position.x, position.y, SHAPE_HALF_SIZE, 0, 2 * Math.PI, false);
ctx.fillStyle= "#39c495";
ctx.fill();
ctx.lineWidth = 2;
ctx.strokeStyle = "#0b845b";
ctx.stroke();
}
drawBox(position) {
ctx.beginPath();
ctx.rect(position.x - SHAPE_HALF_SIZE, position.y - SHAPE_HALF_SIZE, SHAPE_SIZE, SHAPE_SIZE);
ctx.fillStyle= "#e2736e";
ctx.fill();
ctx.lineWidth = 2;
ctx.strokeStyle = "#b74843";
ctx.stroke();
}
}
// Define a query of entities that have "Renderable" and "Shape" components
RendererSystem.queries = {
renderables: { components: [Renderable, Shape] }
}
// Create world and register the components and systems on it
var world = new World();
world
.registerComponent(Velocity)
.registerComponent(Position)
.registerComponent(Shape)
.registerComponent(Renderable)
.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
};
}
// Create entities
for (let i = 0; i < NUM_ELEMENTS; i++) {
let entity = world.createEntity();
entity
.addComponent(Velocity, getRandomVelocity())
.addComponent(Position, getRandomPosition())
.addComponent(Shape, { primitive: Math.random() > 0.5 ? 'box' : 'circle' })
.addComponent(Renderable);
}
// Start the game loop
world.play();
// Resize canvas on window resize
window.addEventListener("resize", () => {
canvasWidth = canvas.width = window.innerWidth;
canvasHeight = canvas.height = window.innerHeight;
});
```
--------------------------------
### ECSY World Initialization and System Registration
Source: https://github.com/ecsyjs/ecsy/blob/dev/site/examples/ball-example/babylon/index.html
This snippet shows how to initialize an ECSY World and register various components and systems. It sets up the core structure for the entity-component-system architecture.
```javascript
import {World} from '../../../build/ecsy.module.js'; import {Object3D, Collisionable, Collider, Recovering, Moving, PulsatingScale, Timeout, PulsatingColor, Colliding, Rotating} from './components.mjs'; import {RotatingSystem, ColliderSystem, PulsatingColorSystem, PulsatingScaleSystem, MovingSystem,TimeoutSystem} from './systems.mjs'; var world = new World(); world \
.registerComponent(Object3D) \
.registerComponent(Collisionable) \
.registerComponent(Collider) \
.registerComponent(Recovering) \
.registerComponent(Moving) \
.registerComponent(PulsatingScale) \
.registerComponent(Timeout) \
.registerComponent(PulsatingColor) \
.registerComponent(Colliding) \
.registerComponent(Rotating) \
.registerSystem(RotatingSystem) \
.registerSystem(PulsatingColorSystem) \
.registerSystem(PulsatingScaleSystem) \
.registerSystem(TimeoutSystem) \
.registerSystem(ColliderSystem) \
.registerSystem(MovingSystem);
```
--------------------------------
### Install ECSY via npm
Source: https://github.com/ecsyjs/ecsy/blob/dev/README.md
This command installs the ECSY library as a project dependency using npm.
```bash
npm install --save ecsy
```
--------------------------------
### ECSY Articles and Introductions
Source: https://github.com/ecsyjs/ecsy/blob/dev/AWESOME_ECSY.md
Articles and blog posts providing insights into ECSY, including introductions to the framework and guides on using its developer tools.
```markdown
https://blog.mozvr.com/ecsy-developer-tools/
https://blog.mozvr.com/introducing-ecsy/
```
--------------------------------
### Babylon.js Scene Initialization and Rendering Loop
Source: https://github.com/ecsyjs/ecsy/blob/dev/site/examples/ball-example/babylon/index.html
This snippet details the initialization of a Babylon.js scene, including camera, light, and mesh creation. It also sets up the render loop to update the scene and execute ECSY systems.
```javascript
var camera, scene, renderer, parent; var mesh; init(); function randomSpherePoint(radius) { var u = Math.random(); var v = Math.random(); var theta = 2 * Math.PI * u; var phi = Math.acos(2 * v - 1); var x = radius * Math.sin(phi) * Math.cos(theta); var y = radius * Math.sin(phi) * Math.sin(theta); var z = radius * Math.cos(phi); return {x,y,z}; } var objMoving, states; function init() { var numObjects = 10000; var size = 0.2; var w = 100; var canvas = document.getElementById('renderCanvas'); var engine = new BABYLON.Engine(canvas, true, {preserveDrawingBuffer: true, stencil: true}); scene = new BABYLON.Scene(engine); var camera = new BABYLON.FreeCamera('camera', new BABYLON.Vector3(0, 5,-20), scene); camera.setTarget(BABYLON.Vector3.Zero()); camera.attachControl(canvas, false); var light = new BABYLON.HemisphericLight('light1', new BABYLON.Vector3(0,1,0), scene); objMoving = BABYLON.MeshBuilder.CreateIcoSphere('sphere',{subdivisions: 1}, scene); var material = new BABYLON.StandardMaterial(); material.diffuseColor.set(1,1,0); objMoving.material = material; var radius = 10; var entity = world.entityManager.createEntity(); entity.addComponent(Collider); entity.addComponent(Object3D, {object: objMoving}); entity.addComponent(Rotating, {rotatingSpeed: 0.5}); objMoving.setPivotMatrix(BABYLON.Matrix.Translation(0, 0, radius), false); states = []; var rootMesh = BABYLON.MeshBuilder.CreateBox('box',{size: size}, scene); var material = new BABYLON.StandardMaterial("material", scene); material.diffuseColor = new BABYLON.Color3(1, 1, 1); rootMesh.material = material; rootMesh.setEnabled(false); rootMesh.registerInstancedBuffer("color", 4); rootMesh.instancedBuffers.color = new BABYLON.Color4(1, 0, 0, 1); for (var i = 0;i < numObjects; i++) { var entity = world.entityManager.createEntity(); var mesh = rootMesh.createInstance('box'); mesh.instancedBuffers.color = new BABYLON.Color4(1, 0, 0, 1); mesh.alwaysSelectAsActiveMesh = true; mesh.position.copyFrom(randomSpherePoint(radius)); var state = { mesh: mesh, colliding: false, rotationSpeed: 0, originalColor: new BABYLON.Color4(1, 0, 0, 1), tmpColor: new BABYLON.Color4() }; states.push(state); entity.addComponent(Object3D, {object: mesh}); entity.addComponent(PulsatingColor, {offset: i}); entity.addComponent(PulsatingScale, {offset: i}); if (Math.random() > 0.5) { entity.addComponent(Moving, {offset: i}); } entity.addComponent(Collisionable); } scene.freezeActiveMeshes(); window.addEventListener( 'resize', () => engine.resize()); var lastTime = performance.now(); engine.runRenderLoop(function() { var time = performance.now() / 1000; var delta = time-lastTime; lastTime = time; scene.render(); world.execute(delta, time); }); }
```
--------------------------------
### HTML Structure for ECSY Example
Source: https://github.com/ecsyjs/ecsy/blob/dev/README.md
Basic HTML5 document structure required to run the ECSY JavaScript example. It includes a canvas element for rendering and meta tags for viewport configuration.
```html
Hello!
```
--------------------------------
### Example Query with Components
Source: https://github.com/ecsyjs/ecsy/blob/dev/site/docs/manual/Architecture.md
An example of defining a query named 'positions' that selects entities possessing both 'Position' and 'Velocity' components.
```javascript
var query = {
positions: {
components: [ Position, Velocity ]
}
};
```
--------------------------------
### Canvas Initialization and Resize Handling
Source: https://github.com/ecsyjs/ecsy/blob/dev/site/examples/circles-boxes/index.html
Initializes the HTML canvas element, sets its dimensions, and adds an event listener to handle window resizing, updating canvas dimensions accordingly.
```javascript
let canvas = document.querySelector("canvas");
let canvasWidth = canvas.width = window.innerWidth;
let canvasHeight = canvas.height = window.innerHeight;
let ctx = canvas.getContext("2d");
window.addEventListener( 'resize', () => {
canvasWidth = canvas.width = window.innerWidth;
canvasHeight = canvas.height = window.innerHeight;
}, false );
```
--------------------------------
### Geometry System Example
Source: https://github.com/ecsyjs/ecsy/blob/dev/site/docs/manual/Architecture.md
An example of a system that manages geometry resources. It uses queries to detect added entities with `Geometry` but without `StateComponentGeometry`, entities to be removed (lacking `Geometry` but having `StateComponentGeometry`), and normal entities with both components.
```javascript
class GeometrySystem extends System {
init() {
return {
queries: {
added: { components: [Geometry, Not(StateComponentGeometry)] },
remove: { components: [Not(Geometry), StateComponentGeometry] },
normal: { components: [Geometry, StateComponentGeometry] },
}
};
},
execute() {
added.forEach(entity => {
var mesh = new Mesh(entity.getComponent(Geometry).primitive);
entity.addComponent(StateComponentGeometry, {mesh: mesh});
});
remove.forEach(entity => {
var component = entity.getComponent(StateComponentGeometry);
// free resources for the mesh
component.mesh.dispose();
entity.removeComponent(StateComponentGeometry);
});
normal.forEach(entity => {
// use entity and its components (Geometry and StateComponentGeometry) if needed
});
}
}
```
--------------------------------
### ECSY Animation Loop
Source: https://github.com/ecsyjs/ecsy/blob/dev/site/examples/circles-boxes/index.html
Sets up the main animation loop using `requestAnimationFrame` to continuously execute ECSY systems and update the canvas.
```javascript
function run() {
var time = performance.now();
var delta = time - lastTime;
world.execute(delta, time);
lastTime = time;
requestAnimationFrame(run);
}
var lastTime = performance.now();
run();
```
--------------------------------
### ECSY Update Loop and Resize Handling
Source: https://github.com/ecsyjs/ecsy/blob/dev/site/examples/canvas/index.html
This snippet sets up the main update loop using `requestAnimationFrame` to execute ECSY systems and handles window resizing to adjust the canvas dimensions and update the CanvasContext component.
```javascript
window.world = world;
window.Circle = Circle;
window.Movement = Movement;
window.addEventListener(
'resize',
() => {
canvasComponent.width = canvas.width = window.innerWidth;
canvasComponent.height = canvas.height = window.innerHeight;
},
false
);
var lastTime = performance.now();
function update() {
var time = performance.now();
var delta = time - lastTime;
lastTime = time;
world.execute(delta);
requestAnimationFrame(update);
}
update();
```
--------------------------------
### ECSY Components and Systems
Source: https://github.com/ecsyjs/ecsy/blob/dev/site/examples/circles-boxes/index.html
Defines the core components (Velocity, Position, Shape, Renderable) and systems (MovableSystem, RendererSystem) for the ECSY framework. Components define data, and systems define logic that operates on entities with specific component combinations.
```javascript
import { World, System, Component, TagComponent, Types } from '../../build/ecsy.module.js';
// Components
class Velocity extends Component {}
Velocity.schema = {
x: { type: Types.Number },
y: { type: Types.Number }
};
class Position extends Component {}
Position.schema = {
x: { type: Types.Number },
y: { type: Types.Number }
};
class Shape extends Component {}
Shape.schema = {
primitive: { type: Types.String, default: 'box' }
};
class Renderable extends TagComponent {}
// Systems
class MovableSystem extends System {
execute(delta, time) {
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;
});
}
}
MovableSystem.queries = {
moving: { components: [Velocity, Position] }
}
class RendererSystem extends System {
execute(delta, time) {
ctx.globalAlpha = 1;
ctx.fillStyle = "#ffffff";
ctx.fillRect(0, 0, canvasWidth, canvasHeight);
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();
}
}
RendererSystem.queries = {
renderables: { components: [Renderable, Shape] }
}
```
--------------------------------
### 3D Scene Initialization with Three.js and ECSY
Source: https://github.com/ecsyjs/ecsy/blob/dev/site/examples/ball-example/three/index.html
Sets up the Three.js scene, camera, renderer, and lights. It then creates multiple entities with ECSY components, attaching Three.js objects to them and applying various visual effects like pulsating color and scale.
```javascript
var camera, scene, renderer, parent;
var clock = new THREE.Clock();
init();
function randomSpherePoint(radius) {
var u = Math.random();
var v = Math.random();
var theta = 2 * Math.PI * u;
var phi = Math.acos(2 * v - 1);
var x = radius * Math.sin(phi) * Math.cos(theta);
var y = radius * Math.sin(phi) * Math.sin(theta);
var z = radius * Math.cos(phi);
return new THREE.Vector3(x, y, z);
}
var objMoving, states;
function init() {
var numObjects = 10000;
var size = 0.2;
var w = 100;
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(
80,
window.innerWidth / window.innerHeight,
0.005,
10000
);
camera.position.z = 20;
parent = new THREE.Object3D();
var geometry = new THREE.IcosahedronGeometry(1);
var material = new THREE.MeshStandardMaterial({ color: '#ff0' });
var parent2 = new THREE.Object3D();
objMoving = new THREE.Mesh(geometry, material);
objMoving.position.set(0, 0, 0);
var radius = 10;
var entity = world.createEntity();
objMoving.position.set(0, 0, radius);
entity.addComponent(Collider);
entity.addComponent(Object3D, { object: objMoving });
entity = world.createEntity();
parent2.add(objMoving);
entity.addComponent(Rotating, { rotatingSpeed: 0.5 }).addComponent(Object3D, { object: parent2 });
parent.add(parent2);
states = [];
var ambientLight = new THREE.AmbientLight(0xcccccc);
scene.add(ambientLight);
var directionalLight = new THREE.DirectionalLight(0xffffff, 2);
directionalLight.position.set(1, 1, 0.5).normalize();
scene.add(directionalLight);
var geometry = new THREE.BoxBufferGeometry(size, size, size);
for (var i = 0; i < numObjects; i++) {
var material = new THREE.MeshStandardMaterial({ color: new THREE.Color(1, 0, 0) });
var mesh = new THREE.Mesh(geometry, material);
mesh.position.copy(randomSpherePoint(radius));
var state = {
mesh: mesh,
colliding: false,
rotationSpeed: 0,
originalColor: material.color.clone(),
tmpColor: new THREE.Color(),
};
states.push(state);
var entity = world.createEntity();
entity.addComponent(Object3D, { object: mesh });
entity.addComponent(PulsatingColor, { offset: i });
entity.addComponent(PulsatingScale, { offset: i });
if (Math.random() > 0.5) {
entity.addComponent(Moving, { offset: i });
}
entity.addComponent(Collidable);
parent.add(mesh);
}
scene.add(parent);
renderer = new THREE.WebGLRenderer();
renderer.setClearColor(0x333333);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// window.addEventListener( 'resize', onWindowResize, false );
renderer.setAnimationLoop(animate);
}
```
--------------------------------
### ECSY World Initialization and Component/System Registration
Source: https://github.com/ecsyjs/ecsy/blob/dev/site/examples/systemstatecomponents/index.html
Initializes an ECSY World, registers the Sprite and SpriteResources components, and registers the MainSystem. This sets up the core ECSY environment for managing entities and systems.
```javascript
var world = new World();
world
.registerComponent(Sprite)
.registerComponent(SpriteResources)
.registerSystem(MainSystem);
```
--------------------------------
### Run ECSY Benchmarks
Source: https://github.com/ecsyjs/ecsy/blob/dev/site/docs/manual/Architecture.md
Instructions on how to execute ECSY's performance benchmarks locally. Running `npm run benchmarks` will generate a JSON file with benchmark results, which can then be used with the `benchmarker-js` tool for comparing performance across different executions or branches.
```bash
# Install benchmarker globally
> npm install -g benchmarker-js
> benchmarker compare results1.json results2.json
```
--------------------------------
### ECSY World Initialization
Source: https://github.com/ecsyjs/ecsy/blob/dev/site/docs/manual/Architecture.md
Demonstrates how to create a new ECSY World instance. The World acts as a container for entities, components, and systems. It can be initialized with an options object to configure entity pooling.
```javascript
world = new World();
```
--------------------------------
### Query with 'Not' Operator
Source: https://github.com/ecsyjs/ecsy/blob/dev/site/docs/manual/Architecture.md
Demonstrates using the 'Not' operator within a query definition to exclude entities that have a specific component. This example selects entities with 'Enemy' but without 'Dead'.
```javascript
SystemTest.queries = {
activeEnemies: {
components: [ Enemy, Not(Dead) ]
}
};
```
--------------------------------
### Using 'Not' Operator for Initialization
Source: https://github.com/ecsyjs/ecsy/blob/dev/site/docs/manual/Architecture.md
An example showcasing the 'Not' operator for identifying entities that require initialization. It selects players without a 'Name' component, allowing a system to assign names.
```javascript
SystemTest.queries = {
playerUninitialized: {
components: [ Player, Not(Name) ]
}
};
queries.playerUninitialized.results.forEach(entity => {
entity.addComponent(Name, {value: getRandomName()});
});
```
--------------------------------
### ECSY Boilerplates and Starters
Source: https://github.com/ecsyjs/ecsy/blob/dev/AWESOME_ECSY.md
Boilerplate projects to quickly set up new ECSY projects with different build tools and language configurations, including Webpack, TypeScript, PixiJS, and React.
```javascript
https://github.com/MozillaReality/ecsy-webpack-boilerplate
https://github.com/MozillaReality/ecsy-three-webpack
https://github.com/robertlong/ecsy-typescript-boilerplate
https://github.com/manueldeval/ecsypixi
https://github.com/jmswrnr/react-ecs
```
--------------------------------
### Custom ColorArray Component
Source: https://github.com/ecsyjs/ecsy/blob/dev/site/docs/manual/Architecture.md
Example of a custom ECSY component that overrides constructor, copy, and reset methods for managing an array of color objects. Demonstrates manual data copying and resetting to optimize performance.
```javascript
class ColorArray extends Component {
/**
* The constructor should set the initial values for a component.
* Override this method to set your own initial values.
**/
constructor(props) {
// Pass false to disable using the schema for default values.
super(false);
// Set your own default values instead
this.value = [];
}
/**
* The copy method is used when copying properties from one component to another.
* Copy is used when copying/cloning entities/components, it is not used in component pooling.
* You can re-implement this method to increase performance or deal with complex data structures.
**/
copy(src) {
this.value.length = src.value.length;
for (let i = 0; i < src.value.length; i++) {
const srcColor = src.value[i];
const destColor = this.value[i];
destColor.r = srcColor.r;
destColor.g = srcColor.g;
destColor.b = srcColor.b;
}
return this;
}
/**
* Clone returns a new, identical instance of a component.
* We don't need to override clone in this case. However, if you needed to pass an argument
* to the constructor, you could override clone to do so.
*
* clone() {
* return new this.constructor().copy(this);
* }
**/
/**
* The reset method is used to reset the component back to it's initial state.
* It's used in component pools when a component is disposed. It can be called fairly often so it is a common method
* to optimize when you are adding/removing a lot of this type of component. You'll want to avoid memory allocation
* as much as possible in the reset method. Try to reuse existing data structures whenever possible.
**/
reset() {
this.value.forEach(color => {
color.r = 0;
color.g = 0;
color.b = 0;
});
}
}
```
--------------------------------
### Basic HTML Structure
Source: https://github.com/ecsyjs/ecsy/blob/dev/site/examples/ball-example/three/index.html
The HTML document sets up basic styling for the body, including margin, background color, and text color, to create a dark canvas for the 3D rendering.
```html
body {
margin: 0px;
background-color: #000000;
overflow: hidden;
color: #fff;
}
```