Skip to content

Instantly share code, notes, and snippets.

@ddmills
Created July 20, 2016 03:33
Show Gist options
  • Save ddmills/6a457f165b35d48afb4191978e5f600e to your computer and use it in GitHub Desktop.
Save ddmills/6a457f165b35d48afb4191978e5f600e to your computer and use it in GitHub Desktop.
Component-Entity-System
let _id;
const id = () => {
let now = Date.now();
if (now <= _id) now++;
_id = now;
return now;
}
const hash = (n) => n.sort((a, b) => a > b).join('$');
const remove = (a, v) => a.splice(a.indexOf(v), 1);
const getComponent = (n) => components.get(n) || newComponent(n);
const tags = new Map();
const entities = [];
const components = new Map();
class Tag {
constructor(n) {
this.na = n;
this.en = [];
entities.forEach(e => this.match(e));
}
match(e) {
let k = Object.keys(e.c);
for (let n of this.na) {
if (!k.includes(n)) return false;
}
this.en.push(e);
}
onAdd(e, c) {
c in this.na && this.en.push(e);
}
onRem(e, c) {
this.na.includes(c) && remove(this.en, e);
}
static get(n) {
let h = hash(n);
return tags.has(h) ? tags.get(h) : Tag.make(n, h);
}
static make(n, h) {
const t = new Tag(n);
tags.set(h, t);
return t;
}
}
class Entity {
constructor(id) {
this.c = {};
this.id = id;
}
add(n, ...a) {
this.c[n] = getComponent(n)(this, ...a);
this.c[n].mount && this.c[n].mount(this);
tags.forEach(t => t.onAdd(this, n));
return this.c[n];
}
remove(n, ...a) {
if (!this.c[n]) return;
this.c[n].unmount && this.c[n].unmount(this, ...a);
delete this.c[n];
tags.forEach(t => t.onRem(this, n));
}
}
const newComponent = (n) => {
components.set(n, (entity) => {});
return c;
}
export const component = (n, d) => components.set(n, d);
export const findByComponent = (...n) => Tag.get(n).en;
export const entity = () => {
let e = new Entity(id());
entities.push(e);
tags.forEach(t => t.match(e));
return e;
}
@ddmills
Copy link
Author

ddmills commented Jul 20, 2016

import { entity, component } from './geotic';
import * as geotic from './geotic';

// define components
component('pos', () => ({ x:0, y:0, z:0 }));
component('hair', (entity) => {
  return {
    style: 'shaggy',
    mount: (e) => {}, // called on attached to entity
    unmount: (e) => {} // called on removed from entity
  };
});

// components can be basic
component('name', (entity, name) => name);

// create entities
const dog = entity();
const cat = entity();

// attach components
dog.add('name', 'Sam');
dog.add('hair');
dog.add('pos');

cat.add('name', 'Princess Dilly');
cat.add('hair');
cat.add('pos');

// reference components by "entity.c[component-name]"
console.log(`hello ${cat.c.name}`); // "Princess Dilly"

// set properties on components
cat.c.pos.x = 20;

// all entities have an id
console.log(cat.id);

// get all entities which have a 'pos' and 'hair' component
console.log(geotic.findByComponent('pos', 'hair'));  // [cat, dog]

// remove a component from an entity
cat.remove('hair');

console.log(geotic.findByComponent('pos', 'hair')); // [dog]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment