Skip to content

Commit

Permalink
feat: add gamepad.vibrate() method
Browse files Browse the repository at this point in the history
  • Loading branch information
shroudedcode committed Apr 18, 2020
1 parent f31fe42 commit 00be2ee
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 0 deletions.
18 changes: 18 additions & 0 deletions docs/components/gamepad.md
Expand Up @@ -30,8 +30,26 @@ When `.query()`-ed returns a `Vector2` of the current position of the stick.

---

#### `vibrate(duration, options)`

Makes the gamepad vibrate with the specified magnitude for the specified duration.

* `duration` is the duration in milliseconds
* `options` is an object with the following properties:
* `weakMagnitude` is the magnitude with which the weak motor will vibrate (defaults to `0`)
* `strongMagnitude` is the magnitude with which the strong motor will vibrate (defaults to `0`)

Note that this feature:

* **only works** with so-called "Dual Rumble" gamepads that have two vibrations motors
* **will not work** in all browsers since it's based on a [proposal][vibration-proposal] that, as of April 2020, isn't part of the official specification yet

---

#### `isConnected()`

Returns whether a gamepad is currently connected.

[gamepad-api]: https://developer.mozilla.org/en-US/docs/Web/API/Gamepad_API
[vibration-proposal]: https://github.com/w3c/gamepad/pull/68

18 changes: 18 additions & 0 deletions src/apis.ts
Expand Up @@ -28,4 +28,22 @@ export interface IGamepad {
connected: boolean
timestamp: number
mapping: string
vibrationActuator?: IGamepadHapticActuator
}

// Based on https://github.com/w3c/gamepad/pull/68
// (just a proposal to the specification, but implemented in Chrome)
export interface IGamepadHapticActuator {
type: string
playEffect(
type: 'dual-rumble',
params: IGamepadEffectParameters,
): Promise<any>
}

export interface IGamepadEffectParameters {
duration?: number
startDelay?: number
strongMagnitude?: number
weakMagnitude?: number
}
42 changes: 42 additions & 0 deletions src/inputs/gamepad.spec.ts
Expand Up @@ -118,6 +118,16 @@ describe('The `Gamepad` class', () => {

})



describe('should have a `vibrate()` method that', () => {

it("doesn't throw an error when called", async () => {
await gamepad.vibrate(1000)
})

})

})

describe('in its connected state', () => {
Expand Down Expand Up @@ -217,6 +227,38 @@ describe('The `Gamepad` class', () => {

})

describe('should have a `vibrate()` method that', () => {

it('correctly passes along the provided options', () => {
let playEffectArgs: any[] = []
nav.gamepads[0].vibrationActuator = {
type: 'dual-rumble',
playEffect: async (...args) => { playEffectArgs = args },
}

gamepad.vibrate(1000, { weakMagnitude: 0.5, strongMagnitude: 1 })

expect(playEffectArgs).to.deep.equal([
'dual-rumble',
{ duration: 1000, weakMagnitude: 0.5, strongMagnitude: 1 },
])
})



it('ignores actuators that are not of type `dual-rumble`', () => {
let playEffectCalled = false
nav.gamepads[0].vibrationActuator = {
type: 'not-dual-rumble',
playEffect: async () => { playEffectCalled = true },
}

gamepad.vibrate(1000)
expect(playEffectCalled).to.equal(false)
})

})

})

})
19 changes: 19 additions & 0 deletions src/inputs/gamepad.ts
Expand Up @@ -120,4 +120,23 @@ export class Gamepad {
}
}

public async vibrate(
duration: number,
{ weakMagnitude, strongMagnitude }: VibrationOptions = {},
): Promise<void> {
if (!this.isConnected()) return

const actuator = this.gamepad.vibrationActuator
if (!actuator || actuator.type !== 'dual-rumble') return

await actuator.playEffect('dual-rumble', {
duration, strongMagnitude, weakMagnitude,
})
}

}

interface VibrationOptions {
strongMagnitude?: number
weakMagnitude?: number
}

0 comments on commit 00be2ee

Please sign in to comment.