]> git.r.bdr.sh - rbdr/sumo/commitdiff
Add gamepad support (#10)
authorRubén Beltrán del Río <redacted>
Tue, 29 May 2018 05:29:27 +0000 (00:29 -0500)
committerGitHub <redacted>
Tue, 29 May 2018 05:29:27 +0000 (00:29 -0500)
* Update docs

* Add gamepad support

* Remove console logs

* Add factories for controllables

* Add both controllables

* Add gamepad support to changelog

CHANGELOG.md
lib/components/control_map.js
lib/factories/pixi.js
lib/factories/sumo.js
lib/sumo.js
lib/systems/control_mapper.js
lib/systems/grab.js

index c038284acf76e8c6a9fae50a30e8eee0eefe493d..c64a8fef2268d338b892073c6a87534dd3f4d8a0 100644 (file)
@@ -19,5 +19,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
 - Scripts to setup hooks, linting, docs, and bundling
 - Keyboard Support
 - Dash & grab mechanics
+- Add support for Gamepads (Tested using PS4 only)
 
 [Unreleased]: https://github.com/rbdr/sumo/compare/master...develop
index cb447d0da258b763d02094e43ddd250a1cfeeac4..4edf31806b27f171a006cf47e105b72f2073c354 100644 (file)
@@ -16,8 +16,8 @@ import { Component } from '@serpentity/serpentity';
  * @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} type type of input, can be keyboard or gamepad
+ * @property {number} gamepadNumber the number of gamepad to use
  * @property {string} gamepadInputSource either axes or buttons
  * @property {number} index the input index or keycode
  */
