]> git.r.bdr.sh - rbdr/sumo/commitdiff
Add control via keyboard (#6)
authorRubén Beltrán del Río <redacted>
Mon, 23 Apr 2018 10:13:08 +0000 (05:13 -0500)
committerGitHub <redacted>
Mon, 23 Apr 2018 10:13:08 +0000 (05:13 -0500)
* Correct angle documentation

* Correct body component doc

* Add a config module with px 2 meters

* Create component to map input

* Add components for mappable actions

* Add component for elastic manipulation

* Add node to modify physics

* Add controllable node

* Add dasher node

* Add control mapper system

* Add component to limit velocity

* Add node for limiting velocity

* Add systems to move and dash

* Use meters in physics systems

* Correct documentation in render system

* Add elastic manipulation system

* Update factories to use new components

* Update main app to use new systems

* Ignore dist dir

* Also ignore cache

* Ignore personal configuration files

* Add system to reduce velocity

* Add changelog

26 files changed:
.gitignore
CHANGELOG.md
lib/components/angle.js
lib/components/body.js
lib/components/control_map.js [new file with mode: 0644]
lib/components/dash.js [new file with mode: 0644]
lib/components/elastic.js [new file with mode: 0644]
lib/components/force.js [new file with mode: 0644]
lib/components/max_velocity.js [new file with mode: 0644]
lib/config.js [new file with mode: 0644]
lib/factories/pixi.js
lib/factories/sumo.js
lib/nodes/controllable.js [new file with mode: 0644]
lib/nodes/dasher.js [new file with mode: 0644]
lib/nodes/elastic.js [new file with mode: 0644]
lib/nodes/limited_velocity.js [new file with mode: 0644]
lib/nodes/physical_with_external_force.js [new file with mode: 0644]
lib/sumo.js
lib/systems/apply_force.js [new file with mode: 0644]
lib/systems/control_mapper.js [new file with mode: 0644]
lib/systems/dash.js [new file with mode: 0644]
lib/systems/elastic.js [new file with mode: 0644]
lib/systems/physics_to_attributes.js
lib/systems/physics_world_control.js
lib/systems/reduce_velocity.js [new file with mode: 0644]
lib/systems/render.js

index 09b28ab0e755c164a7bc22e41f6c10e23e61705f..b59ad8222656b61342f90a4d418bd349a02368b6 100644 (file)
@@ -60,4 +60,8 @@ typings/
 .DS_Store
 
 # Generated assets
 .DS_Store
 
 # Generated assets
-assets
+dist
+.cache
+
+# Personal config files
+.rgignore
index 10fc8156539e548f13fd2dec5d4744557cfbe002..83de4a76e060d62797da71ac1fade250d89cd58b 100644 (file)
@@ -17,5 +17,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
 - Pixi powered renderer system
 - Matter-js powered physics system
 - Scripts to setup hooks, linting, docs, and bundling
 - Pixi powered renderer system
 - Matter-js powered physics system
 - Scripts to setup hooks, linting, docs, and bundling
+- Keyboard Support
 
 [Unreleased]: https://github.com/rbdr/sumo/compare/master...develop
 
 [Unreleased]: https://github.com/rbdr/sumo/compare/master...develop
index 4de976455d40fb7ac892410a1e5d2101e7ac8031..65feeab9406dcd091b2a0562bc7d7ec3ae3dcb64 100644 (file)
@@ -13,9 +13,9 @@ export default class AngleComponent extends Component {
     super(config);
 
     /**
     super(config);
 
     /**
-     * The properthy that holds the pixi container
+     * The properthy that holds the angle
      *
      *
-     * @property {external:MatterJs.Angle} angle
+     * @property {number} angle
      * @instance
      * @memberof AngleComponent
      */
      * @instance
      * @memberof AngleComponent
      */
index 7438dc35e263e267302417c120647db6c94a5c37..fc17e1741ed48af3fbee37d5f1fa1e91b1f26968 100644 (file)
@@ -13,7 +13,7 @@ export default class BodyComponent extends Component {
     super(config);
 
     /**
     super(config);
 
     /**
-     * The properthy that holds the pixi container
+     * The properthy that holds the matterjs body
      *
      * @property {external:MatterJs.Body} body
      * @instance
      *
      * @property {external:MatterJs.Body} body
      * @instance
diff --git a/lib/components/control_map.js b/lib/components/control_map.js
new file mode 100644 (file)
index 0000000..cb447d0
--- /dev/null
@@ -0,0 +1,57 @@
+import { Component } from '@serpentity/serpentity';
+
+/**
+ * The contorl mapping object, holds a source and target for the action.
+ *
+ * @typedef tControlMap
+ * @type object
+ *
+ * @property {tControlSource} source the source input for the action
+ * @property {tControlTarget} target the target property for the action
+ */
+
+/**
+ * The definition of an input source
+ *
+ * @typedef tControlSource
+ * @type object
+ *
+ * @property {string} type type of input, can be keyboard or gamepad (only keyboard supported atm)
+ * @property {number} gamepadNumber the number of gamepad to use (unsupported)
+ * @property {string} gamepadInputSource either axes or buttons
+ * @property {number} index the input index or keycode
+ */
+
+/**
+ * The definition of an input target
+ *
+ * @typedef tControlTarget
+ * @type object
+ *
+ * @property {external:Serpentity:Component} component the component affected by this
+ * @property {string} property property to affect. For nested properties use dot.notation
+ * @property {[function]} value a function that takes in the input and outputs a transformed value
+ */
+
+/**
+ * Component that stores the state of motion controls to properties
+ *
+ * @extends {external:Serpentity.Component}
+ * @class ControlMapComponent
+ * @param {object} config a configuration object to extend.
+ */
+export default class ControlMapComponent extends Component {
+  constructor(config) {
+
+    super(config);
+
+    /**
+     * The map of actions and controls. An array of mappings
+     *
+     * @property {Array<tControlMap>} map
+     * @instance
+     * @memberof ControlMapComponent
+     */
+    this.map = this.map || [];
+  }
+};
diff --git a/lib/components/dash.js b/lib/components/dash.js
new file mode 100644 (file)
index 0000000..de1fe2c
--- /dev/null
@@ -0,0 +1,51 @@
+import { Component } from '@serpentity/serpentity';
+
+/**
+ * Component that stores a dash skill
+ *
+ * @extends {external:Serpentity.Component}
+ * @class DashComponent
+ * @param {object} config a configuration object to extend.
+ */
+export default class DashComponent extends Component {
+  constructor(config) {
+
+    super(config);
+
+    /**
+     * The properthy that holds the dash state
+     *
+     * @property {boolean} dashing
+     * @instance
+     * @memberof DashComponent
+     */
+    this.dashing = this.dashing || false;
+
+    /**
+     * Whether the dash is locked from occuring
+     *
+     * @property {boolean} locked
+     * @instance
+     * @memberof DashComponent
+     */
+    this.locked = this.locked || false;
+
+    /**
+     * Cooldown before lock is removed
+     *
+     * @property {number} cooldown
+     * @instance
+     * @memberof DashComponent
+     */
+    this.cooldown = this.cooldown || 4000;
+
+    /**
+     * Current cooldown
+     *
+     * @property {number} currentCooldown
+     * @instance
+     * @memberof DashComponent
+     */
+    this.currentCooldown = 0;
+  }
+};
diff --git a/lib/components/elastic.js b/lib/components/elastic.js
new file mode 100644 (file)
index 0000000..ee41cf8
--- /dev/null
@@ -0,0 +1,12 @@
+import { Component } from '@serpentity/serpentity';
+
+/**
+ * Component that stores elastic properties
+ *
+ * @extends {external:Serpentity.Component}
+ * @class ElasticComponent
+ * @param {object} config a configuration object to extend.
+ */
+export default class ElasticComponent extends Component {
+};
+
diff --git a/lib/components/force.js b/lib/components/force.js
new file mode 100644 (file)
index 0000000..f0e460c
--- /dev/null
@@ -0,0 +1,43 @@
+import { Component } from '@serpentity/serpentity';
+
+/**
+ * Component that stores a force vector
+ *
+ * @extends {external:Serpentity.Component}
+ * @class ForceComponent
+ * @param {object} config a configuration object to extend.
+ */
+export default class ForceComponent extends Component {
+  constructor(config) {
+
+    super(config);
+
+    /**
+     * The properthy that holds the x component of the vector
+     *
+     * @property {number} x
+     * @instance
+     * @memberof AngleComponent
+     */
+    this.x = this.x || 0;
+
+    /**
+     * The properthy that holds the y component of the vector
+     *
+     * @property {number} y
+     * @instance
+     * @memberof AngleComponent
+     */
+
+    this.y = this.y || 0;
+    /**
+     * The properthy that holds the z component of the vector
+     *
+     * @property {number} z
+     * @instance
+     * @memberof AngleComponent
+     */
+    this.z = this.z || 0;
+  }
+};
+
diff --git a/lib/components/max_velocity.js b/lib/components/max_velocity.js
new file mode 100644 (file)
index 0000000..70bf343
--- /dev/null
@@ -0,0 +1,25 @@
+import { Component } from '@serpentity/serpentity';
+
+/**
+ * Component that stores max velocity constraint
+ *
+ * @extends {external:Serpentity.Component}
+ * @class MaxVelocityComponent
+ * @param {object} config a configuration object to extend.
+ */
+export default class MaxVelocityComponent extends Component {
+  constructor(config) {
+
+    super(config);
+
+    /**
+     * The properthy that holds the max velocity
+     *
+     * @property {number} maxVelocity
+     * @instance
+     * @memberof MaxVelocityComponent
+     */
+    this.maxVelocity = this.maxVelocity || 20;
+  }
+};
+
diff --git a/lib/config.js b/lib/config.js
new file mode 100644 (file)
index 0000000..ba02ed8
--- /dev/null
@@ -0,0 +1,16 @@
+/**
+ Changes the stiffness on the node when it's less extended
+ *
+ * @name Config
+ * @type object
+ */
+export default {
+
+  /**
+   * How many pixels to use per meter
+   *
+   * @property {number} meterSize
+   * @memberof Config
+   */
+  meterSize: 25
+};
index 851a07459b11e7ff6db427b956c461f86cc63d90..839e59315620949541cf3b2f502469349d4bbd44 100644 (file)
@@ -73,6 +73,34 @@ export default {
   createEmptyGraphic(config) {
 
     return new Graphics();
   createEmptyGraphic(config) {
 
     return new Graphics();
+  },
+
+  /**
+   * Creates a harness graphic
+   *
+   * @function createHarness
+   * @memberof PixiFactory
+   * @return {external:CreateJs.Container} the created container
+   */
+  createHarness(config) {
+
+    const radius = config.radius;
+
+    const lineThickness = 10;
+
+    // The body
+    const body = new Graphics();
+    body.lineStyle(lineThickness, 0xe1e1e1, 1)
+      .drawCircle(0, 0, radius);
+
+    const center = new Graphics();
+    center.beginFill(0xf1f1f1)
+      .drawCircle(0, 0, radius - lineThickness / 2)
+      .endFill();
+
+    body.addChild(center);
+
+    return body;
   }
 };
 
   }
 };
 
index d1f31f0628bf04ddcbbbb1b3ad9322e74737a982..ea5a71c753ace339f301377cd2cc2526b0b1d3f3 100644 (file)
@@ -1,12 +1,21 @@
 import { Bodies, Constraint } from 'matter-js';
 import { Entity } from '@serpentity/serpentity';
 
 import { Bodies, Constraint } from 'matter-js';
 import { Entity } from '@serpentity/serpentity';
 
+// Components
+
 import AngleComponent from '../components/angle';
 import BodyComponent from '../components/body';
 import AngleComponent from '../components/angle';
 import BodyComponent from '../components/body';
+import ControlMapComponent from '../components/control_map';
 import CoupledEntitiesComponent from '../components/coupled_entities';
 import CoupledEntitiesComponent from '../components/coupled_entities';
+import DashComponent from '../components/dash';
+import ElasticComponent from '../components/elastic';
+import ForceComponent from '../components/force';
+import MaxVelocityComponent from '../components/max_velocity';
 import PositionComponent from '@serpentity/components.position';
 import PixiContainerComponent from '../components/pixi_container';
 import PositionComponent from '@serpentity/components.position';
 import PixiContainerComponent from '../components/pixi_container';
+
 import PixiFactory from '../factories/pixi';
 import PixiFactory from '../factories/pixi';
+import Config from '../config';
 
 const internals = {
   kNoEntityError: 'Entity Not Found: This method requires entityA and entityB to be set in the config.',
 
 const internals = {
   kNoEntityError: 'Entity Not Found: This method requires entityA and entityB to be set in the config.',
@@ -45,6 +54,17 @@ export default {
     entity.addComponent(new AngleComponent(config.angle));
     const angle = entity.getComponent(AngleComponent);
 
     entity.addComponent(new AngleComponent(config.angle));
     const angle = entity.getComponent(AngleComponent);
 
+    entity.addComponent(new ForceComponent(config.force));
+
+    config.maxVelocity = {
+      maxVelocity: 12
+    };
+    entity.addComponent(new MaxVelocityComponent(config.maxVelocity));
+
+    // CONTROLS & ABILITIES
+
+    entity.addComponent(new DashComponent(config.dash));
+
     // RENDERING
 
     const radius = 25;
     // RENDERING
 
     const radius = 25;
@@ -59,15 +79,18 @@ export default {
 
     // PHYSICS
 
 
     // PHYSICS
 
-    const frictionAir = 0.00001;
-    const friction = 0.00001;
+    const frictionAir = 0.02;
+    const friction = 1;
+    const frictionStatic = 1;
     const restitution = 0.9;
     const restitution = 0.9;
-    const density = 0.5;
+    const density = 1;
 
 
-    const body = Bodies.circle(position.x, position.y, radius, {
+    const body = Bodies.circle(position.x / Config.meterSize, position.y / Config.meterSize, radius / Config.meterSize, {
+      label: 'Sumo body',
       angle: angle.angle,
       density,
       frictionAir,
       angle: angle.angle,
       density,
       frictionAir,
+      frictionStatic,
       friction,
       restitution
     });
       friction,
       restitution
     });
@@ -115,18 +138,16 @@ export default {
 
     const bodyA = config.entityA.getComponent(BodyComponent).body;
     const bodyB = config.entityB.getComponent(BodyComponent).body;
 
     const bodyA = config.entityA.getComponent(BodyComponent).body;
     const bodyB = config.entityB.getComponent(BodyComponent).body;
-    const damping = 0.01;
-    const length = 100;
-    const stiffness = 0.0001;
-    const angularStiffness = 0.0001;
+    const damping = 0;
+    const length = 100 / Config.meterSize;
+    const stiffness = 0.001;
 
     const body = Constraint.create({
       bodyA,
       bodyB,
       damping,
       length,
 
     const body = Constraint.create({
       bodyA,
       bodyB,
       damping,
       length,
-      stiffness,
-      angularStiffness
+      stiffness
     });
     entity.addComponent(new BodyComponent({ body }));
 
     });
     entity.addComponent(new BodyComponent({ body }));
 
@@ -134,6 +155,143 @@ export default {
       coupledEntities: [config.entityA, config.entityB]
     }));
 
       coupledEntities: [config.entityA, config.entityB]
     }));
 
+    entity.addComponent(new ElasticComponent());
+
+    if (engine) {
+      engine.addEntity(entity);
+    }
+
+    return entity;
+  },
+
+  /**
+   * Creates a controllable sumo entity and adds it to the engine. Can override
+   * position in the config object
+   *
+   * @function createControllableSumo
+   * @memberof SumoFactory
+   * @param {external:Serpentity} [engine] the serpentity engine to attach
+   * to. If not sent, it will not be attached.
+   * @param {object} [config] the config to override the entity, accepts
+   * the key `position` as an object with an x and y property.
+   * @return {external:Serpentity.Entity} the created entity
+   */
+  createControllableSumo(engine, config = {}) {
+
+    const entity = this.createSumo(null, config);
+
+    entity.addComponent(new ControlMapComponent({
+      map: [
+        {
+          source: {
+            type: 'keyboard',
+            index: 37 // left arrow
+          },
+          target: {
+            component: ForceComponent,
+            property: 'x',
+            value: (value) => -Number(value)
+          }
+        },
+        {
+          source: {
+            type: 'keyboard',
+            index: 39 // right arrow
+          },
+          target: {
+            component: ForceComponent,
+            property: 'x',
+            value: (value) => Number(value)
+          }
+        },
+        {
+          source: {
+            type: 'keyboard',
+            index: 38 // up arrow
+          },
+          target: {
+            component: ForceComponent,
+            property: 'y',
+            value: (value) => -Number(value)
+          }
+        },
+        {
+          source: {
+            type: 'keyboard',
+            index: 40 // down arrow
+          },
+          target: {
+            component: ForceComponent,
+            property: 'y',
+            value: (value) => Number(value)
+          }
+        },
+        {
+          source: {
+            type: 'keyboard',
+            index: 90 // Z
+          },
+          target: {
+            component: DashComponent,
+            property: 'dashing'
+          }
+        }
+      ]
+    }));
+
+    if (engine) {
+      engine.addEntity(entity);
+    }
+
+    return entity;
+  },
+
+  /**
+   * Creates a static harness entity
+   *
+   * @function createHarness
+   * @memberof SumoFactory
+   * @param {external:Serpentity} [engine] the serpentity engine to attach
+   * to. If not sent, it will not be attached.
+   * @param {object} [config] the config to override the entity, accepts
+   * the key `position` as an object with an x and y property.
+   * @return {external:Serpentity.Entity} the created entity
+   */
+  createHarness(engine, config = {}) {
+
+    const entity = new Entity();
+
+    // POSITION
+
+    entity.addComponent(new PositionComponent(config.position));
+    const position = entity.getComponent(PositionComponent);
+
+    // RENDERING
+
+    const radius = 15;
+
+    const container = config.container || {
+      container: PixiFactory.createHarness({ radius })
+    };
+    container.container.position.x = position.x;
+    container.container.position.y = position.y;
+    entity.addComponent(new PixiContainerComponent(container));
+
+    // PHYSICS
+
+    const friction = 0;
+    const frictionStatic = 0;
+    const restitution = 1;
+
+    const body = Bodies.circle(position.x / Config.meterSize, position.y / Config.meterSize, radius / Config.meterSize, {
+      isStatic: true,
+      label: 'Harness body',
+      friction,
+      restitution,
+      frictionStatic
+    });
+    entity.addComponent(new BodyComponent({ body }));
+
     if (engine) {
       engine.addEntity(entity);
     }
     if (engine) {
       engine.addEntity(entity);
     }
diff --git a/lib/nodes/controllable.js b/lib/nodes/controllable.js
new file mode 100644 (file)
index 0000000..bb6f3b3
--- /dev/null
@@ -0,0 +1,24 @@
+import { Node } from '@serpentity/serpentity';
+
+import ControlMapComponent from '../components/control_map';
+
+/**
+ * Node identifying an entity that has a control mapping
+ *
+ * @extends {external:Serpentity.Node}
+ * @class ControllableNode
+ */
+export default class ControllableNode extends Node {
+
+};
+
+/**
+ * Holds the types that are used to identify a controllable entity
+ *
+ * @property {object} types
+ * @name types
+ * @memberof ControllableNode
+ */
+ControllableNode.types = {
+  controlMap: ControlMapComponent
+};
diff --git a/lib/nodes/dasher.js b/lib/nodes/dasher.js
new file mode 100644 (file)
index 0000000..9662c68
--- /dev/null
@@ -0,0 +1,29 @@
+import { Node } from '@serpentity/serpentity';
+
+import BodyComponent from '../components/body';
+import DashComponent from '../components/dash';
+import ForceComponent from '../components/force';
+
+/**
+ * Node identifying an entity that has a control mapping
+ *
+ * @extends {external:Serpentity.Node}
+ * @class DasherNode
+ */
+export default class DasherNode extends Node {
+
+};
+
+/**
+ * Holds the types that are used to identify a dasher entity
+ *
+ * @property {object} types
+ * @name types
+ * @memberof DasherNode
+ */
+DasherNode.types = {
+  body: BodyComponent,
+  dash: DashComponent,
+  force: ForceComponent
+};
+
diff --git a/lib/nodes/elastic.js b/lib/nodes/elastic.js
new file mode 100644 (file)
index 0000000..090a6a7
--- /dev/null
@@ -0,0 +1,27 @@
+import { Node } from '@serpentity/serpentity';
+
+import BodyComponent from '../components/body';
+import ElasticComponent from '../components/elastic';
+
+/**
+ * Node identifying an entity that is an elastic physical body
+ *
+ * @extends {external:Serpentity.Node}
+ * @class ElasticNode
+ */
+export default class ElasticNode extends Node {
+
+};
+
+/**
+ * Holds the types that are used to identify an elastic entity
+ *
+ * @property {object} types
+ * @name types
+ * @memberof ElasticNode
+ */
+ElasticNode.types = {
+  body: BodyComponent,
+  elastic: ElasticComponent
+};
+
diff --git a/lib/nodes/limited_velocity.js b/lib/nodes/limited_velocity.js
new file mode 100644 (file)
index 0000000..554f68c
--- /dev/null
@@ -0,0 +1,26 @@
+import { Node } from '@serpentity/serpentity';
+
+import BodyComponent from '../components/body';
+import MaxVelocityComponent from '../components/max_velocity';
+
+/**
+ * Node identifying an entity that is a limited velocity physical body
+ *
+ * @extends {external:Serpentity.Node}
+ * @class LimitedVelocityNode
+ */
+export default class LimitedVelocityNode extends Node {
+
+};
+
+/**
+ * Holds the types that are used to identify a limited velocity entity
+ *
+ * @property {object} types
+ * @name types
+ * @memberof LimitedVelocityNode
+ */
+LimitedVelocityNode.types = {
+  body: BodyComponent,
+  maxVelocity: MaxVelocityComponent
+};
diff --git a/lib/nodes/physical_with_external_force.js b/lib/nodes/physical_with_external_force.js
new file mode 100644 (file)
index 0000000..32695c7
--- /dev/null
@@ -0,0 +1,27 @@
+import { Node } from '@serpentity/serpentity';
+
+import BodyComponent from '../components/body';
+import ForceComponent from '../components/force';
+
+/**
+ * Node identifying an entity that has externally applied force
+ *
+ * @extends {external:Serpentity.Node}
+ * @class PhysicalWithExternalForceNode
+ */
+export default class PhysicalWithExternalForceNode extends Node {
+
+};
+
+/**
+ * Holds the types that are used to identify an entity with a physical
+ * body
+ *
+ * @property {object} types
+ * @name types
+ * @memberof PhysicalWithExternalForceNode
+ */
+PhysicalWithExternalForceNode.types = {
+  body: BodyComponent,
+  force: ForceComponent
+};
index daa7e94cecb9fa9f3a91d4f9bf2d1475dd5d0c9d..2b6f085bce02b488a70a4236a1cb4d1825656ea9 100644 (file)
@@ -1,10 +1,22 @@
 import 'babel-polyfill';
 import 'babel-polyfill';
+
+// Systems
+import ApplyForceSystem from './systems/apply_force';
+import CreateCouplingLineSystem from './systems/create_coupling_line';
+import ControlMapperSystem from './systems/control_mapper';
+import DashSystem from './systems/dash';
+import ElasticSystem from './systems/elastic';
 import PhysicsWorldControlSystem from './systems/physics_world_control';
 import PhysicsToAttributesSystem from './systems/physics_to_attributes';
 import PhysicsWorldControlSystem from './systems/physics_world_control';
 import PhysicsToAttributesSystem from './systems/physics_to_attributes';
-import CreateCouplingLineSystem from './systems/create_coupling_line';
 import RenderSystem from './systems/render';
 import AttributesToRenderableSystem from './systems/attributes_to_renderable';
 import RenderSystem from './systems/render';
 import AttributesToRenderableSystem from './systems/attributes_to_renderable';
+
+// Factories
+
 import SumoFactory from './factories/sumo';
 import SumoFactory from './factories/sumo';
+
+// External Dependencies
+
 import Serpentity from '@serpentity/serpentity';
 import { Application } from 'pixi.js';
 import { Engine } from 'matter-js';
 import Serpentity from '@serpentity/serpentity';
 import { Application } from 'pixi.js';
 import { Engine } from 'matter-js';
@@ -116,7 +128,7 @@ internals.Sumo = class Sumo {
 
       // We're sending the currentTime since it gives better results for
       // this type of renderer, though usually we expect the delta
 
       // We're sending the currentTime since it gives better results for
       // this type of renderer, though usually we expect the delta
-      this._engine.update(currentTime);
+      this._engine.update(currentFrameDuration);
       this._previousTime = currentTime;
     }
   }
       this._previousTime = currentTime;
     }
   }
