A performant, featureful, and flexible Entity-Component-System library for JavaScript, written in ECMAScript ES2018, intended for use in games and simulations.
- Overview
- API Reference
- Patterns
- 1.0 Announcement Post
- Changelog
- Discord -- Web Platform Game Dev
- @fritzy Twitter
npm install ape-ecs
- Advanced Queries for entities.
- Persisted Queries (indexes) are updated as Entity composition changes.
- Component reference properties to Entities (EntityRef, EntitySet, EntityObject)
- When a referenced entity is destroyed, the property is updated to null.
- Subscribe-able events for adding and removing references.
- Reverse query from entity to entity-components that reference it.
- Not all systems need to run every frame.
- Export/import support for saving/restoring state with component-level serialization configuration.
- 100% Test Coverage.
const ApeECS = require('ape-ecs');
class Gravity extends ApeECS.System {
init() {
this.mainQuery = this.createQuery().fromAll('Position', 'Physics');
}
update(tick) {
const entities = this.mainQuery.execute();
const frameInfo = this.world.getEntity('frame');
for (const entity of entities) {
const point = entity.getOne('Position');
if (!entity.has('Vector')) {
entity.addComponent({
type: 'Vector',
mx: 0,
my: 0
})
}
const vector = entity.getOne('Vector');
vector.my += 9.807 * frame.time.deltaTime * .01;
vector.update();
}
}
}
class Position extends ApeECS.Component {}
Position.properties = {
x: 0,
y: 0
};
class Vector extends ApeECS.Component {
get speed() {
return Math.sqrt(this.mx**2 + this.my**2);
}
}
Vector.properties = {
mx: 0,
my: 0,
speed: 0
};
class FrameInfo extends ApeECS.Component {}
FrameInfo.properties = {
deltaTime: 0,
deltaFrame: 0,
time: 0
};
const world = new ApeECS.World();
world.registerComponent(Position);
world.registerComponent(Vectory);
world.registerComponent(FrameInfo);
world.registerTags('Physics');
world.registerSystem('frame', Gravity);
const frame = world.createEntity({
id: 'frame',
c: {
time: {
type: 'FrameInfo',
}
}
})
// see world.creatEntity and world.createEntities
// in docs/World.md for more details
world.registerSystem('frame', require('./move.js'));
world.createEntities(require('./saveGame.json'));
let lastTime = 0;
function update(time) {
const delta = time - lastTime;
time = lastTime;
frame.time.update({
time: time,
deltaTime: delta,
deltaFrame: delta / 16.667
});
world.runSystems('frame');
// run update again the next browser render call
// every 16ms or so
window.requestAnimationFrame(update);
}
update(0);
The Entity-Component-System paradigm is great for managing dynamic objects in games and simulations. Instead of binding functionality to data through methods, entities are dynamically composed of any combination of types. Separate systems are then able to query for entities with a given set of types.
ECS's dynamic data composition and freely interacting systems leads to:
- More complex and dynamic composition than OOP
- Improved performance due to lack of API methods
- Emergent Gameplay with logical behavior extended beyond the programmer's vision.
This library has been inspired in part by:
An in-progress arcade game Missile Orders.
This game is not in a complete state yet, nor does it show off all of the potential of ECS yet.
The goal is to keep test coverage at 100%.
git clone git@github.com/fritzy/ape-ecs.git
cd ape-ecs
npm install
npm test
- Ben Morse -- Ben is an early adopter that provided a lot of insight and you have him to thank for the TypeScript definitions! Ben has a game, Super Game of Life that uses Ape ECS.
- Jaime Robles -- For the Ape ECS banner!