From dc45c998880ac4456be8de2086e1fdd35aac9f96 Mon Sep 17 00:00:00 2001 From: sz-piotr Date: Sun, 13 Aug 2017 23:13:02 +0200 Subject: [PATCH] working version, add examples --- examples/basic/index.html | 77 +++++++++++++++++++++++++++++++++++++++ lib/ouyo.js | 2 +- lib/ouyo.js.map | 2 +- package-lock.json | 2 +- package.json | 7 +++- src/engine.js | 4 +- 6 files changed, 87 insertions(+), 7 deletions(-) create mode 100644 examples/basic/index.html diff --git a/examples/basic/index.html b/examples/basic/index.html new file mode 100644 index 0000000..618cd50 --- /dev/null +++ b/examples/basic/index.html @@ -0,0 +1,77 @@ + + + + + + + OUYO Examples: Basic + + + + + + + + diff --git a/lib/ouyo.js b/lib/ouyo.js index 6d4279d..4f568e1 100644 --- a/lib/ouyo.js +++ b/lib/ouyo.js @@ -1,2 +1,2 @@ -!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define("OUYO",[],t):"object"==typeof exports?exports.OUYO=t():e.OUYO=t()}(this,function(){return function(e){function t(i){if(n[i])return n[i].exports;var r=n[i]={i:i,l:!1,exports:{}};return e[i].call(r.exports,r,r.exports,t),r.l=!0,r.exports}var n={};return t.m=e,t.c=n,t.d=function(e,n,i){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:i})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="",t(t.s=1)}([function(e,t,n){"use strict";function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}n.d(t,"a",function(){return u});for(var r=(function(){function e(e,t){for(var n=0;n=o;)n++,e-=o;var i=0!=(this.values[n]&s[e]);t&&!i?this.values[n]+=s[e]:!t&&i&&(this.values[n]-=s[e])}},{key:"matches",value:function(e){for(var t=0;t=this.componentIdMap.length;)this.componentIdMap.push(null);null===this.componentIdMap[e.id]&&(this.componentIdMap[e.id]=this.componentsCount++)}},{key:"start",value:function(e){var t=this;this.started=!0,this.running=!0,e&&e(this);var n=void 0,i=Date.now(),r=function e(){t.running&&(n=Date.now(),t.update((n-i)/60),i=n),requestAnimationFrame(e)};requestAnimationFrame(r)}},{key:"update",value:function(e){for(var t=0;t=o;)n++,e-=o;var i=0!=(this.values[n]&s[e]);t&&!i?this.values[n]+=s[e]:!t&&i&&(this.values[n]-=s[e])}},{key:"matches",value:function(e){for(var t=0;t=this.componentIdMap.length;)this.componentIdMap.push(null);null===this.componentIdMap[e.id]&&(this.componentIdMap[e.id]=this.componentsCount++)}},{key:"start",value:function(e){var t=this;this.started=!0,this.running=!0,e&&e(this);var n=void 0,i=Date.now(),r=function e(){t.running&&(n=Date.now(),t.update((n-i)/60),i=n),requestAnimationFrame(e)};requestAnimationFrame(r)}},{key:"update",value:function(e){for(var t=0;t= KEY_BITS) {\n valueIndex++;\n index -= KEY_BITS;\n }\n\n var previousValue = (this.values[valueIndex] & POWERS[index]) !== 0;\n if (value && !previousValue) {\n this.values[valueIndex] += POWERS[index];\n } else if (!value && previousValue) {\n this.values[valueIndex] -= POWERS[index];\n }\n }\n }, {\n key: \"matches\",\n value: function matches(other) {\n for (var _i2 = 0; _i2 < other.values.length; _i2++) {\n var currentValue = this.values[_i2] || 0;\n var otherValue = other.values[_i2];\n if ((currentValue & otherValue) !== otherValue) {\n return false;\n }\n }\n return true;\n }\n }]);\n\n return Key;\n}();\n\n/***/ }),\n/* 1 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__engine__ = __webpack_require__(2);\n/* harmony reexport (binding) */ __webpack_require__.d(__webpack_exports__, \"Engine\", function() { return __WEBPACK_IMPORTED_MODULE_0__engine__[\"a\"]; });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__component__ = __webpack_require__(4);\n/* harmony reexport (binding) */ __webpack_require__.d(__webpack_exports__, \"component\", function() { return __WEBPACK_IMPORTED_MODULE_1__component__[\"a\"]; });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__query__ = __webpack_require__(5);\n/* harmony reexport (binding) */ __webpack_require__.d(__webpack_exports__, \"Query\", function() { return __WEBPACK_IMPORTED_MODULE_2__query__[\"a\"]; });\n\n\n\n\n/***/ }),\n/* 2 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"a\", function() { return Engine; });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__entity__ = __webpack_require__(3);\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\n\n\nvar Engine = function () {\n function Engine() {\n _classCallCheck(this, Engine);\n\n this.entities = null;\n this.changed = [];\n this.removed = [];\n\n this.systems = [];\n this.queries = [];\n this.componentIdMap = [];\n this.componentsCount = 0;\n\n this.started = false;\n this.running = false;\n\n this.onEntityKeyChange = this.onEntityKeyChange.bind(this);\n }\n\n _createClass(Engine, [{\n key: 'createEntity',\n value: function createEntity() {\n if (!this.started) {\n throw new Error('Entities cannot be added before the engine is started.');\n }\n\n var entity = new __WEBPACK_IMPORTED_MODULE_0__entity__[\"a\" /* Entity */](this.componentsCount, this.componentIdMap, this.onEntityKeyChange);\n entity.next = this.entities;\n if (this.entities !== null) {\n this.entities.prev = entity;\n }\n this.entities = entity;\n\n return entity;\n }\n }, {\n key: 'removeEntity',\n value: function removeEntity(entity) {\n if (!this.started) {\n throw new Error('Entities cannot be removed before the engine is started.');\n }\n\n var next = entity.next;\n var prev = entity.prev;\n\n if (next !== null) {\n next.prev = entity.prev;\n }\n if (prev !== null) {\n prev.next = entity.next;\n }\n\n entity.next = null;\n entity.prev = null;\n\n if (this.entities = entity) {\n this.entities = next;\n }\n\n this.removed.push(entity);\n }\n }, {\n key: 'onEntityKeyChange',\n value: function onEntityKeyChange(entity) {\n this.changed.push(entity);\n }\n }, {\n key: 'registerSystem',\n value: function registerSystem(_ref) {\n var query = _ref.query,\n sort = _ref.sort,\n update = _ref.update,\n processEntity = _ref.processEntity;\n\n if (this.started) {\n throw new Error('Cannot register system after entities have been added or the engine was started.');\n }\n\n if (bothOrNone(update, processEntity)) {\n throw new Error('Exactly one of \"update\" and \"processEntity\" must be defined on a system.');\n }\n\n if (!query && processEntity) {\n throw new Error('\"processEntity\" can only be used with a query.');\n }\n\n if (!query && sort) {\n throw new Error('\"sort\" can only be used with a query.');\n }\n\n var system = {\n query: query ? query.bake(this) : false,\n sort: sort,\n sorted: [],\n update: update || function (entities, timeDelta, engine) {\n for (var i = 0; i < entities.length; ++i) {\n processEntity(entities[i], timeDelta, engine);\n }\n }\n };\n\n this.systems.push(system);\n\n if (system.query) {\n this.queries.push(system.query);\n }\n }\n }, {\n key: 'registerComponent',\n value: function registerComponent(component) {\n if (this.started) {\n throw new Error('Cannot register component after entities have been added or the engine was started.');\n }\n\n while (component.id >= this.componentIdMap.length) {\n this.componentIdMap.push(null);\n }\n var index = this.componentIdMap[component.id];\n if (index === null) {\n this.componentIdMap[component.id] = this.componentsCount++;\n }\n }\n }, {\n key: 'start',\n value: function start(init) {\n var _this = this;\n\n // TODO: add pause\n this.started = true;\n this.running = true;\n if (init) {\n init(this);\n }\n\n var now = void 0;\n var lastTime = Date.now();\n var tick = function tick() {\n if (_this.running) {\n now = Date.now();\n _this.update((now - lastTime) / 60);\n lastTime = now;\n }\n requestAnimationFrame(tick);\n };\n requestAnimationFrame(tick);\n }\n }, {\n key: 'update',\n value: function update(timeDelta) {\n for (var i = 0; i < systems.length; ++i) {\n this.runSystem(systems[i], timeDelta);\n this.handleChanges();\n }\n }\n }, {\n key: 'runSystem',\n value: function runSystem(system, timeDelta) {\n if (system.query) {\n var entities = system.query.entities;\n if (system.sort) {\n copyArray(entities, system.sorted);\n entities = system.sorted;\n entities.sort(system.sort);\n }\n system.update(entities, timeDelta, this);\n } else {\n system.update(timeDelta, this);\n }\n }\n }, {\n key: 'handleChanges',\n value: function handleChanges() {\n for (var i = 0; i < this.changed.length; i++) {\n var changed = this.changed[i];\n for (var j = 0; j < this.queries.length; j++) {\n this.queries[j].onChange(changed);\n }\n }\n this.changed.length = 0;\n\n for (var _i = 0; _i < this.removed.length; _i++) {\n var removed = this.removed[_i];\n for (var _j = 0; _j < this.queries.length; _j++) {\n this.queries[_j].onRemove(removed);\n }\n }\n this.changed.length = 0;\n }\n }]);\n\n return Engine;\n}();\n\nfunction bothOrNone(a, b) {\n return a && b || !a && !b;\n}\n\nfunction copyArray(from, to) {\n for (var i = 0; i < from.length; ++i) {\n to[i] = from[i];\n }\n to.length = from.length;\n}\n\n/***/ }),\n/* 3 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"a\", function() { return Entity; });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__key__ = __webpack_require__(0);\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\n\n\nvar index = 0;\n\nvar Entity = function () {\n function Entity(componentCount, componentIdMap, onKeyChanged) {\n _classCallCheck(this, Entity);\n\n this.id = index++;\n this.components = [];\n for (var i = 0; i < componentCount; i++) {\n this.components.push(null);\n }\n this.componentIdMap = componentIdMap;\n this.key = new __WEBPACK_IMPORTED_MODULE_0__key__[\"a\" /* Key */](componentCount);\n\n this.prev = null;\n this.next = null;\n\n this.keyChangeAnnounced = false;\n this.onKeyChanged = onKeyChanged;\n }\n\n _createClass(Entity, [{\n key: 'add',\n value: function add(component) {\n var index = this.getIndexById(component._id);\n if (this.components[index]) {\n throw new Error('Cannot add another instance of the same component.');\n }\n this.key.setBit(index, true);\n this.components[index] = component;\n this.onChange();\n return this;\n }\n }, {\n key: 'has',\n value: function has(Component) {\n var index = this.getIndexById(Component.id);\n return this.components[index] ? true : false;\n }\n }, {\n key: 'get',\n value: function get(Component) {\n var index = this.getIndexById(Component.id);\n var component = this.components[index];\n if (!component) {\n throw new Error('Requested component is not present.');\n }\n return component;\n }\n }, {\n key: 'remove',\n value: function remove(Component) {\n var index = this.getIndexById(Component.id);\n var component = this.components[index];\n if (!component) {\n throw new Error('Cannot remove component instance, because it doesn\\'t exist on target entity.');\n }\n Component.destroy(component);\n this.key.setBit(index, false);\n this.components[index] = null;\n this.onChange();\n return this;\n }\n }, {\n key: 'onChange',\n value: function onChange() {\n if (!this.keyChangeAnnounced) {\n this.onKeyChanged(this);\n }\n this.keyChangeAnnounced = true;\n }\n }, {\n key: 'resetOnKeyChanged',\n value: function resetOnKeyChanged() {\n this.keyChangeAnnounced = false;\n }\n }, {\n key: 'getIndexById',\n value: function getIndexById(componentId) {\n var index = this.componentIdMap[componentId];\n if (index === undefined) {\n throw new Error('Unknown component passed as argument. Components not included in queries should be manually' + ' registered using Engine.registerComponent().');\n }\n return index;\n }\n }]);\n\n return Entity;\n}();\n\n/***/ }),\n/* 4 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n/* harmony export (immutable) */ __webpack_exports__[\"a\"] = component;\nvar index = 0;\n\nfunction component(objectConstructor, objectDestructor) {\n var id = index++;\n objectConstructor.id = id;\n objectConstructor.prototype._id = id;\n\n objectConstructor.destroy = function (instance) {\n if (objectDestructor) {\n objectDestructor.apply(instance);\n }\n };\n\n return objectConstructor;\n}\n\n/***/ }),\n/* 5 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"a\", function() { return Query; });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__key__ = __webpack_require__(0);\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\n\n\nvar Query = {\n all: function all() {\n var _ref;\n\n return (_ref = new UnbakedQuery()).all.apply(_ref, arguments);\n }\n};\n\nvar UnbakedQuery = function () {\n function UnbakedQuery() {\n _classCallCheck(this, UnbakedQuery);\n\n this.components = [];\n }\n\n _createClass(UnbakedQuery, [{\n key: 'all',\n value: function all() {\n var _components;\n\n if (arguments.length === 0) {\n throw new Error('The all() method must take at least one argument.');\n }\n (_components = this.components).push.apply(_components, arguments);\n\n return this;\n }\n }, {\n key: 'bake',\n value: function bake(engine) {\n return new BakedQuery(this, engine);\n }\n }]);\n\n return UnbakedQuery;\n}();\n\nvar BakedQuery = function () {\n function BakedQuery(_ref2, engine) {\n var _this = this;\n\n var components = _ref2.components;\n\n _classCallCheck(this, BakedQuery);\n\n this.key = new __WEBPACK_IMPORTED_MODULE_0__key__[\"a\" /* Key */](engine.componentsCount + components.length);\n components.forEach(function (component) {\n engine.registerComponent(component);\n _this.key.set(engine.componentIdMap[component.id]);\n });\n this.idMap = {};\n this.entities = [];\n }\n\n _createClass(BakedQuery, [{\n key: 'onChange',\n value: function onChange(entity) {\n var index = this.idMap[entity.id];\n var matched = entity.key.matches(this.key);\n if (index === undefined && matched) {\n this.idMap[entity.id] = this.entities.length;\n this.entities.push(entity);\n } else if (index !== undefined && !matched) {\n this.remove(entity, index);\n }\n }\n }, {\n key: 'onRemove',\n value: function onRemove(entity) {\n var index = this.idMap[entity.id];\n if (index !== undefined) {\n this.remove(entity, index);\n }\n }\n }, {\n key: 'remove',\n value: function remove(entity, index) {\n delete this.idMap[entity.id];\n var otherEntity = this.entities.pop();\n if (otherEntity !== entity) {\n this.entities[index] = otherEntity;\n this.idMap[otherEntity.id] = index;\n }\n }\n }]);\n\n return BakedQuery;\n}();\n\n/***/ })\n/******/ ]);\n});\n\n\n// WEBPACK FOOTER //\n// ouyo.js"," \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, {\n \t\t\t\tconfigurable: false,\n \t\t\t\tenumerable: true,\n \t\t\t\tget: getter\n \t\t\t});\n \t\t}\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 1);\n\n\n\n// WEBPACK FOOTER //\n// webpack/bootstrap 4aa62dae87168b909858","const KEY_BITS = 31\r\n\r\nconst POWERS = []\r\nfor(let i = 0; i < KEY_BITS; i++) {\r\n POWERS[i] = 2 ** i\r\n}\r\n\r\nexport class Key {\r\n constructor(length) {\r\n this.values = []\r\n for(let i = 0; i < length / KEY_BITS; i++) {\r\n this.values.push(0)\r\n }\r\n }\r\n\r\n set(index) {\r\n this.setBit(index, true)\r\n return this\r\n }\r\n\r\n unset(index) {\r\n this.setBit(index, false)\r\n return this\r\n }\r\n\r\n setBit(index, value) {\r\n let valueIndex = 0\r\n while(index >= KEY_BITS) {\r\n valueIndex++\r\n index -= KEY_BITS\r\n }\r\n\r\n const previousValue = (this.values[valueIndex] & POWERS[index]) !== 0\r\n if(value && !previousValue) {\r\n this.values[valueIndex] += POWERS[index]\r\n } else if(!value && previousValue) {\r\n this.values[valueIndex] -= POWERS[index]\r\n }\r\n }\r\n\r\n matches(other) {\r\n for(let i = 0; i < other.values.length; i++) {\r\n const currentValue = this.values[i] || 0\r\n const otherValue = other.values[i]\r\n if((currentValue & otherValue) !== otherValue) {\r\n return false\r\n }\r\n }\r\n return true\r\n }\r\n}\r\n\n\n\n// WEBPACK FOOTER //\n// ./src/key.js","import { Entity } from './entity'\n\nexport class Engine {\n constructor() {\n this.entities = null\n this.changed = []\n this.removed = []\n\n this.systems = []\n this.queries = []\n this.componentIdMap = []\n this.componentsCount = 0\n\n this.started = false\n this.running = false\n\n this.onEntityKeyChange = this.onEntityKeyChange.bind(this)\n }\n\n createEntity() {\n if(!this.started) {\n throw new Error('Entities cannot be added before the engine is started.')\n }\n\n const entity = new Entity(\n this.componentsCount,\n this.componentIdMap,\n this.onEntityKeyChange\n )\n entity.next = this.entities\n if(this.entities !== null) {\n this.entities.prev = entity\n }\n this.entities = entity\n\n return entity\n }\n\n removeEntity(entity) {\n if(!this.started) {\n throw new Error('Entities cannot be removed before the engine is started.')\n }\n\n const next = entity.next\n const prev = entity.prev\n\n if(next !== null) {\n next.prev = entity.prev\n }\n if(prev !== null) {\n prev.next = entity.next\n }\n\n entity.next = null\n entity.prev = null\n\n if(this.entities = entity) {\n this.entities = next\n }\n\n this.removed.push(entity)\n }\n\n onEntityKeyChange(entity) {\n this.changed.push(entity)\n }\n\n registerSystem({ query, sort, update, processEntity }) {\n if(this.started) {\n throw new Error('Cannot register system after entities have been added or the engine was started.')\n }\n\n if(bothOrNone(update, processEntity)) {\n throw new Error('Exactly one of \"update\" and \"processEntity\" must be defined on a system.')\n }\n\n if(!query && processEntity) {\n throw new Error('\"processEntity\" can only be used with a query.')\n }\n\n if(!query && sort) {\n throw new Error('\"sort\" can only be used with a query.')\n }\n\n const system = {\n query: query ? query.bake(this) : false,\n sort,\n sorted: [],\n update: update || function(entities, timeDelta, engine) {\n for(let i = 0; i < entities.length; ++i) {\n processEntity(entities[i], timeDelta, engine)\n }\n }\n }\n\n this.systems.push(system)\n\n if(system.query) {\n this.queries.push(system.query)\n }\n }\n\n registerComponent(component) {\n if(this.started) {\n throw new Error('Cannot register component after entities have been added or the engine was started.')\n }\n\n while(component.id >= this.componentIdMap.length) {\n this.componentIdMap.push(null)\n }\n const index = this.componentIdMap[component.id]\n if(index === null) {\n this.componentIdMap[component.id] = this.componentsCount++\n }\n }\n\n start(init) {\n // TODO: add pause\n this.started = true\n this.running = true\n if(init) {\n init(this)\n }\n\n let now\n let lastTime = Date.now()\n const tick = () => {\n if(this.running) {\n now = Date.now()\n this.update((now - lastTime) / 60)\n lastTime = now\n }\n requestAnimationFrame(tick)\n }\n requestAnimationFrame(tick)\n }\n\n update(timeDelta) {\n for(let i = 0; i < systems.length; ++i) {\n this.runSystem(systems[i], timeDelta)\n this.handleChanges()\n }\n }\n\n runSystem(system, timeDelta) {\n if(system.query) {\n let entities = system.query.entities\n if(system.sort) {\n copyArray(entities, system.sorted)\n entities = system.sorted\n entities.sort(system.sort)\n }\n system.update(entities, timeDelta, this)\n } else {\n system.update(timeDelta, this)\n }\n }\n\n handleChanges() {\n for(let i = 0; i < this.changed.length; i++) {\n let changed = this.changed[i]\n for(let j = 0; j < this.queries.length; j++) {\n this.queries[j].onChange(changed)\n }\n }\n this.changed.length = 0\n\n for(let i = 0; i < this.removed.length; i++) {\n let removed = this.removed[i]\n for(let j = 0; j < this.queries.length; j++) {\n this.queries[j].onRemove(removed)\n }\n }\n this.changed.length = 0\n }\n}\n\nfunction bothOrNone(a, b) {\n return (a && b) || (!a && !b)\n}\n\nfunction copyArray(from, to) {\n for(let i = 0; i < from.length; ++i) {\n to[i] = from[i]\n }\n to.length = from.length\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/engine.js","import { Key } from './key'\n\nlet index = 0\n\nexport class Entity {\n constructor(componentCount, componentIdMap, onKeyChanged) {\n this.id = index++\n this.components = []\n for(let i = 0; i < componentCount; i++) {\n this.components.push(null)\n }\n this.componentIdMap = componentIdMap\n this.key = new Key(componentCount)\n\n this.prev = null\n this.next = null\n\n this.keyChangeAnnounced = false\n this.onKeyChanged = onKeyChanged\n }\n\n add(component) {\n const index = this.getIndexById(component._id)\n if(this.components[index]) {\n throw new Error('Cannot add another instance of the same component.')\n }\n this.key.setBit(index, true)\n this.components[index] = component\n this.onChange()\n return this\n }\n\n has(Component) {\n const index = this.getIndexById(Component.id)\n return this.components[index] ? true : false\n }\n\n get(Component) {\n const index = this.getIndexById(Component.id)\n const component = this.components[index]\n if(!component) {\n throw new Error('Requested component is not present.')\n }\n return component\n }\n\n remove(Component) {\n const index = this.getIndexById(Component.id)\n const component = this.components[index]\n if(!component) {\n throw new Error('Cannot remove component instance, because it doesn\\'t exist on target entity.')\n }\n Component.destroy(component)\n this.key.setBit(index, false)\n this.components[index] = null\n this.onChange()\n return this\n }\n\n onChange() {\n if(!this.keyChangeAnnounced) {\n this.onKeyChanged(this)\n }\n this.keyChangeAnnounced = true\n }\n\n resetOnKeyChanged() {\n this.keyChangeAnnounced = false\n }\n\n getIndexById(componentId) {\n const index = this.componentIdMap[componentId]\n if(index === undefined) {\n throw new Error('Unknown component passed as argument. Components not included in queries should be manually' +\n ' registered using Engine.registerComponent().')\n }\n return index\n }\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/entity.js","let index = 0\r\n\r\nexport function component(objectConstructor, objectDestructor) {\r\n const id = index++\n objectConstructor.id = id\n objectConstructor.prototype._id = id\n\n objectConstructor.destroy = function(instance) {\n if(objectDestructor) {\n objectDestructor.apply(instance)\n }\n }\n\r\n return objectConstructor\r\n}\r\n\n\n\n// WEBPACK FOOTER //\n// ./src/component.js","import { Key } from './key'\n\nexport const Query = {\n all(...args) {\n return new UnbakedQuery().all(...args)\n }\n}\n\nclass UnbakedQuery {\n constructor() {\n this.components = []\n }\n\n all(...args) {\n if(args.length === 0) {\n throw new Error('The all() method must take at least one argument.')\n }\n this.components.push(...args)\n\n return this\n }\n\n bake(engine) {\n return new BakedQuery(this, engine)\n }\n}\n\nclass BakedQuery {\n constructor({ components }, engine) {\n this.key = new Key(engine.componentsCount + components.length)\n components.forEach(component => {\n engine.registerComponent(component)\n this.key.set(engine.componentIdMap[component.id])\n })\n this.idMap = {}\n this.entities = []\n }\n\n onChange(entity) {\n const index = this.idMap[entity.id]\n const matched = entity.key.matches(this.key)\n if(index === undefined && matched) {\n this.idMap[entity.id] = this.entities.length\n this.entities.push(entity)\n } else if(index !== undefined && !matched) {\n this.remove(entity, index)\n }\n }\n\n onRemove(entity) {\n const index = this.idMap[entity.id]\n if(index !== undefined) {\n this.remove(entity, index)\n }\n }\n\n remove(entity, index) {\n delete this.idMap[entity.id]\n const otherEntity = this.entities.pop()\n if(otherEntity !== entity) {\n this.entities[index] = otherEntity\n this.idMap[otherEntity.id] = index\n }\n }\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/query.js"],"sourceRoot":""} \ No newline at end of file +{"version":3,"sources":["webpack:///webpack/universalModuleDefinition","webpack:///ouyo.js","webpack:///webpack/bootstrap 63055e3e9da9aef4219d","webpack:///./src/key.js","webpack:///./src/engine.js","webpack:///./src/entity.js","webpack:///./src/component.js","webpack:///./src/query.js"],"names":["root","factory","exports","module","define","amd","this","modules","__webpack_require__","moduleId","installedModules","i","l","call","m","c","d","name","getter","o","Object","defineProperty","configurable","enumerable","get","n","__esModule","object","property","prototype","hasOwnProperty","p","s","__webpack_exports__","_classCallCheck","instance","Constructor","TypeError","Key","_createClass","defineProperties","target","props","length","descriptor","writable","key","protoProps","staticProps","KEY_BITS","POWERS","Math","pow","values","push","value","index","setBit","valueIndex","previousValue","other","currentValue","otherValue","__WEBPACK_IMPORTED_MODULE_0__engine__","__WEBPACK_IMPORTED_MODULE_1__component__","__WEBPACK_IMPORTED_MODULE_2__query__","bothOrNone","a","b","copyArray","from","to","Engine","__WEBPACK_IMPORTED_MODULE_0__entity__","entities","changed","removed","systems","queries","componentIdMap","componentsCount","started","running","onEntityKeyChange","bind","Error","entity","next","prev","_ref","query","sort","update","processEntity","system","bake","sorted","timeDelta","engine","component","id","init","_this","now","lastTime","Date","tick","requestAnimationFrame","runSystem","handleChanges","j","onChange","onRemove","Entity","__WEBPACK_IMPORTED_MODULE_0__key__","componentCount","onKeyChanged","components","keyChangeAnnounced","getIndexById","_id","Component","destroy","componentId","undefined","objectConstructor","objectDestructor","apply","Query","all","UnbakedQuery","arguments","_components","BakedQuery","_ref2","forEach","registerComponent","set","idMap","matched","matches","remove","otherEntity","pop"],"mappings":"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","file":"ouyo.js","sourcesContent":["(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine(\"OUYO\", [], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"OUYO\"] = factory();\n\telse\n\t\troot[\"OUYO\"] = factory();\n})(this, function() {\nreturn \n\n\n// WEBPACK FOOTER //\n// webpack/universalModuleDefinition","(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine(\"OUYO\", [], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"OUYO\"] = factory();\n\telse\n\t\troot[\"OUYO\"] = factory();\n})(this, function() {\nreturn /******/ (function(modules) { // webpackBootstrap\n/******/ \t// The module cache\n/******/ \tvar installedModules = {};\n/******/\n/******/ \t// The require function\n/******/ \tfunction __webpack_require__(moduleId) {\n/******/\n/******/ \t\t// Check if module is in cache\n/******/ \t\tif(installedModules[moduleId]) {\n/******/ \t\t\treturn installedModules[moduleId].exports;\n/******/ \t\t}\n/******/ \t\t// Create a new module (and put it into the cache)\n/******/ \t\tvar module = installedModules[moduleId] = {\n/******/ \t\t\ti: moduleId,\n/******/ \t\t\tl: false,\n/******/ \t\t\texports: {}\n/******/ \t\t};\n/******/\n/******/ \t\t// Execute the module function\n/******/ \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n/******/\n/******/ \t\t// Flag the module as loaded\n/******/ \t\tmodule.l = true;\n/******/\n/******/ \t\t// Return the exports of the module\n/******/ \t\treturn module.exports;\n/******/ \t}\n/******/\n/******/\n/******/ \t// expose the modules object (__webpack_modules__)\n/******/ \t__webpack_require__.m = modules;\n/******/\n/******/ \t// expose the module cache\n/******/ \t__webpack_require__.c = installedModules;\n/******/\n/******/ \t// define getter function for harmony exports\n/******/ \t__webpack_require__.d = function(exports, name, getter) {\n/******/ \t\tif(!__webpack_require__.o(exports, name)) {\n/******/ \t\t\tObject.defineProperty(exports, name, {\n/******/ \t\t\t\tconfigurable: false,\n/******/ \t\t\t\tenumerable: true,\n/******/ \t\t\t\tget: getter\n/******/ \t\t\t});\n/******/ \t\t}\n/******/ \t};\n/******/\n/******/ \t// getDefaultExport function for compatibility with non-harmony modules\n/******/ \t__webpack_require__.n = function(module) {\n/******/ \t\tvar getter = module && module.__esModule ?\n/******/ \t\t\tfunction getDefault() { return module['default']; } :\n/******/ \t\t\tfunction getModuleExports() { return module; };\n/******/ \t\t__webpack_require__.d(getter, 'a', getter);\n/******/ \t\treturn getter;\n/******/ \t};\n/******/\n/******/ \t// Object.prototype.hasOwnProperty.call\n/******/ \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n/******/\n/******/ \t// __webpack_public_path__\n/******/ \t__webpack_require__.p = \"\";\n/******/\n/******/ \t// Load entry module and return exports\n/******/ \treturn __webpack_require__(__webpack_require__.s = 1);\n/******/ })\n/************************************************************************/\n/******/ ([\n/* 0 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"a\", function() { return Key; });\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nvar KEY_BITS = 31;\n\nvar POWERS = [];\nfor (var i = 0; i < KEY_BITS; i++) {\n POWERS[i] = Math.pow(2, i);\n}\n\nvar Key = function () {\n function Key(length) {\n _classCallCheck(this, Key);\n\n this.values = [];\n for (var _i = 0; _i < length / KEY_BITS; _i++) {\n this.values.push(0);\n }\n }\n\n _createClass(Key, [{\n key: \"set\",\n value: function set(index) {\n this.setBit(index, true);\n return this;\n }\n }, {\n key: \"unset\",\n value: function unset(index) {\n this.setBit(index, false);\n return this;\n }\n }, {\n key: \"setBit\",\n value: function setBit(index, value) {\n var valueIndex = 0;\n while (index >= KEY_BITS) {\n valueIndex++;\n index -= KEY_BITS;\n }\n\n var previousValue = (this.values[valueIndex] & POWERS[index]) !== 0;\n if (value && !previousValue) {\n this.values[valueIndex] += POWERS[index];\n } else if (!value && previousValue) {\n this.values[valueIndex] -= POWERS[index];\n }\n }\n }, {\n key: \"matches\",\n value: function matches(other) {\n for (var _i2 = 0; _i2 < other.values.length; _i2++) {\n var currentValue = this.values[_i2] || 0;\n var otherValue = other.values[_i2];\n if ((currentValue & otherValue) !== otherValue) {\n return false;\n }\n }\n return true;\n }\n }]);\n\n return Key;\n}();\n\n/***/ }),\n/* 1 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__engine__ = __webpack_require__(2);\n/* harmony reexport (binding) */ __webpack_require__.d(__webpack_exports__, \"Engine\", function() { return __WEBPACK_IMPORTED_MODULE_0__engine__[\"a\"]; });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__component__ = __webpack_require__(4);\n/* harmony reexport (binding) */ __webpack_require__.d(__webpack_exports__, \"component\", function() { return __WEBPACK_IMPORTED_MODULE_1__component__[\"a\"]; });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__query__ = __webpack_require__(5);\n/* harmony reexport (binding) */ __webpack_require__.d(__webpack_exports__, \"Query\", function() { return __WEBPACK_IMPORTED_MODULE_2__query__[\"a\"]; });\n\n\n\n\n/***/ }),\n/* 2 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"a\", function() { return Engine; });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__entity__ = __webpack_require__(3);\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\n\n\nvar Engine = function () {\n function Engine() {\n _classCallCheck(this, Engine);\n\n this.entities = null;\n this.changed = [];\n this.removed = [];\n\n this.systems = [];\n this.queries = [];\n this.componentIdMap = [];\n this.componentsCount = 0;\n\n this.started = false;\n this.running = false;\n\n this.onEntityKeyChange = this.onEntityKeyChange.bind(this);\n }\n\n _createClass(Engine, [{\n key: 'createEntity',\n value: function createEntity() {\n if (!this.started) {\n throw new Error('Entities cannot be added before the engine is started.');\n }\n\n var entity = new __WEBPACK_IMPORTED_MODULE_0__entity__[\"a\" /* Entity */](this.componentsCount, this.componentIdMap, this.onEntityKeyChange);\n entity.next = this.entities;\n if (this.entities !== null) {\n this.entities.prev = entity;\n }\n this.entities = entity;\n\n return entity;\n }\n }, {\n key: 'removeEntity',\n value: function removeEntity(entity) {\n if (!this.started) {\n throw new Error('Entities cannot be removed before the engine is started.');\n }\n\n var next = entity.next;\n var prev = entity.prev;\n\n if (next !== null) {\n next.prev = entity.prev;\n }\n if (prev !== null) {\n prev.next = entity.next;\n }\n\n entity.next = null;\n entity.prev = null;\n\n if (this.entities = entity) {\n this.entities = next;\n }\n\n this.removed.push(entity);\n }\n }, {\n key: 'onEntityKeyChange',\n value: function onEntityKeyChange(entity) {\n this.changed.push(entity);\n }\n }, {\n key: 'registerSystem',\n value: function registerSystem(_ref) {\n var query = _ref.query,\n sort = _ref.sort,\n update = _ref.update,\n processEntity = _ref.processEntity;\n\n if (this.started) {\n throw new Error('Cannot register system after entities have been added or the engine was started.');\n }\n\n if (bothOrNone(update, processEntity)) {\n throw new Error('Exactly one of \"update\" and \"processEntity\" must be defined on a system.');\n }\n\n if (!query && processEntity) {\n throw new Error('\"processEntity\" can only be used with a query.');\n }\n\n if (!query && sort) {\n throw new Error('\"sort\" can only be used with a query.');\n }\n\n var system = {\n query: query ? query.bake(this) : false,\n sort: sort,\n sorted: [],\n update: update || function (entities, timeDelta, engine) {\n for (var i = 0; i < entities.length; ++i) {\n processEntity(entities[i], timeDelta, engine);\n }\n }\n };\n\n this.systems.push(system);\n\n if (system.query) {\n this.queries.push(system.query);\n }\n }\n }, {\n key: 'registerComponent',\n value: function registerComponent(component) {\n if (this.started) {\n throw new Error('Cannot register component after entities have been added or the engine was started.');\n }\n\n while (component.id >= this.componentIdMap.length) {\n this.componentIdMap.push(null);\n }\n var index = this.componentIdMap[component.id];\n if (index === null) {\n this.componentIdMap[component.id] = this.componentsCount++;\n }\n }\n }, {\n key: 'start',\n value: function start(init) {\n var _this = this;\n\n // TODO: add pause\n this.started = true;\n this.running = true;\n if (init) {\n init(this);\n }\n\n var now = void 0;\n var lastTime = Date.now();\n var tick = function tick() {\n if (_this.running) {\n now = Date.now();\n _this.update((now - lastTime) / 60);\n lastTime = now;\n }\n requestAnimationFrame(tick);\n };\n requestAnimationFrame(tick);\n }\n }, {\n key: 'update',\n value: function update(timeDelta) {\n for (var i = 0; i < this.systems.length; ++i) {\n this.runSystem(this.systems[i], timeDelta);\n this.handleChanges();\n }\n }\n }, {\n key: 'runSystem',\n value: function runSystem(system, timeDelta) {\n if (system.query) {\n var entities = system.query.entities;\n if (system.sort) {\n copyArray(entities, system.sorted);\n entities = system.sorted;\n entities.sort(system.sort);\n }\n system.update(entities, timeDelta, this);\n } else {\n system.update(timeDelta, this);\n }\n }\n }, {\n key: 'handleChanges',\n value: function handleChanges() {\n for (var i = 0; i < this.changed.length; i++) {\n var changed = this.changed[i];\n for (var j = 0; j < this.queries.length; j++) {\n this.queries[j].onChange(changed);\n }\n }\n this.changed.length = 0;\n\n for (var _i = 0; _i < this.removed.length; _i++) {\n var removed = this.removed[_i];\n for (var _j = 0; _j < this.queries.length; _j++) {\n this.queries[_j].onRemove(removed);\n }\n }\n this.changed.length = 0;\n }\n }]);\n\n return Engine;\n}();\n\nfunction bothOrNone(a, b) {\n return a && b || !a && !b;\n}\n\nfunction copyArray(from, to) {\n for (var i = 0; i < from.length; ++i) {\n to[i] = from[i];\n }\n to.length = from.length;\n}\n\n/***/ }),\n/* 3 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"a\", function() { return Entity; });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__key__ = __webpack_require__(0);\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\n\n\nvar index = 0;\n\nvar Entity = function () {\n function Entity(componentCount, componentIdMap, onKeyChanged) {\n _classCallCheck(this, Entity);\n\n this.id = index++;\n this.components = [];\n for (var i = 0; i < componentCount; i++) {\n this.components.push(null);\n }\n this.componentIdMap = componentIdMap;\n this.key = new __WEBPACK_IMPORTED_MODULE_0__key__[\"a\" /* Key */](componentCount);\n\n this.prev = null;\n this.next = null;\n\n this.keyChangeAnnounced = false;\n this.onKeyChanged = onKeyChanged;\n }\n\n _createClass(Entity, [{\n key: 'add',\n value: function add(component) {\n var index = this.getIndexById(component._id);\n if (this.components[index]) {\n throw new Error('Cannot add another instance of the same component.');\n }\n this.key.setBit(index, true);\n this.components[index] = component;\n this.onChange();\n return this;\n }\n }, {\n key: 'has',\n value: function has(Component) {\n var index = this.getIndexById(Component.id);\n return this.components[index] ? true : false;\n }\n }, {\n key: 'get',\n value: function get(Component) {\n var index = this.getIndexById(Component.id);\n var component = this.components[index];\n if (!component) {\n throw new Error('Requested component is not present.');\n }\n return component;\n }\n }, {\n key: 'remove',\n value: function remove(Component) {\n var index = this.getIndexById(Component.id);\n var component = this.components[index];\n if (!component) {\n throw new Error('Cannot remove component instance, because it doesn\\'t exist on target entity.');\n }\n Component.destroy(component);\n this.key.setBit(index, false);\n this.components[index] = null;\n this.onChange();\n return this;\n }\n }, {\n key: 'onChange',\n value: function onChange() {\n if (!this.keyChangeAnnounced) {\n this.onKeyChanged(this);\n }\n this.keyChangeAnnounced = true;\n }\n }, {\n key: 'resetOnKeyChanged',\n value: function resetOnKeyChanged() {\n this.keyChangeAnnounced = false;\n }\n }, {\n key: 'getIndexById',\n value: function getIndexById(componentId) {\n var index = this.componentIdMap[componentId];\n if (index === undefined) {\n throw new Error('Unknown component passed as argument. Components not included in queries should be manually' + ' registered using Engine.registerComponent().');\n }\n return index;\n }\n }]);\n\n return Entity;\n}();\n\n/***/ }),\n/* 4 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n/* harmony export (immutable) */ __webpack_exports__[\"a\"] = component;\nvar index = 0;\n\nfunction component(objectConstructor, objectDestructor) {\n var id = index++;\n objectConstructor.id = id;\n objectConstructor.prototype._id = id;\n\n objectConstructor.destroy = function (instance) {\n if (objectDestructor) {\n objectDestructor.apply(instance);\n }\n };\n\n return objectConstructor;\n}\n\n/***/ }),\n/* 5 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"a\", function() { return Query; });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__key__ = __webpack_require__(0);\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\n\n\nvar Query = {\n all: function all() {\n var _ref;\n\n return (_ref = new UnbakedQuery()).all.apply(_ref, arguments);\n }\n};\n\nvar UnbakedQuery = function () {\n function UnbakedQuery() {\n _classCallCheck(this, UnbakedQuery);\n\n this.components = [];\n }\n\n _createClass(UnbakedQuery, [{\n key: 'all',\n value: function all() {\n var _components;\n\n if (arguments.length === 0) {\n throw new Error('The all() method must take at least one argument.');\n }\n (_components = this.components).push.apply(_components, arguments);\n\n return this;\n }\n }, {\n key: 'bake',\n value: function bake(engine) {\n return new BakedQuery(this, engine);\n }\n }]);\n\n return UnbakedQuery;\n}();\n\nvar BakedQuery = function () {\n function BakedQuery(_ref2, engine) {\n var _this = this;\n\n var components = _ref2.components;\n\n _classCallCheck(this, BakedQuery);\n\n this.key = new __WEBPACK_IMPORTED_MODULE_0__key__[\"a\" /* Key */](engine.componentsCount + components.length);\n components.forEach(function (component) {\n engine.registerComponent(component);\n _this.key.set(engine.componentIdMap[component.id]);\n });\n this.idMap = {};\n this.entities = [];\n }\n\n _createClass(BakedQuery, [{\n key: 'onChange',\n value: function onChange(entity) {\n var index = this.idMap[entity.id];\n var matched = entity.key.matches(this.key);\n if (index === undefined && matched) {\n this.idMap[entity.id] = this.entities.length;\n this.entities.push(entity);\n } else if (index !== undefined && !matched) {\n this.remove(entity, index);\n }\n }\n }, {\n key: 'onRemove',\n value: function onRemove(entity) {\n var index = this.idMap[entity.id];\n if (index !== undefined) {\n this.remove(entity, index);\n }\n }\n }, {\n key: 'remove',\n value: function remove(entity, index) {\n delete this.idMap[entity.id];\n var otherEntity = this.entities.pop();\n if (otherEntity !== entity) {\n this.entities[index] = otherEntity;\n this.idMap[otherEntity.id] = index;\n }\n }\n }]);\n\n return BakedQuery;\n}();\n\n/***/ })\n/******/ ]);\n});\n\n\n// WEBPACK FOOTER //\n// ouyo.js"," \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, {\n \t\t\t\tconfigurable: false,\n \t\t\t\tenumerable: true,\n \t\t\t\tget: getter\n \t\t\t});\n \t\t}\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 1);\n\n\n\n// WEBPACK FOOTER //\n// webpack/bootstrap 63055e3e9da9aef4219d","const KEY_BITS = 31\r\n\r\nconst POWERS = []\r\nfor(let i = 0; i < KEY_BITS; i++) {\r\n POWERS[i] = 2 ** i\r\n}\r\n\r\nexport class Key {\r\n constructor(length) {\r\n this.values = []\r\n for(let i = 0; i < length / KEY_BITS; i++) {\r\n this.values.push(0)\r\n }\r\n }\r\n\r\n set(index) {\r\n this.setBit(index, true)\r\n return this\r\n }\r\n\r\n unset(index) {\r\n this.setBit(index, false)\r\n return this\r\n }\r\n\r\n setBit(index, value) {\r\n let valueIndex = 0\r\n while(index >= KEY_BITS) {\r\n valueIndex++\r\n index -= KEY_BITS\r\n }\r\n\r\n const previousValue = (this.values[valueIndex] & POWERS[index]) !== 0\r\n if(value && !previousValue) {\r\n this.values[valueIndex] += POWERS[index]\r\n } else if(!value && previousValue) {\r\n this.values[valueIndex] -= POWERS[index]\r\n }\r\n }\r\n\r\n matches(other) {\r\n for(let i = 0; i < other.values.length; i++) {\r\n const currentValue = this.values[i] || 0\r\n const otherValue = other.values[i]\r\n if((currentValue & otherValue) !== otherValue) {\r\n return false\r\n }\r\n }\r\n return true\r\n }\r\n}\r\n\n\n\n// WEBPACK FOOTER //\n// ./src/key.js","import { Entity } from './entity'\n\nexport class Engine {\n constructor() {\n this.entities = null\n this.changed = []\n this.removed = []\n\n this.systems = []\n this.queries = []\n this.componentIdMap = []\n this.componentsCount = 0\n\n this.started = false\n this.running = false\n\n this.onEntityKeyChange = this.onEntityKeyChange.bind(this)\n }\n\n createEntity() {\n if(!this.started) {\n throw new Error('Entities cannot be added before the engine is started.')\n }\n\n const entity = new Entity(\n this.componentsCount,\n this.componentIdMap,\n this.onEntityKeyChange\n )\n entity.next = this.entities\n if(this.entities !== null) {\n this.entities.prev = entity\n }\n this.entities = entity\n\n return entity\n }\n\n removeEntity(entity) {\n if(!this.started) {\n throw new Error('Entities cannot be removed before the engine is started.')\n }\n\n const next = entity.next\n const prev = entity.prev\n\n if(next !== null) {\n next.prev = entity.prev\n }\n if(prev !== null) {\n prev.next = entity.next\n }\n\n entity.next = null\n entity.prev = null\n\n if(this.entities = entity) {\n this.entities = next\n }\n\n this.removed.push(entity)\n }\n\n onEntityKeyChange(entity) {\n this.changed.push(entity)\n }\n\n registerSystem({ query, sort, update, processEntity }) {\n if(this.started) {\n throw new Error('Cannot register system after entities have been added or the engine was started.')\n }\n\n if(bothOrNone(update, processEntity)) {\n throw new Error('Exactly one of \"update\" and \"processEntity\" must be defined on a system.')\n }\n\n if(!query && processEntity) {\n throw new Error('\"processEntity\" can only be used with a query.')\n }\n\n if(!query && sort) {\n throw new Error('\"sort\" can only be used with a query.')\n }\n\n const system = {\n query: query ? query.bake(this) : false,\n sort,\n sorted: [],\n update: update || function(entities, timeDelta, engine) {\n for(let i = 0; i < entities.length; ++i) {\n processEntity(entities[i], timeDelta, engine)\n }\n }\n }\n\n this.systems.push(system)\n\n if(system.query) {\n this.queries.push(system.query)\n }\n }\n\n registerComponent(component) {\n if(this.started) {\n throw new Error('Cannot register component after entities have been added or the engine was started.')\n }\n\n while(component.id >= this.componentIdMap.length) {\n this.componentIdMap.push(null)\n }\n const index = this.componentIdMap[component.id]\n if(index === null) {\n this.componentIdMap[component.id] = this.componentsCount++\n }\n }\n\n start(init) {\n // TODO: add pause\n this.started = true\n this.running = true\n if(init) {\n init(this)\n }\n\n let now\n let lastTime = Date.now()\n const tick = () => {\n if(this.running) {\n now = Date.now()\n this.update((now - lastTime) / 60)\n lastTime = now\n }\n requestAnimationFrame(tick)\n }\n requestAnimationFrame(tick)\n }\n\n update(timeDelta) {\n for(let i = 0; i < this.systems.length; ++i) {\n this.runSystem(this.systems[i], timeDelta)\n this.handleChanges()\n }\n }\n\n runSystem(system, timeDelta) {\n if(system.query) {\n let entities = system.query.entities\n if(system.sort) {\n copyArray(entities, system.sorted)\n entities = system.sorted\n entities.sort(system.sort)\n }\n system.update(entities, timeDelta, this)\n } else {\n system.update(timeDelta, this)\n }\n }\n\n handleChanges() {\n for(let i = 0; i < this.changed.length; i++) {\n let changed = this.changed[i]\n for(let j = 0; j < this.queries.length; j++) {\n this.queries[j].onChange(changed)\n }\n }\n this.changed.length = 0\n\n for(let i = 0; i < this.removed.length; i++) {\n let removed = this.removed[i]\n for(let j = 0; j < this.queries.length; j++) {\n this.queries[j].onRemove(removed)\n }\n }\n this.changed.length = 0\n }\n}\n\nfunction bothOrNone(a, b) {\n return (a && b) || (!a && !b)\n}\n\nfunction copyArray(from, to) {\n for(let i = 0; i < from.length; ++i) {\n to[i] = from[i]\n }\n to.length = from.length\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/engine.js","import { Key } from './key'\n\nlet index = 0\n\nexport class Entity {\n constructor(componentCount, componentIdMap, onKeyChanged) {\n this.id = index++\n this.components = []\n for(let i = 0; i < componentCount; i++) {\n this.components.push(null)\n }\n this.componentIdMap = componentIdMap\n this.key = new Key(componentCount)\n\n this.prev = null\n this.next = null\n\n this.keyChangeAnnounced = false\n this.onKeyChanged = onKeyChanged\n }\n\n add(component) {\n const index = this.getIndexById(component._id)\n if(this.components[index]) {\n throw new Error('Cannot add another instance of the same component.')\n }\n this.key.setBit(index, true)\n this.components[index] = component\n this.onChange()\n return this\n }\n\n has(Component) {\n const index = this.getIndexById(Component.id)\n return this.components[index] ? true : false\n }\n\n get(Component) {\n const index = this.getIndexById(Component.id)\n const component = this.components[index]\n if(!component) {\n throw new Error('Requested component is not present.')\n }\n return component\n }\n\n remove(Component) {\n const index = this.getIndexById(Component.id)\n const component = this.components[index]\n if(!component) {\n throw new Error('Cannot remove component instance, because it doesn\\'t exist on target entity.')\n }\n Component.destroy(component)\n this.key.setBit(index, false)\n this.components[index] = null\n this.onChange()\n return this\n }\n\n onChange() {\n if(!this.keyChangeAnnounced) {\n this.onKeyChanged(this)\n }\n this.keyChangeAnnounced = true\n }\n\n resetOnKeyChanged() {\n this.keyChangeAnnounced = false\n }\n\n getIndexById(componentId) {\n const index = this.componentIdMap[componentId]\n if(index === undefined) {\n throw new Error('Unknown component passed as argument. Components not included in queries should be manually' +\n ' registered using Engine.registerComponent().')\n }\n return index\n }\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/entity.js","let index = 0\r\n\r\nexport function component(objectConstructor, objectDestructor) {\r\n const id = index++\n objectConstructor.id = id\n objectConstructor.prototype._id = id\n\n objectConstructor.destroy = function(instance) {\n if(objectDestructor) {\n objectDestructor.apply(instance)\n }\n }\n\r\n return objectConstructor\r\n}\r\n\n\n\n// WEBPACK FOOTER //\n// ./src/component.js","import { Key } from './key'\n\nexport const Query = {\n all(...args) {\n return new UnbakedQuery().all(...args)\n }\n}\n\nclass UnbakedQuery {\n constructor() {\n this.components = []\n }\n\n all(...args) {\n if(args.length === 0) {\n throw new Error('The all() method must take at least one argument.')\n }\n this.components.push(...args)\n\n return this\n }\n\n bake(engine) {\n return new BakedQuery(this, engine)\n }\n}\n\nclass BakedQuery {\n constructor({ components }, engine) {\n this.key = new Key(engine.componentsCount + components.length)\n components.forEach(component => {\n engine.registerComponent(component)\n this.key.set(engine.componentIdMap[component.id])\n })\n this.idMap = {}\n this.entities = []\n }\n\n onChange(entity) {\n const index = this.idMap[entity.id]\n const matched = entity.key.matches(this.key)\n if(index === undefined && matched) {\n this.idMap[entity.id] = this.entities.length\n this.entities.push(entity)\n } else if(index !== undefined && !matched) {\n this.remove(entity, index)\n }\n }\n\n onRemove(entity) {\n const index = this.idMap[entity.id]\n if(index !== undefined) {\n this.remove(entity, index)\n }\n }\n\n remove(entity, index) {\n delete this.idMap[entity.id]\n const otherEntity = this.entities.pop()\n if(otherEntity !== entity) {\n this.entities[index] = otherEntity\n this.idMap[otherEntity.id] = index\n }\n }\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/query.js"],"sourceRoot":""} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index c0ef254..b311604 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "ouyo", - "version": "0.1.0", + "version": "0.3.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index f7c81fa..943e874 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,8 @@ "build": "webpack -p --progress", "examples": "http-server -o -a localhost -i", "test": "jest", - "testwatch": "jest --watchAll" + "testwatch": "jest --watchAll", + "examples": "http-server -o" }, "devDependencies": { "babel-core": "^6.25.0", @@ -32,6 +33,8 @@ "webpack": "^3.4.1" }, "jest": { - "moduleFileExtensions": ["js"] + "moduleFileExtensions": [ + "js" + ] } } diff --git a/src/engine.js b/src/engine.js index d23ff69..0fadf9a 100644 --- a/src/engine.js +++ b/src/engine.js @@ -136,8 +136,8 @@ export class Engine { } update(timeDelta) { - for(let i = 0; i < systems.length; ++i) { - this.runSystem(systems[i], timeDelta) + for(let i = 0; i < this.systems.length; ++i) { + this.runSystem(this.systems[i], timeDelta) this.handleChanges() } }