playcanvas
Version:
PlayCanvas WebGL game engine
951 lines (948 loc) • 34.7 kB
JavaScript
import { EventHandler } from '../../core/event-handler.js';
import { EVENT_GAMEPADCONNECTED, EVENT_GAMEPADDISCONNECTED, XRPAD_STICK_Y, XRPAD_STICK_X, XRPAD_TOUCHPAD_Y, XRPAD_TOUCHPAD_X, PAD_R_STICK_Y, PAD_R_STICK_X, PAD_L_STICK_Y, PAD_L_STICK_X, XRPAD_B, XRPAD_A, XRPAD_STICK_BUTTON, XRPAD_TOUCHPAD_BUTTON, XRPAD_SQUEEZE, XRPAD_TRIGGER, PAD_VENDOR, PAD_RIGHT, PAD_LEFT, PAD_DOWN, PAD_UP, PAD_R_STICK_BUTTON, PAD_L_STICK_BUTTON, PAD_START, PAD_SELECT, PAD_R_SHOULDER_2, PAD_L_SHOULDER_2, PAD_R_SHOULDER_1, PAD_L_SHOULDER_1, PAD_FACE_4, PAD_FACE_3, PAD_FACE_2, PAD_FACE_1 } from './constants.js';
import { math } from '../../core/math/math.js';
import { platform } from '../../core/platform.js';
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
try {
var info = gen[key](arg);
var value = info.value;
} catch (error) {
reject(error);
return;
}
if (info.done) {
resolve(value);
} else {
Promise.resolve(value).then(_next, _throw);
}
}
function _async_to_generator(fn) {
return function() {
var self = this, args = arguments;
return new Promise(function(resolve, reject) {
var gen = fn.apply(self, args);
function _next(value) {
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
}
function _throw(err) {
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
}
_next(undefined);
});
};
}
var dummyArray = Object.freeze([]);
/**
* Get Gamepads from API.
*
* @type {Function}
* @returns {Gamepad[]} Retrieved gamepads from the device.
*/ var getGamepads = function getGamepads() {
return dummyArray;
};
if (typeof navigator !== 'undefined') {
getGamepads = (navigator.getGamepads || navigator.webkitGetGamepads || getGamepads).bind(navigator);
}
var MAPS_INDEXES = {
buttons: {
PAD_FACE_1,
PAD_FACE_2,
PAD_FACE_3,
PAD_FACE_4,
PAD_L_SHOULDER_1,
PAD_R_SHOULDER_1,
PAD_L_SHOULDER_2,
PAD_R_SHOULDER_2,
PAD_SELECT,
PAD_START,
PAD_L_STICK_BUTTON,
PAD_R_STICK_BUTTON,
PAD_UP,
PAD_DOWN,
PAD_LEFT,
PAD_RIGHT,
PAD_VENDOR,
XRPAD_TRIGGER,
XRPAD_SQUEEZE,
XRPAD_TOUCHPAD_BUTTON,
XRPAD_STICK_BUTTON,
XRPAD_A,
XRPAD_B
},
axes: {
PAD_L_STICK_X,
PAD_L_STICK_Y,
PAD_R_STICK_X,
PAD_R_STICK_Y,
XRPAD_TOUCHPAD_X,
XRPAD_TOUCHPAD_Y,
XRPAD_STICK_X,
XRPAD_STICK_Y
}
};
var MAPS = {
DEFAULT: {
buttons: [
// Face buttons
'PAD_FACE_1',
'PAD_FACE_2',
'PAD_FACE_3',
'PAD_FACE_4',
// Shoulder buttons
'PAD_L_SHOULDER_1',
'PAD_R_SHOULDER_1',
'PAD_L_SHOULDER_2',
'PAD_R_SHOULDER_2',
// Other buttons
'PAD_SELECT',
'PAD_START',
'PAD_L_STICK_BUTTON',
'PAD_R_STICK_BUTTON',
// D Pad
'PAD_UP',
'PAD_DOWN',
'PAD_LEFT',
'PAD_RIGHT',
// Vendor specific button
'PAD_VENDOR'
],
axes: [
// Analog Sticks
'PAD_L_STICK_X',
'PAD_L_STICK_Y',
'PAD_R_STICK_X',
'PAD_R_STICK_Y'
]
},
DEFAULT_DUAL: {
buttons: [
// Face buttons
'PAD_FACE_1',
'PAD_FACE_2',
'PAD_FACE_3',
'PAD_FACE_4',
// Shoulder buttons
'PAD_L_SHOULDER_1',
'PAD_R_SHOULDER_1',
'PAD_L_SHOULDER_2',
'PAD_R_SHOULDER_2',
// Other buttons
'PAD_SELECT',
'PAD_START',
'PAD_L_STICK_BUTTON',
'PAD_R_STICK_BUTTON',
// Vendor specific button
'PAD_VENDOR'
],
axes: [
// Analog Sticks
'PAD_L_STICK_X',
'PAD_L_STICK_Y',
'PAD_R_STICK_X',
'PAD_R_STICK_Y'
],
synthesizedButtons: {
PAD_UP: {
axis: 0,
min: 0,
max: 1
},
PAD_DOWN: {
axis: 0,
min: -1,
max: 0
},
PAD_LEFT: {
axis: 0,
min: -1,
max: 0
},
PAD_RIGHT: {
axis: 0,
min: 0,
max: 1
}
}
},
PS3: {
buttons: [
// X, O, TRI, SQ
'PAD_FACE_1',
'PAD_FACE_2',
'PAD_FACE_4',
'PAD_FACE_3',
// Shoulder buttons
'PAD_L_SHOULDER_1',
'PAD_R_SHOULDER_1',
'PAD_L_SHOULDER_2',
'PAD_R_SHOULDER_2',
// Other buttons
'PAD_SELECT',
'PAD_START',
'PAD_L_STICK_BUTTON',
'PAD_R_STICK_BUTTON',
// D Pad
'PAD_UP',
'PAD_DOWN',
'PAD_LEFT',
'PAD_RIGHT',
'PAD_VENDOR'
],
axes: [
// Analog Sticks
'PAD_L_STICK_X',
'PAD_L_STICK_Y',
'PAD_R_STICK_X',
'PAD_R_STICK_Y'
],
mapping: 'standard'
},
DEFAULT_XR: {
buttons: [
// Back buttons
'XRPAD_TRIGGER',
'XRPAD_SQUEEZE',
// Axes buttons
'XRPAD_TOUCHPAD_BUTTON',
'XRPAD_STICK_BUTTON',
// Face buttons
'XRPAD_A',
'XRPAD_B'
],
axes: [
// Analog Sticks
'XRPAD_TOUCHPAD_X',
'XRPAD_TOUCHPAD_Y',
'XRPAD_STICK_X',
'XRPAD_STICK_Y'
],
mapping: 'xr-standard'
}
};
var PRODUCT_CODES = {
'Product: 0268': 'PS3'
};
var custom_maps = {};
/**
* Retrieve the order for buttons and axes for given HTML5 Gamepad.
*
* @param {Gamepad} pad - The HTML5 Gamepad object.
* @returns {object} Object defining the order of buttons and axes for given HTML5 Gamepad.
*/ function getMap(pad) {
var custom = custom_maps[pad.id];
if (custom) {
return custom;
}
for(var code in PRODUCT_CODES){
if (pad.id.indexOf(code) !== -1) {
var product = PRODUCT_CODES[code];
if (!pad.mapping) {
var raw = MAPS["RAW_" + product];
if (raw) {
return raw;
}
}
return MAPS[product];
}
}
if (pad.mapping === 'xr-standard') {
return MAPS.DEFAULT_XR;
}
var defaultmap = MAPS.DEFAULT;
var map = pad.buttons.length < defaultmap.buttons.length ? MAPS.DEFAULT_DUAL : defaultmap;
map.mapping = pad.mapping;
return map;
}
var deadZone = 0.25;
/**
* @param {number} ms - Number of milliseconds to sleep for.
* @returns {Promise<void>}
*/ function sleep(ms) {
return new Promise((resolve)=>{
setTimeout(resolve, ms);
});
}
/**
* A GamePadButton stores information about a button from the Gamepad API.
*
* @category Input
*/ class GamePadButton {
/**
* Update the existing GamePadButton Instance.
*
* @param {GamepadButton} button - The original Gamepad API gamepad button.
* @ignore
*/ update(button) {
var { value, pressed } = button;
var _button_touched;
var touched = (_button_touched = button.touched) != null ? _button_touched : value > 0;
this.wasPressed = !this.pressed && pressed;
this.wasReleased = this.pressed && !pressed;
this.wasTouched = !this.touched && touched;
this.value = value;
this.pressed = pressed;
this.touched = touched;
}
/**
* Create a new GamePadButton instance.
*
* @param {number|GamepadButton} current - The original Gamepad API gamepad button.
* @param {number|GamepadButton} [previous] - The previous Gamepad API gamepad button.
* @ignore
*/ constructor(current, previous){
/**
* The value for the button between 0 and 1, with 0 representing a button that is not pressed, and 1 representing a button that is fully pressed.
*
* @type {number}
*/ this.value = 0;
/**
* Whether the button is currently down.
*
* @type {boolean}
*/ this.pressed = false;
/**
* Whether the button is currently touched.
*
* @type {boolean}
*/ this.touched = false;
/**
* Whether the button was pressed.
*
* @type {boolean}
*/ this.wasPressed = false;
/**
* Whether the button was released since the last update.
*
* @type {boolean}
*/ this.wasReleased = false;
/**
* Whether the button was touched since the last update.
*
* @type {boolean}
*/ this.wasTouched = false;
if (typeof current === 'number') {
this.value = current;
this.pressed = current === 1;
this.touched = current > 0;
} else {
this.value = current.value;
this.pressed = current.pressed;
var _current_touched;
this.touched = (_current_touched = current.touched) != null ? _current_touched : current.value > 0;
}
if (previous) {
if (typeof previous === 'number') {
this.wasPressed = previous !== 1 && this.pressed;
this.wasReleased = previous === 1 && !this.pressed;
this.wasTouched = previous === 0 && this.touched;
} else {
this.wasPressed = !previous.pressed && this.pressed;
this.wasReleased = previous.pressed && !this.pressed;
var _previous_touched;
this.wasTouched = !((_previous_touched = previous.touched) != null ? _previous_touched : previous.value > 0) && this.touched;
}
}
}
}
var dummyButton = Object.freeze(new GamePadButton(0));
/**
* A GamePad stores information about a gamepad from the Gamepad API.
*
* @category Input
*/ class GamePad {
/**
* Gets whether the gamepad is connected.
*
* @type {boolean}
*/ get connected() {
return this.pad.connected;
}
/**
* Compile the buttons mapping to reduce lookup delay.
*
* @private
*/ _compileMapping() {
var { axes, buttons } = this._compiledMapping;
var axesIndexes = MAPS_INDEXES.axes;
var buttonsIndexes = MAPS_INDEXES.buttons;
// Clear existing
axes.length = 0;
buttons.length = 0;
// Add axes
var axesMap = this.map.axes;
if (axesMap) {
this.map.axes.forEach((axis, i)=>{
axes[axesIndexes[axis]] = ()=>this.pad.axes[i] || 0;
});
}
// Fill empty indexes for axes
for(var i = 0, l = axes.length; i < l; i++){
if (!axes[i]) {
axes[i] = ()=>0;
}
}
// Add basic buttons
var buttonsMap = this.map.buttons;
if (buttonsMap) {
buttonsMap.forEach((button, i)=>{
buttons[buttonsIndexes[button]] = ()=>this._buttons[i] || dummyButton;
});
}
// Add synthesized buttons
var synthesizedButtonsMap = this.map.synthesizedButtons;
if (synthesizedButtonsMap) {
Object.entries(synthesizedButtonsMap).forEach((button)=>{
var { axis, max, min } = button[1];
buttons[buttonsIndexes[button[0]]] = ()=>{
var _this__axes_axis, _this__previousAxes_axis;
return new GamePadButton(Math.abs(math.clamp((_this__axes_axis = this._axes[axis]) != null ? _this__axes_axis : 0, min, max)), Math.abs(math.clamp((_this__previousAxes_axis = this._previousAxes[axis]) != null ? _this__previousAxes_axis : 0, min, max)));
};
});
}
// Fill empty indexes for buttons
for(var i1 = 0, l1 = buttons.length; i1 < l1; i1++){
if (!buttons[i1]) {
buttons[i1] = ()=>dummyButton;
}
}
}
/**
* Update the existing GamePad Instance.
*
* @param {Gamepad} gamepad - The original Gamepad API gamepad.
* @ignore
*/ update(gamepad) {
this.pad = gamepad;
var previousAxes = this._previousAxes;
var axes = this._axes;
// Store previous values for axes for dual buttons.
previousAxes.length = 0;
previousAxes.push(...axes);
// Update axes
axes.length = 0;
axes.push(...gamepad.axes);
// Update buttons
var buttons = this._buttons;
for(var i = 0, l = buttons.length; i < l; i++){
buttons[i].update(gamepad.buttons[i]);
}
return this;
}
/**
* Update the map for this gamepad.
*
* @param {object} map - The new mapping for this gamepad.
* @param {string[]} map.buttons - Buttons mapping for this gamepad.
* @param {string[]} map.axes - Axes mapping for this gamepad.
* @param {object} [map.synthesizedButtons] - Information about buttons to pull from axes for this gamepad. Requires definition of axis index, min value and max value.
* @param {"custom"} [map.mapping] - New mapping format. Will be forced into "custom".
* @example
* this.pad.updateMap({
* buttons: [[
* 'PAD_FACE_1',
* 'PAD_FACE_2',
* 'PAD_FACE_3',
* 'PAD_FACE_4',
* 'PAD_L_SHOULDER_1',
* 'PAD_R_SHOULDER_1',
* 'PAD_L_SHOULDER_2',
* 'PAD_R_SHOULDER_2',
* 'PAD_SELECT',
* 'PAD_START',
* 'PAD_L_STICK_BUTTON',
* 'PAD_R_STICK_BUTTON',
* 'PAD_VENDOR'
* ],
* axes: [
* 'PAD_L_STICK_X',
* 'PAD_L_STICK_Y',
* 'PAD_R_STICK_X',
* 'PAD_R_STICK_Y'
* ],
* synthesizedButtons: {
* PAD_UP: { axis: 0, min: 0, max: 1 },
* PAD_DOWN: { axis: 0, min: -1, max: 0 },
* PAD_LEFT: { axis: 0, min: -1, max: 0 },
* PAD_RIGHT: { axis: 0, min: 0, max: 1 }
* }
* });
*/ updateMap(map) {
map.mapping = 'custom';
// Save the map in case of disconnection.
custom_maps[this.id] = map;
this.map = map;
this.mapping = 'custom';
this._compileMapping();
}
/**
* Reset gamepad mapping to default.
*/ resetMap() {
if (custom_maps[this.id]) {
delete custom_maps[this.id];
var map = getMap(this.pad);
this.map = map;
this.mapping = map.mapping;
this._compileMapping();
}
}
/**
* Gets the values from analog axes present on the GamePad. Values are between -1 and 1.
*
* @type {number[]}
*/ get axes() {
return this._compiledMapping.axes.map((a)=>a());
}
/**
* Gets the buttons present on the GamePad.
*
* @type {GamePadButton[]}
*/ get buttons() {
return this._compiledMapping.buttons.map((b)=>b());
}
/**
* Make the gamepad vibrate.
*
* @param {number} intensity - Intensity for the vibration in the range 0 to 1.
* @param {number} duration - Duration for the vibration in milliseconds.
* @param {object} [options] - Options for special vibration pattern.
* @param {number} [options.startDelay] - Delay before the pattern starts, in milliseconds. Defaults to 0.
* @param {number} [options.strongMagnitude] - Intensity for strong actuators in the range 0 to 1. Defaults to intensity.
* @param {number} [options.weakMagnitude] - Intensity for weak actuators in the range 0 to 1. Defaults to intensity.
* @returns {Promise<boolean>} Return a Promise resulting in true if the pulse was successfully completed.
*/ pulse(intensity, duration, options) {
var _this = this;
return _async_to_generator(function*() {
var actuators = _this.pad.vibrationActuator ? [
_this.pad.vibrationActuator
] : _this.pad.hapticActuators || dummyArray;
if (actuators.length) {
var _options_startDelay;
var startDelay = (_options_startDelay = options == null ? void 0 : options.startDelay) != null ? _options_startDelay : 0;
var _options_strongMagnitude;
var strongMagnitude = (_options_strongMagnitude = options == null ? void 0 : options.strongMagnitude) != null ? _options_strongMagnitude : intensity;
var _options_weakMagnitude;
var weakMagnitude = (_options_weakMagnitude = options == null ? void 0 : options.weakMagnitude) != null ? _options_weakMagnitude : intensity;
var results = yield Promise.all(actuators.map(/*#__PURE__*/ _async_to_generator(function*(actuator) {
if (!actuator) {
return true;
}
if (actuator.playEffect) {
return actuator.playEffect(actuator.type, {
duration,
startDelay,
strongMagnitude,
weakMagnitude
});
} else if (actuator.pulse) {
yield sleep(startDelay);
return actuator.pulse(intensity, duration);
}
return false;
})));
return results.some((r)=>r === true || r === 'complete');
}
return false;
})();
}
/**
* Retrieve a button from its index.
*
* @param {number} index - The index to return the button for.
* @returns {GamePadButton} The button for the searched index. May be a placeholder if none found.
*/ getButton(index) {
var button = this._compiledMapping.buttons[index];
return button ? button() : dummyButton;
}
/**
* Returns true if the button is pressed.
*
* @param {number} button - The button to test, use constants {@link PAD_FACE_1}, etc.
* @returns {boolean} True if the button is pressed.
*/ isPressed(button) {
return this.getButton(button).pressed;
}
/**
* Return true if the button was pressed since the last update.
*
* @param {number} button - The button to test, use constants {@link PAD_FACE_1}, etc.
* @returns {boolean} Return true if the button was pressed, false if not.
*/ wasPressed(button) {
return this.getButton(button).wasPressed;
}
/**
* Return true if the button was released since the last update.
*
* @param {number} button - The button to test, use constants {@link PAD_FACE_1}, etc.
* @returns {boolean} Return true if the button was released, false if not.
*/ wasReleased(button) {
return this.getButton(button).wasReleased;
}
/**
* Returns true if the button is touched.
*
* @param {number} button - The button to test, use constants {@link PAD_FACE_1}, etc.
* @returns {boolean} True if the button is touched.
*/ isTouched(button) {
return this.getButton(button).touched;
}
/**
* Return true if the button was touched since the last update.
*
* @param {number} button - The button to test, use constants {@link PAD_FACE_1}, etc.
* @returns {boolean} Return true if the button was touched, false if not.
*/ wasTouched(button) {
return this.getButton(button).wasTouched;
}
/**
* Returns the value of a button between 0 and 1, with 0 representing a button that is not pressed, and 1 representing a button that is fully pressed.
*
* @param {number} button - The button to retrieve, use constants {@link PAD_FACE_1}, etc.
* @returns {number} The value of the button between 0 and 1.
*/ getValue(button) {
return this.getButton(button).value;
}
/**
* Get the value of one of the analog axes of the pad.
*
* @param {number} axis - The axis to get the value of, use constants {@link PAD_L_STICK_X}, etc.
* @returns {number} The value of the axis between -1 and 1.
*/ getAxis(axis) {
var a = this.axes[axis];
return a && Math.abs(a) > deadZone ? a : 0;
}
/**
* Create a new GamePad Instance.
*
* @param {Gamepad} gamepad - The original Gamepad API gamepad.
* @param {object} map - The buttons and axes map.
* @ignore
*/ constructor(gamepad, map){
/**
* The compiled mapping to reduce lookup delay when retrieving buttons
*
* @type {object}
* @private
*/ this._compiledMapping = {
buttons: [],
axes: []
};
/**
* The identifier for the gamepad. Its structure depends on device.
*
* @type {string}
*/ this.id = gamepad.id;
/**
* The index for this controller. A gamepad that is disconnected and reconnected will retain the same index.
*
* @type {number}
*/ this.index = gamepad.index;
/**
* The buttons present on the GamePad. Order is provided by API, use GamePad#buttons instead.
*
* @type {GamePadButton[]}
* @private
*/ this._buttons = gamepad.buttons.map((b)=>new GamePadButton(b));
/**
* The axes values from the GamePad. Order is provided by API, use GamePad#axes instead.
*
* @type {number[]}
* @private
*/ this._axes = [
...gamepad.axes
];
/**
* Previous value for the analog axes present on the gamepad. Values are between -1 and 1.
*
* @type {number[]}
* @private
*/ this._previousAxes = [
...gamepad.axes
];
/**
* The gamepad mapping detected by the browser. Value is either "standard", "xr-standard", "" or "custom". When empty string, you may need to update the mapping yourself. "custom" means you updated the mapping.
*
* @type {string}
*/ this.mapping = map.mapping;
/**
* The buttons and axes map.
*
* @type {object}
*/ this.map = map;
/**
* The hand this gamepad is usually handled on. Only relevant for XR pads. Value is either "left", "right" or "none".
*
* @type {string}
*/ this.hand = gamepad.hand || 'none';
/**
* The original Gamepad API gamepad.
*
* @type {Gamepad}
* @ignore
*/ this.pad = gamepad;
this._compileMapping();
}
}
/**
* Input handler for accessing GamePad input.
*
* @category Input
*/ class GamePads extends EventHandler {
/**
* Sets the threshold for axes to return values. Must be between 0 and 1.
*
* @type {number}
* @ignore
*/ set deadZone(value) {
deadZone = value;
}
/**
* Gets the threshold for axes to return values.
*
* @type {number}
* @ignore
*/ get deadZone() {
return deadZone;
}
/**
* Gets the list of previous button states.
*
* @type {boolean[][]}
* @ignore
*/ get previous() {
var current = this.current;
for(var i = 0, l = current.length; i < l; i++){
var buttons = current[i]._buttons;
if (!this._previous[i]) {
this._previous[i] = [];
}
for(var j = 0, m = buttons.length; j < m; j++){
var button = buttons[i];
this.previous[i][j] = button ? !button.wasPressed && button.pressed || button.wasReleased : false;
}
}
this._previous.length = this.current.length;
return this._previous;
}
/**
* Callback function when a gamepad is connecting.
*
* @param {GamepadEvent} event - The event containing the connecting gamepad.
* @private
*/ _ongamepadconnected(event) {
var pad = new GamePad(event.gamepad, this.getMap(event.gamepad));
var current = this.current;
var padIndex = current.findIndex((gp)=>gp.index === pad.index);
while(padIndex !== -1){
current.splice(padIndex, 1);
padIndex = current.findIndex((gp)=>gp.index === pad.index);
}
current.push(pad);
this.fire(EVENT_GAMEPADCONNECTED, pad);
}
/**
* Callback function when a gamepad is disconnecting.
*
* @param {GamepadEvent} event - The event containing the disconnecting gamepad.
* @private
*/ _ongamepaddisconnected(event) {
var current = this.current;
var padIndex = current.findIndex((gp)=>gp.index === event.gamepad.index);
if (padIndex !== -1) {
this.fire(EVENT_GAMEPADDISCONNECTED, current[padIndex]);
current.splice(padIndex, 1);
}
}
/**
* Update the previous state of the gamepads. This must be called every frame for
* `wasPressed` and `wasTouched` to work.
*
* @ignore
*/ update() {
this.poll();
}
/**
* Poll for the latest data from the gamepad API.
*
* @param {GamePad[]} [pads] - An optional array used to receive the gamepads mapping. This
* array will be returned by this function.
* @returns {GamePad[]} An array of gamepads and mappings for the model of gamepad that is
* attached.
* @example
* const gamepads = new pc.GamePads();
* const pads = gamepads.poll();
*/ poll(pads) {
if (pads === void 0) pads = [];
if (pads.length > 0) {
pads.length = 0;
}
var padDevices = getGamepads();
for(var i = 0, len = padDevices.length; i < len; i++){
if (padDevices[i]) {
var pad = this.findByIndex(padDevices[i].index);
if (pad) {
pads.push(pad.update(padDevices[i]));
} else {
var nPad = new GamePad(padDevices[i], this.getMap(padDevices[i]));
this.current.push(nPad);
pads.push(nPad);
}
}
}
return pads;
}
/**
* Destroy the event listeners.
*
* @ignore
*/ destroy() {
window.removeEventListener('gamepadconnected', this._ongamepadconnectedHandler, false);
window.removeEventListener('gamepaddisconnected', this._ongamepaddisconnectedHandler, false);
}
/**
* Retrieve the order for buttons and axes for given HTML5 Gamepad.
*
* @param {Gamepad} pad - The HTML5 Gamepad object.
* @returns {object} Object defining the order of buttons and axes for given HTML5 Gamepad.
*/ getMap(pad) {
return getMap(pad);
}
/**
* Returns true if the button on the pad requested is pressed.
*
* @param {number} orderIndex - The order index of the pad to check, use constants {@link PAD_1}, {@link PAD_2}, etc. For gamepad index call the function from the pad.
* @param {number} button - The button to test, use constants {@link PAD_FACE_1}, etc.
* @returns {boolean} True if the button is pressed.
*/ isPressed(orderIndex, button) {
var _this_current_orderIndex;
return ((_this_current_orderIndex = this.current[orderIndex]) == null ? void 0 : _this_current_orderIndex.isPressed(button)) || false;
}
/**
* Returns true if the button was pressed since the last frame.
*
* @param {number} orderIndex - The index of the pad to check, use constants {@link PAD_1}, {@link PAD_2}, etc. For gamepad index call the function from the pad.
* @param {number} button - The button to test, use constants {@link PAD_FACE_1}, etc.
* @returns {boolean} True if the button was pressed since the last frame.
*/ wasPressed(orderIndex, button) {
var _this_current_orderIndex;
return ((_this_current_orderIndex = this.current[orderIndex]) == null ? void 0 : _this_current_orderIndex.wasPressed(button)) || false;
}
/**
* Returns true if the button was released since the last frame.
*
* @param {number} orderIndex - The index of the pad to check, use constants {@link PAD_1}, {@link PAD_2}, etc. For gamepad index call the function from the pad.
* @param {number} button - The button to test, use constants {@link PAD_FACE_1}, etc.
* @returns {boolean} True if the button was released since the last frame.
*/ wasReleased(orderIndex, button) {
var _this_current_orderIndex;
return ((_this_current_orderIndex = this.current[orderIndex]) == null ? void 0 : _this_current_orderIndex.wasReleased(button)) || false;
}
/**
* Get the value of one of the analog axes of the pad.
*
* @param {number} orderIndex - The index of the pad to check, use constants {@link PAD_1}, {@link PAD_2}, etc. For gamepad index call the function from the pad.
* @param {number} axis - The axis to get the value of, use constants {@link PAD_L_STICK_X}, etc.
* @returns {number} The value of the axis between -1 and 1.
*/ getAxis(orderIndex, axis) {
var _this_current_orderIndex;
return ((_this_current_orderIndex = this.current[orderIndex]) == null ? void 0 : _this_current_orderIndex.getAxis(axis)) || 0;
}
/**
* Make the gamepad vibrate.
*
* @param {number} orderIndex - The index of the pad to check, use constants {@link PAD_1}, {@link PAD_2}, etc. For gamepad index call the function from the pad.
* @param {number} intensity - Intensity for the vibration in the range 0 to 1.
* @param {number} duration - Duration for the vibration in milliseconds.
* @param {object} [options] - Options for special vibration pattern.
* @param {number} [options.startDelay] - Delay before the pattern starts, in milliseconds. Defaults to 0.
* @param {number} [options.strongMagnitude] - Intensity for strong actuators in the range 0 to 1. Defaults to intensity.
* @param {number} [options.weakMagnitude] - Intensity for weak actuators in the range 0 to 1. Defaults to intensity.
* @returns {Promise<boolean>} Return a Promise resulting in true if the pulse was successfully completed.
*/ pulse(orderIndex, intensity, duration, options) {
var pad = this.current[orderIndex];
return pad ? pad.pulse(intensity, duration, options) : Promise.resolve(false);
}
/**
* Make all gamepads vibrate.
*
* @param {number} intensity - Intensity for the vibration in the range 0 to 1.
* @param {number} duration - Duration for the vibration in milliseconds.
* @param {object} [options] - Options for special vibration pattern.
* @param {number} [options.startDelay] - Delay before the pattern starts, in milliseconds. Defaults to 0.
* @param {number} [options.strongMagnitude] - Intensity for strong actuators in the range 0 to 1. Defaults to intensity.
* @param {number} [options.weakMagnitude] - Intensity for weak actuators in the range 0 to 1. Defaults to intensity.
* @returns {Promise<boolean[]>} Return a Promise resulting in an array of booleans defining if the pulse was successfully completed for every gamepads.
*/ pulseAll(intensity, duration, options) {
return Promise.all(this.current.map((pad)=>pad.pulse(intensity, duration, options)));
}
/**
* Find a connected {@link GamePad} from its identifier.
*
* @param {string} id - The identifier to search for.
* @returns {GamePad|null} The {@link GamePad} with the matching identifier or null if no gamepad is found or the gamepad is not connected.
*/ findById(id) {
return this.current.find((gp)=>gp && gp.id === id) || null;
}
/**
* Find a connected {@link GamePad} from its device index.
*
* @param {number} index - The device index to search for.
* @returns {GamePad|null} The {@link GamePad} with the matching device index or null if no gamepad is found or the gamepad is not connected.
*/ findByIndex(index) {
return this.current.find((gp)=>gp && gp.index === index) || null;
}
/**
* Create a new GamePads instance.
*/ constructor(){
super();
/**
* Whether gamepads are supported by this device.
*
* @type {boolean}
*/ this.gamepadsSupported = platform.gamepads;
/**
* The list of current gamepads.
*
* @type {GamePad[]}
*/ this.current = [];
/**
* The list of previous buttons states
*
* @type {boolean[][]}
* @private
*/ this._previous = [];
this._ongamepadconnectedHandler = this._ongamepadconnected.bind(this);
this._ongamepaddisconnectedHandler = this._ongamepaddisconnected.bind(this);
window.addEventListener('gamepadconnected', this._ongamepadconnectedHandler, false);
window.addEventListener('gamepaddisconnected', this._ongamepaddisconnectedHandler, false);
this.poll();
}
}
/**
* Fired when a gamepad is connected. The handler is passed the {@link GamePad} object that was
* connected.
*
* @event
* @example
* const onPadConnected = (pad) => {
* if (!pad.mapping) {
* // Map the gamepad as the system could not find the proper map.
* } else {
* // Make the gamepad pulse.
* }
* };
*
* app.keyboard.on("gamepadconnected", onPadConnected, this);
*/ GamePads.EVENT_GAMEPADCONNECTED = 'gamepadconnected';
/**
* Fired when a gamepad is disconnected. The handler is passed the {@link GamePad} object that
* was disconnected.
*
* @event
* @example
* const onPadDisconnected = (pad) => {
* // Pause the game.
* };
*
* app.keyboard.on("gamepaddisconnected", onPadDisconnected, this);
*/ GamePads.EVENT_GAMEPADDISCONNECTED = 'gamepaddisconnected';
export { GamePad, GamePadButton, GamePads };