Easy Entity Component System is a minimalist open-source Javascript library that helps you create games quickly. It's focused on developer happyness and performance. It has zero dependancies, is super lightweight and extensible.
Just get a simple javascript project with npm
support.
You can also use the build version and use it without any stack.
- World: One class instance to rule them all.
- Entity: It's a instantiable class that is composed of Components.
- Component: The «data» of your entity. Never instantiated, only declarative.
- System: Where the logic happens. A system process Entities that have a specific set of Components.
- Addon: This is a way to extend the World.
Install package from npm
in your project or get the build/easy-ecs.js
file.
npm i @muini/easy-ecs
Creating the structure of your game is a declarative process
import { Component } from "@muini/easy-ecs";
// Component
export class Position extends Component {
static name = "Position";
static x = 0;
static y = 0;
}
// Component inheritence
export class Position3D extends Position {
static name = "Position3D";
static z = 0;
}
// Another component
export class Health extends Component {
static name = "Health";
static health = 0;
static maxHealth = 0;
}
import { Entity } from "@muini/easy-ecs";
// Entity is just a list of Component classes
export class Character extends Entity {
static components = [Position];
}
// Entity inheritence, you need to spread the parent components array
export class Hero extends Character {
static components = [...super.components, Health];
}
import { System } from '@muini/easy-ecs';
// System declaration
export class CharacterMovement extends System {
// Component list that will filter entities that have those components
dependencies = [Position];
// onUpdate Will fire every world.update (or every frame if Loop Addon is added)
onUpdate = (entities) => {
// entities is filtered entities
entities.forEach(entity => {
entity.x += 0.1 * Time.delta; //Time is an Addon, see below
// You can access world with this.world
// You can query entities with
// - this.world.getEntitiesWithComponents([...ComponentsClass])
// - this.world.getEntitiesOfType(Class)
// - this.world.getEntityById(id)
})
};
}
// Make your own system
export class MySystem extends System {
dependencies = [MyComponent, ...];
onInit = (entities) => {};
onUpdate = (entities) => {};
}
import { World } from '@muini/easy-ecs';
import { Loop, Time, Input, Renderer, SaveSystem } from '@muini/easy-ecs/addons';
import { Character } from './your-game/entities';
import { PlayerMovement, CharacterMovement, CharacterRenderer } from './your-game/systems';
//Instantiate your world
const world = new World({
// Add as much addon as you can to extend the world and engine
addons: [Loop, Time, Input, Renderer, SaveSystem, ...],
// Order of systems is the order of execution
systems: [PlayerMovement, CharacterMovement, CharacterRenderer, ...]
});
// Instantiate an entity, first arg is a World, second is default values
const bob = new Character(world, {
x: 10,
y: 10,
health: 100,
maxHealth: 100
})
// Start the world, that's all !
world.start()
Addon is an easy way to extend the world engine. Addon will never be instantiated and all properties must be static.
- 🔁 Loop:
Will set a loop calling
world.update(timestamp)
based onrequestAnimationFrame
- ⏱️ Time:
Access
Time.time
,Time.delta
andTime.elapsed
easily anywhere - 🕹️ Input:
Access to current input, either
Input.mouse
position orInput.isPressed(key)
to check if a specific key is pressed, orInput.keypress
to get all keys pressed - 🖼️ Renderer:
Canvas Renderer with basic access to
Renderer.canvas
and contextRenderer.ctx
- 💾 SaveSystem:
Save & restore world state from
localStorage
usingconst id = SaveSystem.saveWorld(world, saveName)
andSaveSystem.restoreWorld(world, saveName)
// Example of a new addon
export class MyAddon extends Addon {
static onInit = (world) => {
/*Do stuff*/
};
static onBeforeUpdate = (world, time) => {
/*Do stuff*/
};
static onAfterUpdate = (world, time) => {
/*Do stuff*/
};
}
// Example of Time Addon
export class Time extends Addon {
static time = 0;
static delta = 0;
static elapsed = 0;
static onBeforeUpdate = (world, time) => {
Time.delta = time - Time.time;
Time.time = time;
Time.elapsed += Time.delta;
};
}
Just a bunch of rules that I try to follow to make everything nice, that are from this video :
- Components have no functions
- Systems have no states
- Shared code lives in utils
- Complex side effects should be deferred
- System can't call other systems
- Core
- Addons: Loop, Time, Input, Renderer(canvas), SaveSystem
- Readme
- Example
- Addon: Audio
- Documentation
- Addon: AssetManager
- Multithreading
- Addon: Network
Other Addon ideas = UI (html based?), WebGL Renderer, ...
- Unity - ECS approach from Unity game engine
- ECSY - ECS approach from Mozilla team
- MattDesl - ECS approach from MattDesl
Feel free to open issues for questions, bugs or improvements & PR !
- Corentin Flach - Initial work - Github
This project is licensed under the MIT License