@@ -176,10 +188,18 @@ internals.Sumo = class Sumo {
 
   _initializeSystems() {
 
 
   _initializeSystems() {
 
+    this._engine.addSystem(new ControlMapperSystem());
+
+    this._engine.addSystem(new DashSystem());
+
+    this._engine.addSystem(new ApplyForceSystem());
+
     this._engine.addSystem(new PhysicsWorldControlSystem({
       engine: this._matterJs
     }));
 
     this._engine.addSystem(new PhysicsWorldControlSystem({
       engine: this._matterJs
     }));
 
+    this._engine.addSystem(new ElasticSystem());
+
     this._engine.addSystem(new PhysicsToAttributesSystem());
 
     this._engine.addSystem(new AttributesToRenderableSystem());
     this._engine.addSystem(new PhysicsToAttributesSystem());
 
     this._engine.addSystem(new AttributesToRenderableSystem());
@@ -195,42 +215,42 @@ internals.Sumo = class Sumo {
 
   _initializeEntities() {
 
 
   _initializeEntities() {
 
-    const entityA = SumoFactory.createSumo(null, {
+    const sumoA = SumoFactory.createSumo(null, {
       position: {
         x: 50,
         y: 50
       }
     });
 
       position: {
         x: 50,
         y: 50
       }
     });
 
-    const entityB = SumoFactory.createSumo(null, {
+    const sumoB = SumoFactory.createControllableSumo(null, {
       position: {
       position: {
-        x: 309,
-        y: 112
+        x: 500,
+        y: 78
       }
     });
 
       }
     });
 
-    const entityC = SumoFactory.createSumo(null, {
+    const harness = SumoFactory.createHarness(null, {
       position: {
       position: {
-        x: 500,
-        y: 78
+        x: 309,
+        y: 112
       }
     });
 
     SumoFactory.createRubberBand(this._engine, {
       }
     });
 
     SumoFactory.createRubberBand(this._engine, {
-      entityA,
-      entityB
+      entityA: sumoA,
+      entityB: harness
     });
 
     SumoFactory.createRubberBand(this._engine, {
     });
 
     SumoFactory.createRubberBand(this._engine, {
-      entityA: entityC,
-      entityB
+      entityA: sumoB,
+      entityB: harness
     });
 
     // To keep the coupling behind, we'll manually add the sumos later
 
     });
 
     // To keep the coupling behind, we'll manually add the sumos later
 
-    this._engine.addEntity(entityA);
-    this._engine.addEntity(entityB);
-    this._engine.addEntity(entityC);
+    this._engine.addEntity(sumoA);
+    this._engine.addEntity(sumoB);
+    this._engine.addEntity(harness);
   }
 };
 
   }
 };
 
diff --git a/lib/systems/apply_force.js b/lib/systems/apply_force.js
new file mode 100644 (file)
index 0000000..4adc55d
--- /dev/null
@@ -0,0 +1,97 @@
+import { System } from '@serpentity/serpentity';
+import { Body, Vector } from 'matter-js';
+
+const internals = {
+  kForce: 0.0001
+};
+
+import PhysicalWithExternalForceNode from '../nodes/physical_with_external_force';
+
+/**
+ * Applies physica from external forces (eg. controls) to the physics body
+ *
+ * @extends {external:Serpentity.System}
+ * @class ApplyForceSystem
+ * @param {object} config a configuration object to extend.
+ */
+export default class ApplyForceSystem extends System {
+
+  constructor(config = {}) {
+
+    super();
+
+    /**
+     * The node collection of entities that have external force
+     *
+     * @property {external:Serpentity.NodeCollection} physicalEntities
+     * @instance
+     * @memberof ApplyForceSystem
+     */
+    this.physicalEntities = null;
+  }
+
+  /**
+   * Initializes system when added. Requests physics nodes
+   *
+   * @function added
+   * @memberof ApplyForceSystem
+   * @instance
+   * @param {external:Serpentity.Engine} engine the serpentity engine to
+   * which we are getting added
+   */
+  added(engine) {
+
+    this.physicalEntities = engine.getNodes(PhysicalWithExternalForceNode);
+  }
+
+  /**
+   * Clears system resources when removed.
+   *
+   * @function removed
+   * @instance
+   * @memberof ApplyForceSystem
+   */
+  removed() {
+
+    this.physicalEntities = null;
+  }
+
+  /**
+   * Runs on every update of the loop. Updates the body based on the force
+   * component
+   *
+   * @function update
+   * @instance
+   * @param {Number} currentFrameDuration the duration of the current
+   * frame
+   * @memberof ApplyForceSystem
+   */
+  update(currentFrameDuration) {
+
+    for (const physicalEntity of this.physicalEntities) {
+      const body = physicalEntity.body.body;
+      const force = physicalEntity.force;
+      const forceVector = Vector.create(force.x * internals.kForce, force.y * internals.kForce);
+
+
+      // Store the last angle and apply force on non-zero forces
+
+      if (force.x || force.y) {
+        force.lastAngle = Math.atan2(force.y, force.x);
+        Body.applyForce(body,
+          {
+            x: body.position.x,
+            y: body.position.y
+          },
+          forceVector
+        );
+      }
+
+      // Reset the force
+
+      force.x = 0;
+      force.y = 0;
+    }
+  }
+};
+
diff --git a/lib/systems/control_mapper.js b/lib/systems/control_mapper.js
new file mode 100644 (file)
index 0000000..e91ecc6
--- /dev/null
@@ -0,0 +1,118 @@
+import { System } from '@serpentity/serpentity';
+
+import ControllableNode from '../nodes/controllable';
+
+/* global window */
+
+const internals = {
+  keyboardState: {
+  }
+};
+
+/**
+ * Updates control status based on the controller map
+ *
+ * @extends {external:Serpentity.System}
+ * @class ControlMapperSystem
+ * @param {object} config a configuration object to extend.
+ */
+export default class ControlMapperSystem extends System {
+
+  constructor(config = {}) {
+
+    super();
+
+    /**
+     * The node collection of controllable entities
+     *
+     * @property {external:Serpentity.NodeCollection} controllables
+     * @instance
+     * @memberof RenderSystem
+     */
+    this.controllables = null;
+
+    this._initializeKeyboard();
+  }
+
+  /**
+   * Initializes system when added. Requests controllable nodes.
+   *
+   * @function added
+   * @memberof RenderSystem
+   * @instance
+   * @param {external:Serpentity.Engine} engine the serpentity engine to
+   * which we are getting added
+   */
+  added(engine) {
+
+    this.controllables = engine.getNodes(ControllableNode);
+  }
+
+  /**
+   * Clears system resources when removed.
+   *
+   * @function removed
+   * @instance
+   * @memberof RenderSystem
+   */
+  removed() {
+
+    this.controllables = null;
+  }
+
+  /**
+   * Runs on every update of the loop. Maps the actions given the current state of the inputs
+   *
+   * @function update
+   * @instance
+   * @param {Number} currentFrameDuration the duration of the current
+   * frame
+   * @memberof RenderSystem
+   */
+  update(currentFrameDuration) {
+
+    for (const controllable of this.controllables) {
+      for (const map of controllable.controlMap.map) {
+        if (map.source.type === 'keyboard') {
+          this._setValue(controllable.entity, map.target, !!internals.keyboardState[map.source.index]);
+        }
+      }
+    }
+  }
+
+  // Listens to keyboard to update internal map
+
+  _initializeKeyboard() {
+
+    window.addEventListener('keydown', (event) => {
+
+      internals.keyboardState[event.keyCode] = true;
+    });
+
+    window.addEventListener('keyup', (event) => {
+
+      internals.keyboardState[event.keyCode] = false;
+    });
+  }
+
+  // Sets the value to a target
+
+  _setValue(entity, target, value) {
+
+    const component = entity.getComponent(target.component);
+
+    if (component) {
+      const keyFragments = target.property.split('.');
+      let currentObject = component;
+      for (const keyFragment of keyFragments.slice(0, keyFragments.length - 1)) {
+        currentObject = currentObject[keyFragment] = currentObject[keyFragment] || {};
+      }
+
+
+      const finalValue = !!target.value ? target.value(value) : value;
+      const finalProperty = keyFragments.pop();
+      currentObject[finalProperty] += finalValue;
+    }
+  }
+};
+
diff --git a/lib/systems/dash.js b/lib/systems/dash.js
new file mode 100644 (file)
index 0000000..d05e9ea
--- /dev/null
@@ -0,0 +1,116 @@
+import { System } from '@serpentity/serpentity';
+
+const internals = {
+  kForce: 10
+};
+
+import DasherNode from '../nodes/dasher';
+
+/**
+ * Applies a dash as a force on an entity. Locks it until the button is released
+ * and a cooldown period has passed
+ *
+ * @extends {external:Serpentity.System}
+ * @class DashSystem
+ * @param {object} config a configuration object to extend.
+ */
+export default class DashSystem extends System {
+
+  constructor(config = {}) {
+
+    super();
+
+    /**
+     * The node collection of dashers
+     *
+     * @property {external:Serpentity.NodeCollection} dashers
+     * @instance
+     * @memberof DashSystem
+     */
+    this.dashers = null;
+  }
+
+  /**
+   * Initializes system when added. Requests dasher nodes
+   *
+   * @function added
+   * @memberof DashSystem
+   * @instance
+   * @param {external:Serpentity.Engine} engine the serpentity engine to
+   * which we are getting added
+   */
+  added(engine) {
+
+    this.dashers = engine.getNodes(DasherNode);
+  }
+
+  /**
+   * Clears system resources when removed.
+   *
+   * @function removed
+   * @instance
+   * @memberof DashSystem
+   */
+  removed() {
+
+    this.dashers = null;
+  }
+
+  /**
+   * Runs on every update of the loop. Triggers dash and manages cooldown
+   *
+   * @function update
+   * @instance
+   * @param {Number} currentFrameDuration the duration of the current
+   * frame
+   * @memberof DashSystem
+   */
+  update(currentFrameDuration) {
+
+    for (const dasher of this.dashers) {
+
+      const dash = dasher.dash;
+
+      if (dash.dashing && !dash.locked) {
+        this._dash(dasher);
+      }
+
+      if (!dash.dashing && dash.locked && dash.currentCooldown >= dash.cooldown) {
+        this._unlock(dasher);
+      }
+
+      if (dash.locked) {
+        dash.currentCooldown += currentFrameDuration;
+      }
+
+      dash.dashing = 0;
+    }
+  }
+
+  // Executes the dash action
+
+  _dash(dasher) {
+
+    const dash = dasher.dash;
+    const force = dasher.force;
+
+    const angle = force.lastAngle || 0;
+    dash.locked = true;
+    dash.currentCooldown = 0;
+
+    const xComponent = internals.kForce * Math.cos(angle);
+    const yComponent = internals.kForce * Math.sin(angle);
+
+    force.x += xComponent;
+    force.y += yComponent;
+  }
+
+  // Executes the unlock action
+
+  _unlock(dasher) {
+
+    dasher.dash.locked = false;
+  }
+};
+
+
diff --git a/lib/systems/elastic.js b/lib/systems/elastic.js
new file mode 100644 (file)
index 0000000..b94d371
--- /dev/null
@@ -0,0 +1,93 @@
+import { System } from '@serpentity/serpentity';
+
+const internals = {
+  kTightStiffness: 0.001,
+  kBaseStiffness: 0.0008,
+  kLooseStiffness: 0.0000001
+};
+
+import ElasticNode from '../nodes/elastic';
+
+/**
+ Changes the stiffness on the node when it's less extended
+ *
+ * @extends {external:Serpentity.System}
+ * @class ElasticSystem
+ * @param {object} config a configuration object to extend.
+ */
+export default class ElasticSystem extends System {
+
+  constructor(config = {}) {
+
+    super();
+
+    /**
+     * The node collection of entities that have external force
+     *
+     * @property {external:Serpentity.NodeCollection} elastics
+     * @instance
+     * @memberof ElasticSystem
+     */
+    this.elastics = null;
+  }
+
+  /**
+   * Initializes system when added. Requests elastic nodes
+   *
+   * @function added
+   * @memberof ElasticSystem
+   * @instance
+   * @param {external:Serpentity.Engine} engine the serpentity engine to
+   * which we are getting added
+   */
+  added(engine) {
+
+    this.elastics = engine.getNodes(ElasticNode);
+  }
+
+  /**
+   * Clears system resources when removed.
+   *
+   * @function removed
+   * @instance
+   * @memberof ElasticSystem
+   */
+  removed() {
+
+    this.elastics = null;
+  }
+
+  /**
+   * Runs on every update of the loop. Checks length of elastic and adjusts
+   * stiffness
+   *
+   * @function update
+   * @instance
+   * @param {Number} currentFrameDuration the duration of the current
+   * frame
+   * @memberof ElasticSystem
+   */
+  update(currentFrameDuration) {
+
+    for (const elastic of this.elastics) {
+      const constraint = elastic.body.body;
+
+      const currentDistance = Math.abs(
+        Math.sqrt(
+          Math.pow(constraint.bodyA.position.x - constraint.bodyB.position.x, 2) +
+          Math.pow(constraint.bodyA.position.y - constraint.bodyB.position.y, 2)));
+
+      if (currentDistance <= constraint.length) {
+        constraint.stiffness = internals.kLooseStiffness;
+        continue;
+      }
+
+      if (currentDistance >= 2.6 * constraint.length) {
+        constraint.stiffness = internals.kTightStiffness;
+        continue;
+      }
+
+      constraint.stiffness = internals.kBaseStiffness;
+    }
+  }
+};
index a49a16bd7db8d5f70b68d5df1e23f27b5775be13..e054710df2ff6ff5d41419f17851c912b0660d99 100644 (file)
@@ -1,6 +1,7 @@
 import { System } from '@serpentity/serpentity';
 
 import PhysicalWithAttributesNode from '../nodes/physical_with_attributes';
 import { System } from '@serpentity/serpentity';
 
 import PhysicalWithAttributesNode from '../nodes/physical_with_attributes';
+import Config from '../config';
 
 /**
  * Distribuets physics data to the related components
 
 /**
  * Distribuets physics data to the related components
@@ -64,8 +65,8 @@ export default class PhysicsToAttributesSystem extends System {
   update(currentFrameDuration) {
 
     for (const physicalEntity of this.physicalEntities) {
   update(currentFrameDuration) {
 
     for (const physicalEntity of this.physicalEntities) {
-      physicalEntity.position.x = physicalEntity.body.body.position.x;
-      physicalEntity.position.y = physicalEntity.body.body.position.y;
+      physicalEntity.position.x = physicalEntity.body.body.position.x * Config.meterSize;
+      physicalEntity.position.y = physicalEntity.body.body.position.y * Config.meterSize;
       physicalEntity.angle.angle = physicalEntity.body.body.angle;
     }
   }
       physicalEntity.angle.angle = physicalEntity.body.body.angle;
     }
   }
index daa39c130f07366f118a92eae89770e1de64f3b3..a658d5cc8cca89e013b4347d485f5507e1bb8e40 100644 (file)
@@ -91,7 +91,7 @@ export default class PhysicsWorldControlSystem extends System {
    */
   update(currentFrameDuration) {
 
    */
   update(currentFrameDuration) {
 
-    Engine.run(this.engine);
+    Engine.update(this.engine, currentFrameDuration);
   }
 };
 
   }
 };
 
diff --git a/lib/systems/reduce_velocity.js b/lib/systems/reduce_velocity.js
new file mode 100644 (file)
index 0000000..6521014
--- /dev/null
@@ -0,0 +1,70 @@
+import { System } from '@serpentity/serpentity';
+
+import LimitedVelocityNode from '../nodes/limited_velocity';
+
+/**
+ * Reduces velocity if it exceeds threshold
+ *
+ * @extends {external:Serpentity.System}
+ * @class ReduceVelocitySystem
+ * @param {object} config a configuration object to extend.
+ */
+export default class ReduceVelocitySystem extends System {
+
+  constructor(config = {}) {
+
+    super();
+
+    /**
+     * The node collection of entities that have external force
+     *
+     * @property {external:Serpentity.NodeCollection} limitedVelocityEntities
+     * @instance
+     * @memberof ReduceVelocitySystem
+     */
+    this.limitedVelocityEntities = null;
+  }
+
+  /**
+   * Initializes system when added. Requests limited velocity nodes
+   *
+   * @function added
+   * @memberof ReduceVelocitySystem
+   * @instance
+   * @param {external:Serpentity.Engine} engine the serpentity engine to
+   * which we are getting added
+   */
+  added(engine) {
+
+    this.limitedVelocityEntities = engine.getNodes(LimitedVelocityNode);
+  }
+
+  /**
+   * Clears system resources when removed.
+   *
+   * @function removed
+   * @instance
+   * @memberof ReduceVelocitySystem
+   */
+  removed() {
+
+    this.limitedVelocityEntities = null;
+  }
+
+  /**
+   * Runs on every update of the loop. Checks current velocity and adjusts if necessary
+   *
+   * @function update
+   * @instance
+   * @param {Number} currentFrameDuration the duration of the current
+   * frame
+   * @memberof ReduceVelocitySystem
+   */
+  update(currentFrameDuration) {
+
+    for (const limitedVelocityEntity of this.limitedVelocityEntities) {
+      console.log(limitedVelocityEntity.body.body.velocity, limitedVelocityEntity.maxVelocity.maxVelocity);
+    }
+  }
+};
+
index a2e30203639355569251ded345adda2b9a83c1e0..06d0e603642432fceee069d8c6f2e33f877474e3 100644 (file)
@@ -31,13 +31,13 @@ export default class RenderSystem extends System {
     /**
      * The pixi engine we will use to render
      *
     /**
      * The pixi engine we will use to render
      *
-     * @property {external:PixiJs.Application} renderables
+     * @property {external:PixiJs.Application} application
      * @instance
      * @memberof RenderSystem
      */
      * @instance
      * @memberof RenderSystem
      */
-    this._application = config.application;
+    this.application = config.application;
 
 
-    if (!this._application) {
+    if (!this.application) {
       throw new Error(internals.kNoPixiError);
     }
   }
       throw new Error(internals.kNoPixiError);
     }
   }
@@ -57,11 +57,11 @@ export default class RenderSystem extends System {
     this.renderables = engine.getNodes(RenderableNode);
     this.renderables.on('nodeAdded', (event) => {
 
     this.renderables = engine.getNodes(RenderableNode);
     this.renderables.on('nodeAdded', (event) => {
 
-      this._application.stage.addChild(event.node.container.container);
+      this.application.stage.addChild(event.node.container.container);
     });
     this.renderables.on('nodeRemoved', (event) => {
 
     });
     this.renderables.on('nodeRemoved', (event) => {
 
-      this._application.stage.removeChild(event.node.container.container);
+      this.application.stage.removeChild(event.node.container.container);
     });
   }
 
     });
   }