added a demo mode

This commit is contained in:
e7d 2017-05-16 17:18:11 +02:00
parent af6c61bec5
commit 495a49f681
5 changed files with 251 additions and 10 deletions

View File

@ -65,6 +65,7 @@
<script src="https://static.e7d.io/libs/jquery/3.1.1/jquery.min.js"></script>
<script src="js/urlParam.jquery.js"></script>
<script src="js/gamepad-demo.js"></script>
<script src="js/gamepad.js"></script>
<script src="js/app.js"></script>
</body>

View File

@ -1 +1 @@
window.gamepad = new Gamepad();
window.gamepad = new Gamepad();

221
js/gamepad-demo.js Normal file
View File

@ -0,0 +1,221 @@
/**
* The Gamepad demo class
*
* @class GamepadDemo
*/
class GamepadDemo {
/**
* Creates an instance of GamepadDemo.
*
* @param Gamepad gamepad
*/
constructor(gamepad) {
this.gamepad = gamepad;
this.demoGamepad = {
'id': id,
'timestamp': 0,
'index': 'demo',
'mapping': 'standard',
'axes': [],
'buttons': [],
};
for (let axisIndex = 0; axisIndex < 6; axisIndex++) {
this.demoGamepad.axes[axisIndex] = 0;
}
for (let buttonIndex = 0; buttonIndex < 20; buttonIndex++) {
this.demoGamepad.buttons[buttonIndex] = {
pressed: false,
value: 0
};
}
}
/**
* Starts the demonstration mode
*
* @param {string} [mode='random']
* @param {string} [id='xinput']
*/
start(mode = 'random', id = 'xinput') {
// remove any active gamepad
this.gamepad.removeGamepad(true);
// add the demo gamepad to the gamepads list
this.gamepad.gamepads['demo'] = this.demoGamepad;
// map the demo gamepad as active gamepad
this.gamepad.mapGamepad('demo');
// determine the callback to use following the demo mode
let callback;
switch (mode) {
case 'random':
callback = this.randomModeCallback;
this.demoUpdateDelay = 100;
break;
case 'realist':
callback = this.realistModeCallback;
this.demoUpdateDelay = false;
break;
case 'sequential':
default:
callback = this.launchSequentialDemoMode;
this.controlType = 'axis';
this.controlIndex = 0;
this.demoUpdateDelay = 1000;
break;
}
// execute the callback once
callback.bind(this)();
if (this.demoUpdateDelay) {
// setup an repeat callback if needed
this.demoInterval = window.setInterval(
() => {
callback.bind(this)();
},
this.demoUpdateDelay
);
}
}
/**
* Stops the demonstration mode
*/
stop() {
// stop any running demo callback
window.clearInterval(this.demoInterval);
}
/**
* Executes the random mode callback
*/
randomModeCallback() {
// set a random value for each axis
for (let axisIndex = 0; axisIndex < this.demoGamepad.axes.length; axisIndex++) {
this.demoGamepad.axes[axisIndex] = (Math.random() * 2) - 1;
}
// set a random value for each button
for (let buttonIndex = 0; buttonIndex < this.demoGamepad.buttons.length; buttonIndex++) {
this.pressButton(buttonIndex, Math.random() > 0.5, Math.random());
}
}
/**
* Executes the realistic mode callback
*/
realistModeCallback() {
// TODO: capture a real usage, approx. 10s
}
/**
* Executes the sequential mode callback
*/
sequentialModeCallback() {
// store the current index locally
const index = this.controlIndex;
// Axis
if ('axis' === this.controlType) {
// move axis from neutral to maximum, then minimum, then neutral again
for (let i = 0; i <= 1; i = +(i + 0.01).toFixed(2)) {
let value = 0;
if (i > 0.75) {
value = (1 - i) * 4;
} else if (i > 0.25) {
value = (i - 0.5) * 4;
} else {
value = (-i) * 4;
}
window.setTimeout(
() => {
this.moveAxis(
index,
value
);
},
this.demoUpdateDelay * 0.7 * i
)
}
// job is done for this axis, move to next axis
this.controlIndex++;
if (this.controlIndex >= this.demoGamepad.axes.length) {
// if all axis where animated, change for buttons
this.controlType = 'button';
this.controlIndex = 0;
}
return;
}
if ('button' === this.controlType) {
// update button pressure from null to maximum, then release again
for (let i = 0; i <= 1; i = +(i + 0.01).toFixed(2)) {
let value = 0;
if (i > 0.5) {
value = (1 - i) * 2;
} else {
value = i * 2;
}
window.setTimeout(
() => {
this.pressButton(
index,
value > 0,
value
);
},
this.demoUpdateDelay * 0.7 * i
)
}
// job is done for this axis, move to next button
this.controlIndex++;
if (this.controlIndex >= this.demoGamepad.buttons.length) {
// if all axis where animated, change for axes
this.controlType = 'axis';
this.controlIndex = 0;
}
return;
}
}
/**
* Simulates a button press
*
* @param {any} index
* @param {boolean} [pressed=true]
* @param {number} [value=1]
*/
pressButton(index, pressed = true, value = 1) {
// no pressure means a null value
if (!pressed) {
value = 0;
}
// update gamepad timestamp
this.demoGamepad.timestamp++;
// update button status
this.demoGamepad.buttons[index] = {
'pressed': pressed,
'value': value
};
}
/**
* Simulates an axis movement
*
* @param {any} index
* @param {number} [value=1]
*/
moveAxis(index, value = 1) {
// update gamepad timestamp
this.demoGamepad.timestamp++;
// update axis status
this.demoGamepad.axes[index] = value;
}
}

View File

@ -8,7 +8,7 @@ class Gamepad {
* Creates an instance of Gamepad.
*/
constructor() {
this.haveEvents = 'GamepadEvent' in window;
this.gamepadDemo = new GamepadDemo(this);
// cached DOM references
this.$gamepad = $('.gamepad');
@ -56,8 +56,11 @@ class Gamepad {
};
// listen for gamepad related events
window.addEventListener("gamepadconnected", this.onGamepadConnect.bind(this));
window.addEventListener("gamepaddisconnected", this.onGamepadDisconnect.bind(this));
this.haveEvents = 'GamepadEvent' in window;
if (this.haveEvents) {
window.addEventListener("gamepadconnected", this.onGamepadConnect.bind(this));
window.addEventListener("gamepaddisconnected", this.onGamepadDisconnect.bind(this));
}
// listen for keyboard events
window.addEventListener("keydown", this.onKeyDown.bind(this));
@ -67,6 +70,7 @@ class Gamepad {
// read URI for display parameters to initalize
this.params = {
demoMode: $.urlParam('demo') || null,
gamepadColor: $.urlParam('color') || $.urlParam('c') || null,
gamepadIndex: $.urlParam('index') || $.urlParam('i') || null,
gamepadType: $.urlParam('type') || $.urlParam('t') || null,
@ -81,6 +85,12 @@ class Gamepad {
return;
}
if (this.params.demoMode) {
this.gamepadDemo.start(this.params.demoMode);
return;
}
// by default, enqueue a delayed display of the help modal
this.displayGamepadHelp();
}
@ -139,7 +149,7 @@ class Gamepad {
switch (e.code) {
case "Delete":
case "Escape":
this.removeGamepad(this.activeGamepadIndex);
this.removeGamepad(true);
break;
case "KeyC":
this.changeGamepadColor();
@ -170,7 +180,10 @@ class Gamepad {
*/
refreshGamepads() {
// get fresh information from DOM about gamepads
this.gamepads = navigator.getGamepads ? navigator.getGamepads() : (navigator.webkitGetGamepads ? navigator.webkitGetGamepads() : []);
const navigatorGamepads = navigator.getGamepads ? navigator.getGamepads() : (navigator.webkitGetGamepads ? navigator.webkitGetGamepads() : []);
for (let key in navigatorGamepads) {
this.gamepads[key] = navigatorGamepads[key];
}
}
/**
@ -200,8 +213,14 @@ class Gamepad {
return;
}
// ensure to kill demo mode
if ('demo' === this.activeGamepadIndex) {
this.gamepadDemo.stop();
}
// if this is the active gamepad
if (gamepadIndex === this.activeGamepadIndex) {
if (true === gamepadIndex ||
this.activeGamepadIndex === gamepadIndex) {
// clear associated date
this.activeGamepadIndex = null;
this.activeGamepad = null;

View File

@ -1,8 +1,8 @@
$.urlParam = function(name) {
let results = new RegExp('[\?&]' + name + '=([^&#]*)').exec(window.location.href);
let results = new RegExp('[\?&]' + name + '(=([^&#]*))?').exec(window.location.href);
if (results === null) {
return null;
} else {
return decodeURIComponent(results[1]) || 0;
return decodeURIComponent(results[2] || true) || true;
}
};
};