-
-
Save caracal7/b05852b71f5b5030efd87a7734417cdf to your computer and use it in GitHub Desktop.
Basic ECS
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Components | |
// ---------- | |
function KeyboardController() {} | |
function AIController() {} | |
function Position(x, y) { | |
this.x = x || 0; | |
this.y = y || 0; | |
} | |
function Motion(dx, dy) { | |
this.dx = dx || 0; | |
this.dy = dy || 0; | |
} | |
function Display(sprite) { | |
this.sprite = sprite; | |
} | |
// Systems | |
// ------- | |
function InputSystem() { | |
this.keyboard = function(entity) { | |
var motion = entity.get(Motion); | |
// Read keyboard inputs and update motion component. | |
// ... | |
}; | |
this.ai = function(entity) { | |
var motion = entity.get(Motion); | |
// Make AI decisions and update motion component. | |
// ... | |
}; | |
} | |
function PhysicsSystem() { | |
this.move = function(delta, entity) { | |
var position = entity.get(Position); | |
var motion = entity.get(Motion); | |
// Update position. | |
position.x += motion.dx * delta; | |
position.y += motion.dy * delta; | |
}; | |
} | |
function RenderingSystem() { | |
this.sprite = function() { | |
// Render entity. | |
// ... | |
}; | |
} | |
// Game | |
// ---- | |
function Game() { | |
// Instantiates the ECS engine | |
this.world = Object.create(World); | |
// Creates systems | |
this.input = new InputSystem(); | |
this.movement = new MovementSystem(); | |
this.rendering = new RenderingSystem(); | |
} | |
Game.prototype = { | |
/** | |
* Game initialization | |
*/ | |
create: function() { | |
// Create player | |
this.world.createEntity([ | |
new KeyboardController(), | |
new Position(400, 300), | |
new Motion(), | |
new Display('hero.png') | |
]); | |
// Create enemies | |
for (var i = 20, i--;) { | |
this.world.createEntity([ | |
new KeyboardController(), | |
new Position(rand(0, 800), rand(0, 600)), | |
new Motion(), | |
new Display('enemy.png') | |
]); | |
} | |
}, | |
/** | |
* Update loop | |
*/ | |
update: function(delta) { | |
// Process inputs and AI | |
this.world.match(KeyboardController).map(this.input.keyboard); | |
this.world.match(AIController).map(this.input.ai); | |
// Apply physic | |
this.world.match(Motion).map(this.physics.move.bind(this.physics, delta)); | |
// Render entities | |
this.world.match(Display).map(this.rendering.sprite); | |
} | |
}; | |
// Create game | |
var game = new Game(); | |
// Then do whatever you want! | |
// - Call create() to start the game | |
// - Call update(delta) to update and render the game |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Basic ECS engine. | |
* | |
* WARNING: This engine is not optimized. | |
*/ | |
var World = (function() { | |
var nextID = 0; | |
var entities = []; | |
var entitiesToComponents = []; | |
var componentsToEntities = {}; | |
/** | |
* Internal entity definition. | |
*/ | |
var Entity = { | |
/** | |
* Retrieves the specified component. | |
*/ | |
get: function(comp) { | |
return entitiesToComponents[this.id][comp.name]; | |
}, | |
/** | |
* Adds the specified component. | |
*/ | |
add: function(component) { | |
var name = component.constructor.name; | |
if (!componentsToEntities[name]) { | |
componentsToEntities[name] = []; | |
} | |
entitiesToComponents[this.id][name] = component; | |
componentsToEntities[name][this.id] = true; | |
}, | |
/** | |
* Removes the specified component. | |
*/ | |
remove: function(component) { | |
delete entitiesToComponents[this.id][component.name]; | |
delete componentsToEntities[component.name][this.id]; | |
}, | |
/** | |
* Kills the entity. | |
*/ | |
kill: function() { | |
delete entities[this.id]; | |
delete entitiesToComponents[this.id]; | |
for (var comp in componentsToEntities) { | |
delete componentsToEntities[comp][this.id]; | |
} | |
} | |
}; | |
return { | |
/** | |
* Creates a new entity. | |
*/ | |
createEntity: function(components) { | |
var entityID = nextID++; | |
var entity = Object.create(Entity, { | |
id: { | |
value: entityID, | |
writable: false | |
} | |
}); | |
entities[entityID] = entity; | |
entitiesToComponents[entityID] = {}; | |
if (components) { | |
components.forEach(function(component) { | |
entity.add(component); | |
}); | |
} | |
return entity; | |
}, | |
/** | |
* Returns all entities having the specified component. | |
*/ | |
match: function(comp) { | |
return Object.keys(componentsToEntities[comp.name] || []).map(function(entityID) { | |
return entities[entityID]; | |
}); | |
} | |
}; | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment