Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

ecsy

Package Overview
Dependencies
Maintainers
1
Versions
19
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

ecsy - npm Package Compare versions

Comparing version 0.0.1 to 0.1.0

26

examples/canvas/systems.js

@@ -8,3 +8,5 @@ import { System } from "../../build/ecsy.module.js";

return {
entities: [Circle, Movement]
queries: {
entities: { components: [Circle, Movement] }
}
};

@@ -36,6 +38,2 @@ }

var dx = circle.position.x;
var dy = circle.position.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (circle.position.y + circle.radius < 0)

@@ -59,8 +57,9 @@ circle.position.y = canvasHeight + circle.radius;

return {
entities: [Circle]
queries: {
entities: { components: [Circle] }
}
};
}
execute(delta) {
var ctx = this.world.components.canvasContext.ctx;
execute() {
let entities = this.queries.entities;

@@ -90,2 +89,5 @@

}
if (entity.hasComponent(Intersecting) && entity.getComponent(Intersecting).points.length === 0) {
entity.removeComponent(Intersecting);
}
}

@@ -111,8 +113,10 @@ }

return {
circles: [Circle],
intersectingCircles: [Intersecting]
queries: {
circles: { components: [Circle] },
intersectingCircles: { components: [Intersecting] }
}
};
}
execute(delta) {
execute() {
let canvasComponent = this.world.components.canvasContext;

@@ -119,0 +123,0 @@ let ctx = canvasComponent.ctx;

{
"name": "ecsy",
"version": "0.0.1",
"version": "0.1.0",
"description": "Entity Component System in JS",

@@ -10,3 +10,3 @@ "main": "build/ecsy.js",

"build": "rollup -c",
"dev": "concurrently --names \"ROLLUP,HTTP\" -c \"bgBlue.bold,bgGreen.bold\" \"rollup -c -w -m inline\" \"http-server -c-1 -p 8080\"",
"dev": "concurrently --names \"ROLLUP,HTTP\" -c \"bgBlue.bold,bgGreen.bold\" \"rollup -c -w -m inline\" \"http-server -c-1 -p 8080 --cors\"",
"lint": "eslint src",

@@ -13,0 +13,0 @@ "start": "npm run dev",

@@ -22,7 +22,7 @@ # ecsy

- Reactive support:
- Support for reactive systems (React to changes on entities and components)
- Support for reactive behaviour on systems (React to changes on entities and components)
- System can query mutable or immutable components
- Predictable:
- Systems will run on the order they were registered
- Reactive events will not generate a random callback when emited but queued
- Systems will run on the order they were registered or based on the priority defined when registering them
- Reactive events will not generate a random callback when emited but queued and be processed in order
- Modern Javascript: ES6, classes, modules,...

@@ -72,3 +72,5 @@ - Pool for components and entities

return {
entities: [Rotating, Transform]
queries: {
entities: { components: [Rotating, Transform] }
}
};

@@ -109,18 +111,36 @@ }

return {
entities: [Transform]
queries: {
entities: {
components: [Rotating, Transform]
events: {
added: {
event: "EntityAdded"
},
removed: {
event: "EntityRemoved"
},
changed: {
event: "EntityChanged"
},
rotatingChanged: {
event: "ComponentChanged",
components: [Rotating]
},
transformChanged: {
event: "ComponentChanged",
components: [Transform]
}
}
}
}
};
}
onEntitiesAdded(entities) {
console.log('OnAdded', this.queries.entities.added);
execute() {
console.log('OnAdded', this.events.entities.added);
console.log('OnRemoved', this.events.entities.removed);
console.log('OnChanged entities', this.events.entities.changed);
console.log('OnChanged Rotating Component', this.events.entities.rotatingChanged);
console.log('OnChanged Transform Component', this.events.entities.transformChanged);
}
onEntitiesRemoved(entities) {
console.log('OnRemoved', this.queries.entities.removed);
}
onEntitiesChanged(entities) {
console.log('OnChanged entities', this.queries.entities.changed);
}
onComponentChanged(entities, component) {
console.log('OnChanged components', entities, component);
}
}

@@ -127,0 +147,0 @@

@@ -56,4 +56,8 @@ import Entity from "./Entity.js";

if (values) {
for (var name in values) {
component[name] = values[name];
if (component.copy) {
component.copy(values);
} else {
for (var name in values) {
component[name] = values[name];
}
}

@@ -60,0 +64,0 @@ }

export { World } from "./World.js";
export { System } from "./System.js";
export { ReactiveSystem } from "./ReactiveSystem.js";
export { SchemaTypes } from "./SchemaTypes.js";

@@ -16,3 +16,3 @@ import EventDispatcher from "./EventDispatcher.js";

// This query is being used by a ReactiveSystem
// This query is being used by a reactive system
this.reactive = false;

@@ -19,0 +19,0 @@

/**
* @class System
*/
import Query from "./Query.js";
export class System {
toJSON() {
var json = {
name: this.constructor.name,
enabled: this.enabled,
executeTime: this.executeTime,
priority: this.priority,
queries: {},
events: {}
};
if (this.config) {
var queries = this.config.queries;
for (let queryName in queries) {
let query = queries[queryName];
json.queries[queryName] = {
key: this._queries[queryName].key
};
if (query.events) {
let events = (json.queries[queryName]["events"] = {});
for (let eventName in query.events) {
let event = query.events[eventName];
events[eventName] = {
eventName: event.event,
numEntities: this.events[queryName][eventName].length
};
if (event.components) {
events[eventName].components = event.components.map(c => c.name);
}
}
}
}
let events = this.config.events;
for (let eventName in events) {
json.events[eventName] = {
eventName: events[eventName]
};
}
}
return json;
}
constructor(world, attributes) {
this.world = world;
this.enabled = true;
this.queryComponents = this.init ? this.init() : null;
// @todo Better naming :)
this._queries = {};
this.queries = {};
this._events = {};
this.events = {};
this.priority = 0;
if (attributes) {
if (attributes.priority) {
this.priority = attributes.priority;
// Used for stats
this.executeTime = 0;
if (attributes && attributes.priority) {
this.priority = attributes.priority;
}
this.config = this.init ? this.init() : null;
if (!this.config) return;
if (this.config.queries) {
for (var name in this.config.queries) {
var queryConfig = this.config.queries[name];
var Components = queryConfig.components;
if (!Components || Components.length === 0) {
throw new Error("'components' attribute can't be empty in a query");
}
var query = this.world.entityManager.queryComponents(Components);
this._queries[name] = query;
this.queries[name] = query.entities;
if (queryConfig.events) {
this.events[name] = {};
let events = this.events[name];
for (let eventName in queryConfig.events) {
let event = queryConfig.events[eventName];
events[eventName] = [];
const eventMapping = {
EntityAdded: Query.prototype.ENTITY_ADDED,
EntityRemoved: Query.prototype.ENTITY_REMOVED,
EntityChanged: Query.prototype.ENTITY_CHANGED
};
if (eventMapping[event.event]) {
query.eventDispatcher.addEventListener(
eventMapping[event.event],
entity => {
events[eventName].push(entity);
}
);
} else if (event.event === "ComponentChanged") {
query.reactive = true;
query.eventDispatcher.addEventListener(
Query.prototype.COMPONENT_CHANGED,
(entity, component) => {
if (event.components.indexOf(component.constructor) !== -1) {
events[eventName].push(entity);
}
}
);
}
}
}
}
}
for (var name in this.queryComponents) {
var Components = this.queryComponents[name];
var query = this.world.entityManager.queryComponents(Components);
this._queries[name] = query;
this.queries[name] = query.entities;
if (this.config.events) {
for (let name in this.config.events) {
var event = this.config.events[name];
this.events[name] = [];
this.world.addEventListener(event, data => {
this.events[name].push(data);
});
}
}

@@ -34,2 +138,15 @@ }

}
clearEvents() {
for (var name in this.events) {
var event = this.events[name];
if (Array.isArray(event)) {
this.events[name].length = 0;
} else {
for (name in event) {
event[name].length = 0;
}
}
}
}
}

@@ -1,3 +0,1 @@

import { ReactiveSystem } from "./ReactiveSystem.js";
/**

@@ -47,24 +45,10 @@ * @class SystemManager

if (system.enabled) {
if (system instanceof ReactiveSystem) {
if (system.onEntitiesAdded && system.counters.added) {
system.onEntitiesAdded();
}
if (system.onEntitiesRemoved && system.counters.removed) {
system.onEntitiesRemoved();
}
if (system.onEntitiesChanged && system.counters.changed) {
system.onEntitiesChanged();
}
} else if (system.execute) {
if (system.execute) {
let startTime = performance.now();
system.execute(delta, time);
system.executeTime = performance.now() - startTime;
}
system.clearEvents();
}
});
this.systems.forEach(system => {
system = this.systems[name];
if (system instanceof ReactiveSystem) {
system.clearQueries();
}
});
}

@@ -71,0 +55,0 @@

@@ -5,2 +5,3 @@ import { SystemManager } from "./SystemManager.js";

import { componentPropertyName } from "./Utils.js";
import EventDispatcher from "./EventDispatcher.js";

@@ -18,4 +19,22 @@ /**

this.components = {};
this.eventQueues = {};
this.eventDispatcher = new EventDispatcher();
var event = new CustomEvent("ecsy-world-created", { detail: this });
window.dispatchEvent(event);
}
emitEvent(eventName, data) {
this.eventDispatcher.dispatchEvent(eventName, data);
}
addEventListener(eventName, callback) {
this.eventDispatcher.addEventListener(eventName, callback);
}
removeEventListener(eventName, callback) {
this.eventDispatcher.removeEventListener(eventName, callback);
}
/**

@@ -22,0 +41,0 @@ * Register a singleton component

@@ -5,17 +5,12 @@ import test from "ava";

class SystemA extends System {}
class SystemB extends System {}
class SystemC extends System {}
class SystemD extends System {}
class SystemE extends System {}
/**
* TODO
* - IDs
* - tags
*/
test("init", t => {
var world = new World();
class SystemA extends System {}
class SystemB extends System {}
class SystemC extends System {}
class SystemD extends System {}
class SystemE extends System {}
// Register empty system
world

@@ -49,3 +44,3 @@ .registerSystem(SystemA)

);
/*
/*
world = new World();

@@ -66,2 +61,256 @@ world

*/
world.execute();
});
test("empty_queries", t => {
var world = new World();
class SystemEmpty0 extends System {}
class SystemEmpty1 extends System {
init() {}
}
class SystemEmpty2 extends System {
init() {
return {};
}
}
class SystemEmpty3 extends System {
init() {
return { queries: {} };
}
}
class SystemEmpty4 extends System {
init() {
return {
queries: {
entities: {}
}
};
}
}
class SystemEmpty5 extends System {
init() {
return {
queries: {
entities: { components: [] }
}
};
}
}
// Register empty system
world
.registerSystem(SystemEmpty0)
.registerSystem(SystemEmpty1)
.registerSystem(SystemEmpty2)
.registerSystem(SystemEmpty3);
t.deepEqual(world.systemManager.systems[0].queries, {});
t.deepEqual(world.systemManager.systems[1].queries, {});
t.deepEqual(world.systemManager.systems[2].queries, {});
t.deepEqual(world.systemManager.systems[3].queries, {});
const error = t.throws(() => {
world.registerSystem(SystemEmpty4);
}, Error);
t.is(error.message, "'components' attribute can't be empty in a query");
const error2 = t.throws(() => {
world.registerSystem(SystemEmpty5);
}, Error);
t.is(error2.message, "'components' attribute can't be empty in a query");
});
test("queries", t => {
var world = new World();
world.registerComponent(FooComponent).registerComponent(BarComponent);
for (var i = 0; i < 15; i++) {
var entity = world.createEntity();
if (i < 10) entity.addComponent(FooComponent);
if (i >= 5) entity.addComponent(BarComponent);
}
class SystemFoo extends System {
init() {
return {
queries: {
entities: { components: [FooComponent] }
}
};
}
}
class SystemBar extends System {
init() {
return {
queries: {
entities: { components: [BarComponent] }
}
};
}
}
class SystemBoth extends System {
init() {
return {
queries: {
entities: { components: [FooComponent, BarComponent] }
}
};
}
}
// Register empty system
world
.registerSystem(SystemFoo)
.registerSystem(SystemBar)
.registerSystem(SystemBoth);
// Foo
t.is(world.systemManager.systems[0].queries.entities.length, 10);
// Bar
t.is(world.systemManager.systems[1].queries.entities.length, 10);
// Both
t.is(world.systemManager.systems[2].queries.entities.length, 5);
});
test("reactive", t => {
var world = new World();
class ReactiveSystem extends System {
init() {
return {
queries: {
entities: {
components: [FooComponent, BarComponent],
events: {
added: {
event: "EntityAdded"
},
removed: {
event: "EntityRemoved"
},
changed: {
event: "EntityChanged"
},
fooChanged: {
event: "ComponentChanged",
components: [FooComponent]
},
barChanged: {
event: "ComponentChanged",
components: [BarComponent]
},
foobarChanged: {
event: "ComponentChanged",
components: [FooComponent, BarComponent]
}
}
}
}
};
}
}
// Register empty system
world.registerSystem(ReactiveSystem);
world.registerComponent(FooComponent).registerComponent(BarComponent);
for (var i = 0; i < 15; i++) {
world
.createEntity()
.addComponent(FooComponent)
.addComponent(BarComponent);
}
// Entities from the standard query
t.is(world.systemManager.systems[0].queries.entities.length, 15);
// Added entities
t.is(world.systemManager.systems[0].events.entities.added.length, 15);
world.execute(); // After execute, events should be cleared
t.is(world.systemManager.systems[0].events.entities.added.length, 0);
// Add a new one
world
.createEntity()
.addComponent(FooComponent)
.addComponent(BarComponent);
t.is(world.systemManager.systems[0].events.entities.added.length, 1);
world.execute(); // After execute, events should be cleared
t.is(world.systemManager.systems[0].events.entities.added.length, 0);
// Changing
world.entityManager._entities[0].getMutableComponent(FooComponent);
t.is(world.systemManager.systems[0].events.entities.changed.length, 1);
t.is(world.systemManager.systems[0].events.entities.fooChanged.length, 1);
t.is(world.systemManager.systems[0].events.entities.barChanged.length, 0);
t.is(world.systemManager.systems[0].events.entities.foobarChanged.length, 0);
world.execute(); // After execute, events should be cleared
t.is(world.systemManager.systems[0].events.entities.changed.length, 0);
t.is(world.systemManager.systems[0].events.entities.fooChanged.length, 0);
world.entityManager._entities[0].getMutableComponent(BarComponent);
t.is(world.systemManager.systems[0].events.entities.changed.length, 1);
t.is(world.systemManager.systems[0].events.entities.fooChanged.length, 0);
t.is(world.systemManager.systems[0].events.entities.barChanged.length, 1);
t.is(world.systemManager.systems[0].events.entities.foobarChanged.length, 0);
world.execute(); // After execute, events should be cleared
t.is(world.systemManager.systems[0].events.entities.changed.length, 0);
t.is(world.systemManager.systems[0].events.entities.barChanged.length, 0);
// Check if the entity is already on the list?
world.entityManager._entities[0].getMutableComponent(FooComponent);
world.entityManager._entities[0].getMutableComponent(BarComponent);
t.is(world.systemManager.systems[0].events.entities.changed.length, 1);
t.is(world.systemManager.systems[0].events.entities.fooChanged.length, 1);
t.is(world.systemManager.systems[0].events.entities.barChanged.length, 1);
t.is(world.systemManager.systems[0].events.entities.foobarChanged.length, 1);
world.execute(); // After execute, events should be cleared
t.is(world.systemManager.systems[0].events.entities.changed.length, 0);
t.is(world.systemManager.systems[0].events.entities.fooChanged.length, 0);
t.is(world.systemManager.systems[0].events.entities.barChanged.length, 0);
t.is(world.systemManager.systems[0].events.entities.foobarChanged.length, 0);
// Dispose an entity
world.entityManager._entities[0].dispose();
t.is(world.systemManager.systems[0].events.entities.removed.length, 1);
world.execute(); // After execute, events should be cleared
t.is(world.systemManager.systems[0].events.entities.removed.length, 0);
// Removed
world.entityManager._entities[0].removeComponent(FooComponent);
t.is(world.systemManager.systems[0].events.entities.removed.length, 1);
world.execute(); // After execute, events should be cleared
t.is(world.systemManager.systems[0].events.entities.removed.length, 0);
// Added componets to the previous one
world.entityManager._entities[0].addComponent(FooComponent);
t.is(world.systemManager.systems[0].events.entities.added.length, 1);
world.execute(); // After execute, events should be cleared
t.is(world.systemManager.systems[0].events.entities.added.length, 0);
// Remove all components from the first 5 entities
for (i = 0; i < 5; i++) {
world.entityManager._entities[i].removeAllComponents();
}
t.is(world.systemManager.systems[0].events.entities.removed.length, 5);
world.execute(); // After execute, events should be cleared
t.is(world.systemManager.systems[0].events.entities.removed.length, 0);
// Dispose all entities
world.entityManager.removeAllEntities();
t.is(world.systemManager.systems[0].events.entities.removed.length, 10);
world.execute(); // After execute, events should be cleared
t.is(world.systemManager.systems[0].events.entities.removed.length, 0);
});

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc