### 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; } ```