index f941ffeba0dbec556874319db5c48b8f67e6a82f..077c5b4c41187f36af8d0794d1de29df44f00d1b 100644 (file)
@@ -19,10 +19,11 @@ export default {
   createSumo(config) {
 
     const radius = config.radius;
+    const sumoColor = config.color || 0x87c5ea;
 
     // The body
     const body = new Graphics();
-    body.beginFill(0x87c5ea)
+    body.beginFill(sumoColor)
       .drawCircle(0, 0, radius)
       .endFill();
 
index 7b50f8ae50493ce087d8d8f570e23a12c08f7a9a..18e9d72dfafacfd37455658b5cddc40f0db2bf14 100644 (file)
@@ -71,9 +71,12 @@ export default {
     // RENDERING
 
     const radius = 25;
+    const pixiConfig = Object.assign({
+      radius
+    }, config.pixi);
 
     const container = config.container || {
-      container: PixiFactory.createSumo({ radius })
+      container: PixiFactory.createSumo(pixiConfig)
     };
     container.container.position.x = position.x;
     container.container.position.y = position.y;
@@ -195,74 +198,304 @@ export default {
 
     const entity = this.createSumo(null, config);
 
-    entity.addComponent(new ControlMapComponent({
-      map: [
-        {
-          source: {
-            type: 'keyboard',
-            index: 37 // left arrow
+    entity.addComponent(new ControlMapComponent(config.controlMap));
+
+    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. Has contrrol scheme defaults for
+   * player 1
+   *
+   * @function createPlayer1Sumo
+   * @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
+   */
+  createPlayer1Sumo(engine, config = {}) {
+
+    const playerConfig = Object.assign({
+      controlMap: {
+        map: [
+          {
+            source: {
+              type: 'keyboard',
+              index: 65 // a
+            },
+            target: {
+              component: ForceComponent,
+              property: 'x',
+              value: (value) => -Number(value)
+            }
           },
-          target: {
-            component: ForceComponent,
-            property: 'x',
-            value: (value) => -Number(value)
-          }
-        },
-        {
-          source: {
-            type: 'keyboard',
-            index: 39 // right arrow
+          {
+            source: {
+              type: 'keyboard',
+              index: 68 // d
+            },
+            target: {
+              component: ForceComponent,
+              property: 'x',
+              value: (value) => Number(value)
+            }
           },
-          target: {
-            component: ForceComponent,
-            property: 'x',
-            value: (value) => Number(value)
-          }
-        },
-        {
-          source: {
-            type: 'keyboard',
-            index: 38 // up arrow
+          {
+            source: {
+              type: 'keyboard',
+              index: 87 // w
+            },
+            target: {
+              component: ForceComponent,
+              property: 'y',
+              value: (value) => -Number(value)
+            }
           },
-          target: {
-            component: ForceComponent,
-            property: 'y',
-            value: (value) => -Number(value)
-          }
-        },
-        {
-          source: {
-            type: 'keyboard',
-            index: 40 // down arrow
+          {
+            source: {
+              type: 'keyboard',
+              index: 83 // s
+            },
+            target: {
+              component: ForceComponent,
+              property: 'y',
+              value: (value) => Number(value)
+            }
           },
-          target: {
-            component: ForceComponent,
-            property: 'y',
-            value: (value) => Number(value)
-          }
-        },
-        {
-          source: {
-            type: 'keyboard',
-            index: 90 // Z
+          {
+            source: {
+              type: 'keyboard',
+              index: 90 // Z
+            },
+            target: {
+              component: DashComponent,
+              property: 'dashing'
+            }
+          },
+          {
+            source: {
+              type: 'keyboard',
+              index: 88 // X
+            },
+            target: {
+              component: GrabComponent,
+              property: 'grabbing'
+            }
+          },
+          {
+            source: {
+              type: 'gamepad',
+              gamepadNumber: 0,
+              gamepadInputSource: 'axes',
+              index: 0 // left stick horizontal
+            },
+            target: {
+              component: ForceComponent,
+              property: 'x',
+              value: (value) => Number(value)
+            }
+          },
+          {
+            source: {
+              type: 'gamepad',
+              gamepadNumber: 0,
+              gamepadInputSource: 'axes',
+              index: 1 // left stick vertical
+            },
+            target: {
+              component: ForceComponent,
+              property: 'y',
+              value: (value) => Number(value)
+            }
           },
-          target: {
-            component: DashComponent,
-            property: 'dashing'
+          {
+            source: {
+              type: 'gamepad',
+              gamepadNumber: 0,
+              gamepadInputSource: 'buttons',
+              index: 2 // left face button
+            },
+            target: {
+              component: DashComponent,
+              property: 'dashing',
+              value: (value) => value.value
+            }
+          },
+          {
+            source: {
+              type: 'gamepad',
+              gamepadNumber: 0,
+              gamepadInputSource: 'buttons',
+              index: 0 // bottom face button
+            },
+            target: {
+              component: GrabComponent,
+              property: 'grabbing',
+              value: (value) => value.value
+            }
           }
-        },
-        {
-          source: {
-            type: 'keyboard',
-            index: 88 // X
+        ]
+      }
+    }, config);
+
+    const entity = this.createControllableSumo(null, playerConfig);
+
+    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. Has contrrol scheme defaults for
+   * player 2
+   *
+   * @function createPlayer2Sumo
+   * @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
+   */
+  createPlayer2Sumo(engine, config = {}) {
+
+    const playerConfig = Object.assign({
+      pixi: {
+        color: 0xeaacac
+      },
+      controlMap: {
+        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: 188 // ,
+            },
+            target: {
+              component: DashComponent,
+              property: 'dashing'
+            }
           },
-          target: {
-            component: GrabComponent,
-            property: 'grabbing'
+          {
+            source: {
+              type: 'keyboard',
+              index: 190 // .
+            },
+            target: {
+              component: GrabComponent,
+              property: 'grabbing'
+            }
+          },
+          {
+            source: {
+              type: 'gamepad',
+              gamepadNumber: 1,
+              gamepadInputSource: 'axes',
+              index: 0 // left stick horizontal
+            },
+            target: {
+              component: ForceComponent,
+              property: 'x',
+              value: (value) => Number(value)
+            }
+          },
+          {
+            source: {
+              type: 'gamepad',
+              gamepadNumber: 1,
+              gamepadInputSource: 'axes',
+              index: 1 // left stick vertical
+            },
+            target: {
+              component: ForceComponent,
+              property: 'y',
+              value: (value) => Number(value)
+            }
+          },
+          {
+            source: {
+              type: 'gamepad',
+              gamepadNumber: 1,
+              gamepadInputSource: 'buttons',
+              index: 2 // left face button
+            },
+            target: {
+              component: DashComponent,
+              property: 'dashing',
+              value: (value) => value.value
+            }
+          },
+          {
+            source: {
+              type: 'gamepad',
+              gamepadNumber: 1,
+              gamepadInputSource: 'buttons',
+              index: 0 // bottom face button
+            },
+            target: {
+              component: GrabComponent,
+              property: 'grabbing',
+              value: (value) => value.value
+            }
           }
-        }
-      ]
-    }));
+        ]
+      }
+    }, config);
+
+    const entity = this.createControllableSumo(null, playerConfig);
 
     if (engine) {
       engine.addEntity(entity);
index d39c055eaece84e07eed6ee579437ca0375bdcbf..cdf8cc65bdb73e98e8d70ad659dd9756178bc7b0 100644 (file)
@@ -237,14 +237,14 @@ internals.Sumo = class Sumo {
       }
     });
 
-    const sumoA = SumoFactory.createSumo(null, {
+    const sumoA = SumoFactory.createPlayer1Sumo(null, {
       position: {
         x: this.horizontalResolution / 2 - 100,
         y: this.verticalResolution / 2
       }
     });
 
-    const sumoB = SumoFactory.createControllableSumo(null, {
+    const sumoB = SumoFactory.createPlayer2Sumo(null, {
       position: {
         x: this.horizontalResolution / 2 + 100,
         y: this.verticalResolution / 2
index e91ecc6c05bc4dba532fb675c4b12623587b7380..c39aea3c8fe44ba48fcc90fd0f408b0a256eb53c 100644 (file)
@@ -2,9 +2,11 @@ import { System } from '@serpentity/serpentity';
 
 import ControllableNode from '../nodes/controllable';
 
-/* global window */
+/* global navigator window */
 
 const internals = {
+  gamepadState: {
+  },
   keyboardState: {
   }
 };
@@ -32,6 +34,7 @@ export default class ControlMapperSystem extends System {
     this.controllables = null;
 
     this._initializeKeyboard();
+    this._initializeGamepad();
   }
 
   /**
@@ -71,11 +74,21 @@ export default class ControlMapperSystem extends System {
    */
   update(currentFrameDuration) {
 
+    this._updateGamepads();
+
     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]);
         }
+
+        if (map.source.type === 'gamepad') {
+          const gamepad = internals.gamepadState[map.source.gamepadNumber];
+          if (gamepad) {
+            const source = gamepad[map.source.gamepadInputSource];
+            source && this._setValue(controllable.entity, map.target, source[map.source.index]);
+          }
+        }
       }
     }
   }
@@ -95,6 +108,32 @@ export default class ControlMapperSystem extends System {
     });
   }
 
+  // Requests gamepad access and binds to the events
+
+  _initializeGamepad() {
+
+    window.addEventListener('gamepadconnected', (event) => {
+
+      internals.gamepadState[event.gamepad.index] = event.gamepad;
+      window.gamepad = event.gamepad;
+    });
+
+    window.addEventListener('gamepaddisconnected', (event) => {
+
+      delete internals.gamepadState[event.gamepad.index];
+    });
+  }
+
+  // Update Gamepad
+
+  _updateGamepads() {
+
+    const gamepads = navigator.getGamepads();
+    for (const index of Object.keys(internals.gamepadState)) {
+      internals.gamepadState[index] = gamepads[index];
+    }
+  }
+
   // Sets the value to a target
 
   _setValue(entity, target, value) {
index 43e62462b6b861e3f22ed0c38df2f8edb2d41212..c50d1d9ff1c68525da9309985b9e86a9f3e3189a 100644 (file)
@@ -131,8 +131,6 @@ export default class GrabSystem extends System {
 
     Body.setPosition(grabber.grabArea.area, grabber.body.body.position);
 
-    console.log('Grab!');
-
     for (const grabbable of this.grabbables) {
 
       if (grabbable.entity === grabber.entity) {
@@ -142,7 +140,6 @@ export default class GrabSystem extends System {
       const collision = SAT.collides(grabber.grabArea.area, grabbable.body.body);
       if (collision.collided) {
         grab.constraint = this._createConstraint(grabber.body.body, grabbable.body.body);
-        console.log('Grabbing', grab.constraint);
       }
     }
   }
@@ -158,7 +155,6 @@ export default class GrabSystem extends System {
 
   _release(grabber) {
 
-    console.log('Releasing', grabber.grab.constraint);
     World.remove(this.engine.world, grabber.grab.constraint);
     grabber.grab.currentCooldown = 0;
     grabber.grab.constraint = null;