From: Ben Beltran Date: Fri, 29 Jan 2016 03:49:48 +0000 (-0600) Subject: ES6ifies serpentity X-Git-Tag: 1.0.0^2~12 X-Git-Url: https://git.r.bdr.sh/rbdr/serpentity/commitdiff_plain/d0eb71f3e1f7a8fb227f9fd9286c9d9f041fcce0 ES6ifies serpentity --- diff --git a/lib/serpentity.js b/lib/serpentity.js new file mode 100644 index 0000000..c5ccc97 --- /dev/null +++ b/lib/serpentity.js @@ -0,0 +1,248 @@ +'use strict'; + +/* +Serpentity is a simple entity framework inspired by Ash. + +Usage: + + let Serpentity = require('serpentity'); + +## Instantiating an engine + + let engine = Serpentity(); + +Add entities or systems, systems are added with a priority (the smaller +the number, the earlier it will be called): + + engine.addEntity(entityFactory()); + engine.addSystem(new GameSystem(), priority); + +Update all systems: + + engine.update(dt); + +Remove entities or systems: + + engine.removeEntity(entityReference); + engine.removeSystem(systemReference); + +## Creating Entities + +Entities are the basic object of Serpentity, and they do nothing. + + let entity = new Serpentity.Entity(); + +All the behavior is added through components + +## Creating Components + +Components define data that we can add to an entity. This data will +eventually be consumed by "Systems" + + let PositionComponent = class PositionComponent extends Serpentity.Component { + constructor (config) { + this.x = 0; + this.y = 0; + + super(config); + } + }; + +You can add components to entities by using the add method: + + entity.addComponent(new PositionComponent()); + + +Systems can refer to entities by requesting nodes. + +## Working with Nodes + +Nodes are sets of components that you define, so your system can require +entities that always follow the API defined in the node. + + let MovementNode = class MovementNode extends Serpentity.Node; + MovementNode.position = PositionComponent; + MovementNode.motion = MotionComponent; + +You can then request an array of all the nodes representing entities +that comply with that API + + engine.getNodes(MovementNode); + +## Creating Systems + +Systems are called on every update, and they use components through nodes. + + let TestSystem = class TestSystem extends Serpentity.System { + added (engine){ + this.nodeList = engine.getNodes(MovementNode); + }, + removed (engine){ + this.nodeList = undefined; + } + update (dt){ + let node; + for (node of this.nodeList) { + console.log(`Current position is: ${node.position.x},${node.position.y}`); + } + } + }; + +## That's it + +Just run `engine.update(dt)` in your game loop :D + +*/ +let Serpentity = class Serpentity { + + constructor (config) { + this.systems = []; + this.entities = []; + this._nodeCollections = []; + this._nodeCollectionKeys = []; + + Object.assign(this, config || {}); + } + + /* + * Adds a system to the engine, so its update method will be called + * with the others. Triggers added hook. + * + * returns true if added succesfully, false if already added + */ + addSystem (system, priority) { + let lastIndex, found; + + if (this.systems.indexOf(system) >= 0) { + return false; + } + + system.priority = priority; + + found = false; + lastIndex = 0; + + this.systems.some(function findPriority(existingSystem, i) { + lastIndex = i; + if (existingSystem.priority >= system.priority) { + found = true; + return true; + } + }); + + if (!found) { + lastIndex += 1; + } + + this.systems.splice(lastIndex, 0, system); + system.added(this); + return true; + } + + /* + * Removes a system from the engine, so its update method will no + * longer will be called. Triggers the removed hook. + * + * returns true if removed succesfully, false if already added + */ + removeSystem (system) { + let position; + + position = this.systems.indexOf(system); + if (position >= 0) { + this.systems[position].removed(this); + this.systems.splice(position, 1); + return true; + } + + return false; + } + + /* + * Adds an entity to the engine, adds to existing node collections + * + * returns true if added, false if already there + */ + addEntity (entity) { + if (this.entities.indexOf(entity) >= 0) { + return false; + } + this.entities.push(entity); + + this._nodeCollections.forEach(function (collection) { + collection.add(entity); + }); + + return true; + } + + /* + * Removes entity from system, removing from all node collections + * + * returns true if removed, false if not present + */ + removeEntity (entity) { + let position; + + position = this.entities.indexOf(entity); + if (position >= 0) { + this._nodeCollections.forEach(function (collection) { + collection.remove(entity); + }); + + this.entities.splice(position, 1); + return true; + } + + return false; + } + + /* + * Given a Node Class, retrieves a list of all the nodes for each + * applicable entity. + */ + getNodes (nodeType) { + let position, nodeCollection; + + position = this._nodeCollectionKeys.indexOf(nodeType); + + if (position >= 0) { + return this._nodeCollections[position].nodes; + } + + nodeCollection = new Serpentity.NodeCollection({ + type : nodeType + }); + + this._nodeCollectionKeys.push(nodeType); + this._nodeCollections.push(nodeCollection); + + this.entities.forEach(function (entity) { + nodeCollection.add(entity); + }); + + return nodeCollection.nodes; + } + + /* + * Calls update for every loaded system. + */ + update (dt) { + this.systems.forEach(function (system) { + system.update(dt); + }); + } +}; + +// Add namespaced objects. +if (typeof module !== 'undefined' && this.module !== module) { + Serpentity.Component = require('./serpentity/component.js'); + Serpentity.Entity = require('./serpentity/entity.js'); + Serpentity.Node = require('./serpentity/node.js'); + Serpentity.NodeCollection = require('./serpentity/node_collection.js'); + Serpentity.System = require('./serpentity/system.js'); + + module.exports = Serpentity; +} else { + window.Serpentity = Serpentity; +} diff --git a/lib/serpentity/component.js b/lib/serpentity/component.js index 0018a88..e9abcd1 100644 --- a/lib/serpentity/component.js +++ b/lib/serpentity/component.js @@ -1,21 +1,22 @@ +'use strict'; + +/* global Serpentity */ + /* * Components store data. Nothing to say here really, just * inherit and add a prototype, or don't even inherit, see? * It's just an empty class, so what I'm trying to say is your * components can be any class whatsoever. */ -Class(Serpentity, "Component")({ - prototype : { - init : function init(config) { - var property; - config = config || {}; +let Component = class Component { + constructor (config) { + Object.assign(this, config || {}); + } +}; - for (property in config) { - if (config.hasOwnProperty(property)) { - this[property] = config[property]; - } - } - } - } -}); +if (typeof module !== 'undefined' && this.module !== module) { + module.exports = Component; +} else { + Serpentity.Component = Component; +} diff --git a/lib/serpentity/entity.js b/lib/serpentity/entity.js index c020f05..2895158 100644 --- a/lib/serpentity/entity.js +++ b/lib/serpentity/entity.js @@ -1,58 +1,58 @@ +'use strict'; + +/* global Serpentity */ + /* * The entity gives the entity framework its name. It exists only * to hold components. */ -Class(Serpentity, "Entity")({ - prototype : { - _components : null, - _componentKeys : null, - - init : function init(config) { - var property; - - this._componentKeys = []; - this._components = []; - - for (property in config) { - if (config.hasOwnProperty(property)) { - this[property] = config[property]; - } - } - }, - - /* - * Adds a component to the entity. - * - * returns true if added, false if already present - */ - addComponent : function addComponent(component) { - if (this._componentKeys.indexOf(component.constructor) >= 0) { - return false; - } - this._componentKeys.push(component.constructor); - this._components.push(component); - return true; - }, - - /* - * returns true if component is included, false otherwise - */ - hasComponent : function hasComponent(componentClass) { - if (this._componentKeys.indexOf(componentClass) >= 0) { - return true; - } - return false; - }, - - /* - * returns the component associated with that key - */ - getComponent : function getComponent(componentClass) { - var position; - position = this._componentKeys.indexOf(componentClass); - if (position >= 0) { - return this._components[position]; - } - } + +let Entity = class Entity { + constructor (config) { + this._componentKeys = []; + this._components = []; + + Object.assign(this, config || {}); + } + + /* + * Adds a component to the entity. + * + * returns true if added, false if already present + */ + addComponent (component) { + if (this._componentKeys.indexOf(component.constructor) >= 0) { + return false; } -}); + this._componentKeys.push(component.constructor); + this._components.push(component); + return true; + } + + /* + * returns true if component is included, false otherwise + */ + hasComponent (componentClass) { + if (this._componentKeys.indexOf(componentClass) >= 0) { + return true; + } + return false; + } + + /* + * returns the component associated with that key + */ + getComponent (componentClass) { + let position; + position = this._componentKeys.indexOf(componentClass); + if (position >= 0) { + return this._components[position]; + } + } +}; + +if (typeof module !== 'undefined' && this.module !== module) { + module.exports = Entity; +} else { + Serpentity.Entity = Entity; +} diff --git a/lib/serpentity/node.js b/lib/serpentity/node.js index 00abde2..db33f17 100644 --- a/lib/serpentity/node.js +++ b/lib/serpentity/node.js @@ -1,50 +1,49 @@ +'use strict'; + +/* global Serpentity */ + /* * A node describes a set of components in order to describe entities * that include them. */ -Class(Serpentity, "Node")({ - - /* - * Returns true if the given entity matches the defined protocol, - * false otherwise - */ - matches : function matches(entity) { - var property, matched, types; - - types = this.types; - - for (property in this.types) { - - if (this.types.hasOwnProperty(property)) { - matched = false; +let Node = class Node { - if (entity.hasComponent(types[property])) { - matched = true; - } + /* + * Returns true if the given entity matches the defined protocol, + * false otherwise + */ + static matches (entity) { + let property, types; - if (!matched) { - return false; - } - } - } + types = this.types; - return true; - }, + for (property in types) { + if (types.hasOwnProperty(property)) { + let matched, type; - prototype : { + matched = false; + type = types[property]; + if (entity.hasComponent(type)) { + matched = true; + } + if (!matched) { + return false; + } + } + } - types : null, + return true; + } - init : function (config) { - var property; + constructor (config) { + this.types = {}; - this.types = {}; + Object.assign(this, config || {}); + } +}; - for (property in this.constructor) { - if (this.constructor.hasOwnProperty(property)) { - this.types[property] = this.constructor[property]; - } - } - } - } -}); +if (typeof module !== 'undefined' && this.module !== module) { + module.exports = Node; +} else { + Serpentity.Node = Node; +} diff --git a/lib/serpentity/node_collection.js b/lib/serpentity/node_collection.js index d1d25fc..f5b1794 100644 --- a/lib/serpentity/node_collection.js +++ b/lib/serpentity/node_collection.js @@ -1,3 +1,7 @@ +'use strict'; + +/* global Serpentity */ + /* * Node Collections contain nodes, in order to keep the lists of nodes * that belong to each type. @@ -5,90 +9,87 @@ * It has a type which is the class name of the node, and an array of * instances of that class. */ -Class(Serpentity, "NodeCollection")({ - prototype : { - type : null, - nodes : null, - - init : function init(config) { - var property; - - config = config || {}; - - this.nodes = []; - - for (property in config) { - if (config.hasOwnProperty(property)) { - this[property] = config[property]; - } - } - }, - - /* - * Creates a node for an entity if it matches, and adds it to the - * node list. - * - * Returns true if added, false otherwise. - */ - add : function add(entity) { - var node, types, property; - - if (this.type.matches(entity) && !this._entityExists(entity)) { - node = new this.type({}); - - node.entity = entity; - - types = this.type.types; - - for (property in types) { - if (types.hasOwnProperty(property)) { - node[property] = entity.getComponent(types[property]); - } - } - - this.nodes.push(node); - - return true; - } - - return false; - }, - - /* - * Removes an entity by removing its related node from the list of nodes - * - * returns true if it was removed, false otherwise. - */ - remove : function (entity) { - var found; - found = -1; - this.nodes.forEach(function (node, i) { - if (node.entity === entity) { - found = i; - } - }); - - if (found >= 0) { - this.nodes.splice(found, 1); - return true; - } - - return false; - }, - - /* - * Checks whether we already have nodes for this entity. - */ - _entityExists : function entityExists(entity) { - var found; - found = false; - this.nodes.forEach(function (node) { - if (node.entity === entity) { - found = true; - } - }); - - return found; + +let NodeCollection = class NodeCollection { + + constructor (config) { + this.nodes = []; + this.type = null; + + Object.assign(this, config || {}); + } + + /* + * Creates a node for an entity if it matches, and adds it to the + * node list. + * + * Returns true if added, false otherwise. + */ + add (entity) { + + if (this.type.matches(entity) && !this._entityExists(entity)) { + let node, types, property; + + node = new this.type({}); + node.entity = entity; + types = this.type.types; + + for (property in types) { + if (types.hasOwnProperty(property)) { + node[property] = entity.getComponent(types[property]); } + } + + this.nodes.push(node); + + return true; + } + + return false; + } + + /* + * Removes an entity by removing its related node from the list of nodes + * + * returns true if it was removed, false otherwise. + */ + remove (entity) { + let found; + + found = -1; + this.nodes.forEach(function (node, i) { + if (node.entity === entity) { + found = i; + } + }); + + if (found >= 0) { + this.nodes.splice(found, 1); + return true; } -}); + + return false; + } + + /* + * Checks whether we already have nodes for this entity. + */ + _entityExists (entity) { + let found, node; + + found = false; + for (node of this.nodes) { + if (node.entity === entity) { + found = true; + } + } + + return found; + } +}; + +if (typeof module !== 'undefined' && this.module !== module) { + module.exports = NodeCollection; +} else { + Serpentity.NodeCollection = NodeCollection; +} diff --git a/lib/serpentity/serpentity.js b/lib/serpentity/serpentity.js deleted file mode 100644 index ed1a9df..0000000 --- a/lib/serpentity/serpentity.js +++ /dev/null @@ -1,261 +0,0 @@ -if (typeof require !== "undefined") { - require("neon"); -} - -/* -Serpentity is a simple entity framework inspired by Ash. - -Usage: - - require('serpentity'); - -## Instantiating an engine - - var engine = Serpentity(); - -Add entities or systems, systems are added with a priority (the smaller -the number, the earlier it will be called): - - engine.addEntity(entityFactory()); - engine.addSystem(new GameSystem(), priority); - -Update all systems: - - engine.update(dt); - -Remove entities or systems: - - engine.removeEntity(entityReference); - engine.removeSystem(systemReference); - -## Creating Entities - -Entities are the basic object of Serpentity, and they do nothing. - - var entity = new Serpentity.Entity(); - -All the behavior is added through components - -## Creating Components - -Components define data that we can add to an entity. This data will -eventually be consumed by "Systems" - - Class("PositionComponent").inherits(Serpentity.Component)({ - prototype : { - x : 0, - y : 0 - } - }); - -You can add components to entities by using the add method: - - entity.addComponent(new PositionComponent()); - - -Systems can refer to entities by requesting nodes. - -## Working with Nodes - -Nodes are sets of components that you define, so your system can require -entities that always follow the API defined in the node. - - Class("MovementNode").inherits(Serpentity.Node)({ - types : { - position : PositionComponent, - motion : MotionComponent - } - }); - -You can then request an array of all the nodes representing entities -that comply with that API - - engine.getNodes(MovementNode); - -## Creating Systems - -Systems are called on every update, and they use components through nodes. - - Class("TestSystem").inherits(Serpentity.System)({ - prototype : { - added : function added(engine){ - this.nodeList = engine.getNodes(MovementNode); - }, - removed : function removed(engine){ - this.nodeList = undefined; - } - update : function update(dt){ - this.nodeList.forEach(function (node) { - console.log("Current position is: " + node.position.x + "," + node.position.y); - }); - } - } - }); - -## That's it - -Just run `engine.update(dt)` in your game loop :D - -*/ -Class("Serpentity")({ - prototype : { - systems : null, - entities : null, - _nodeCollections : null, - _nodeCollectionKeys : null, - - init : function init(config) { - var property; - - config = config || {}; - - this.systems = []; - this.entities = []; - this._nodeCollections = []; - this._nodeCollectionKeys = []; - - for (property in config) { - if (config.hasOwnProperty(property)) { - this[property] = config[property]; - } - } - }, - - /* - * Adds a system to the engine, so its update method will be called - * with the others. Triggers added hook. - * - * returns true if added succesfully, false if already added - */ - addSystem : function addSystem(system, priority) { - var lastIndex, found; - - if (this.systems.indexOf(system) >= 0) { - return false; - } - - system.priority = priority; - - found = false; - lastIndex = 0; - - this.systems.some(function findPriority(existingSystem, i) { - lastIndex = i; - if (existingSystem.priority >= system.priority) { - found = true; - return true; - } - }); - - if (!found) { - lastIndex += 1 - } - - this.systems.splice(lastIndex, 0, system); - system.added(this); - return true; - }, - - /* - * Removes a system from the engine, so its update method will no - * longer will be called. Triggers the removed hook. - * - * returns true if removed succesfully, false if already added - */ - removeSystem : function removeSystem(system) { - var position; - - position = this.systems.indexOf(system); - if (position >= 0) { - this.systems[position].removed(this); - this.systems.splice(position, 1); - return true; - } - - return false; - }, - - /* - * Adds an entity to the engine, adds to existing node collections - * - * returns true if added, false if already there - */ - addEntity : function addEntity(entity) { - if (this.entities.indexOf(entity) >= 0) { - return false; - } - this.entities.push(entity); - - this._nodeCollections.forEach(function (collection) { - collection.add(entity); - }); - - return true; - }, - - /* - * Removes entity from system, removing from all node collections - * - * returns true if removed, false if not present - */ - removeEntity : function removeEntity(entity) { - var position; - - position = this.entities.indexOf(entity); - if (position >= 0) { - this._nodeCollections.forEach(function (collection) { - collection.remove(entity); - }); - - this.entities.splice(position, 1); - return true; - } - - return false; - }, - - /* - * Given a Node Class, retrieves a list of all the nodes for each - * applicable entity. - */ - getNodes : function getNodes(nodeType) { - var position, nodeCollection; - - position = this._nodeCollectionKeys.indexOf(nodeType); - - if (position >= 0) { - return this._nodeCollections[position].nodes; - } - - nodeCollection = new Serpentity.NodeCollection({ - type : nodeType, - }); - - this._nodeCollectionKeys.push(nodeType); - this._nodeCollections.push(nodeCollection); - - this.entities.forEach(function (entity) { - nodeCollection.add(entity); - }); - - return nodeCollection.nodes; - }, - - /* - * Calls update for every loaded system. - */ - update : function update(dt) { - this.systems.forEach(function (system) { - system.update(dt); - }); - } - } -}); - -if (typeof require !== "undefined") { - require("./component.js"); - require("./entity.js"); - require("./node.js"); - require("./node_collection.js"); - require("./system.js"); -} diff --git a/lib/serpentity/system.js b/lib/serpentity/system.js index c817be1..339f6e7 100644 --- a/lib/serpentity/system.js +++ b/lib/serpentity/system.js @@ -1,32 +1,44 @@ +'use strict'; + +/* global Serpentity */ + /* * Systems contain most of the logic, and work with nodes in order to * act and change their values. * * You usually want to inherit from this class and override the - * three methods. + * three methods. They are shown here to document the interface. */ -Class(Serpentity, "System")({ - prototype : { - /* - * This will be run when the system is added to the engine - */ - added : function added(engine) { - // Override - }, +let System = class System { + + /* + * This will be run when the system is added to the engine + */ + added () { + // Override with added(engine) + // Receives an instance of the serpentity engine + } + + /* + * This will be run when the system is removed from the engine + */ + removed () { + // Override with removed(engine) + // Receives an instance of the serpentity engine + } - /* - * This will be run when the system is removed from the engine - */ - removed : function removed(engine) { - // Override - }, + /* + * This will run every time the engine's update method is called + */ + update () { + // Override with update(dt) + // Receives a delta of the time + } +}; - /* - * This will run every time the engine's update method is called - */ - update : function update(dt) { - // Override - } - } -}); +if (typeof module !== 'undefined' && this.module !== module) { + module.exports = System; +} else { + Serpentity.System = System; +}