|
//-----------------------------------------------------------------------------
/**从键盘和游戏手柄处理输入数据的静态类。
* The static class that handles input data from the keyboard and gamepads.
*
* @class Input
*/
function Input() {
throw new Error('This is a static class');
}
/**初始化输入系统。
* Initializes the input system.
*
* @static
* @method initialize
*/
Input.initialize = function() {
this.clear();
this._wrapNwjsAlert();
this._setupEventHandlers();
};
/**在帧中的键重复的等待时间
* The wait time of the key repeat in frames.
*
* @static
* @property keyRepeatWait
* @type Number
*/
Input.keyRepeatWait = 24;
/**在帧中的键重复的间隔时间
* The interval of the key repeat in frames.
*
* @static
* @property keyRepeatInterval
* @type Number
*/
Input.keyRepeatInterval = 6;
/**一个哈希表来从一个虚拟键代码转换为一个映射的键名
* A hash table to convert from a virtual key code to a mapped key name.
*
* @static
* @property keyMapper
* @type Object
*/
Input.keyMapper = {
9: 'tab', // tab
13: 'ok', // enter
16: 'shift', // shift
17: 'control', // control
18: 'control', // alt
27: 'escape', // escape
32: 'ok', // space
33: 'pageup', // pageup
34: 'pagedown', // pagedown
37: 'left', // left arrow
38: 'up', // up arrow
39: 'right', // right arrow
40: 'down', // down arrow
45: 'escape', // insert
81: 'pageup', // Q
87: 'pagedown', // W
88: 'escape', // X
90: 'ok', // Z
96: 'escape', // numpad 0
98: 'down', // numpad 2
100: 'left', // numpad 4
102: 'right', // numpad 6
104: 'up', // numpad 8
120: 'debug' // F9
};
/**一个哈希表来从手柄按钮转换为映射的键名。
* A hash table to convert from a gamepad button to a mapped key name.
*
* @static
* @property gamepadMapper
* @type Object
*/
Input.gamepadMapper = {
0: 'ok', // A
1: 'cancel', // B
2: 'shift', // X
3: 'menu', // Y
4: 'pageup', // LB
5: 'pagedown', // RB
12: 'up', // D-pad up
13: 'down', // D-pad down
14: 'left', // D-pad left
15: 'right', // D-pad right
};
/**清除所有的输入数据。
* Clears all the input data.
*
* @static
* @method clear
*/
Input.clear = function() {
this._currentState = {};
this._previousState = {};
this._gamepadStates = [];
this._latestButton = null;
this._pressedTime = 0;
this._dir4 = 0;
this._dir8 = 0;
this._preferredAxis = '';
this._date = 0;
};
/**更新的输入数据。
* Updates the input data.
*
* @static
* @method update
*/
Input.update = function() {
this._pollGamepads();
if (this._currentState[this._latestButton]) {
this._pressedTime++;
} else {
this._latestButton = null;
}
for (var name in this._currentState) {
if (this._currentState[name] && !this._previousState[name]) {
this._latestButton = name;
this._pressedTime = 0;
this._date = Date.now();
}
this._previousState[name] = this._currentState[name];
}
this._updateDirection();
};
/**检查一键当前是否按下。
* Checks whether a key is currently pressed down.
*
* @static
* @method isPressed
* @param {String} keyName The mapped name of the key
* @return {Boolean} True if the key is pressed
*/
Input.isPressed = function(keyName) {
if (this._isEscapeCompatible(keyName) && this.isPressed('escape')) {
return true;
} else {
return !!this._currentState[keyName];
}
};
/**检查是否有键刚按下
* Checks whether a key is just pressed.
*
* @static
* @method isTriggered
* @param {String} keyName The mapped name of the key
* @return {Boolean} True if the key is triggered
*/
Input.isTriggered = function(keyName) {
if (this._isEscapeCompatible(keyName) && this.isTriggered('escape')) {
return true;
} else {
return this._latestButton === keyName && this._pressedTime === 0;
}
};
/**检查一个按键是否只是按下或出现按键重复的。
* Checks whether a key is just pressed or a key repeat occurred.
*
* @static
* @method isRepeated
* @param {String} keyName The mapped name of the key
* @return {Boolean} True if the key is repeated
*/
Input.isRepeated = function(keyName) {
if (this._isEscapeCompatible(keyName) && this.isRepeated('escape')) {
return true;
} else {
return (this._latestButton === keyName &&
(this._pressedTime === 0 ||
(this._pressedTime >= this.keyRepeatWait &&
this._pressedTime % this.keyRepeatInterval === 0)));
}
};
/**检查一个按键是否被持续按下
* Checks whether a key is kept depressed.
*
* @static
* @method isLongPressed
* @param {String} keyName The mapped name of the key
* @return {Boolean} True if the key is long-pressed
*/
Input.isLongPressed = function(keyName) {
if (this._isEscapeCompatible(keyName) && this.isLongPressed('escape')) {
return true;
} else {
return (this._latestButton === keyName &&
this._pressedTime >= this.keyRepeatWait);
}
};
/**[只读]为数字小键盘上四方向值,或者0
* [read-only] The four direction value as a number of the numpad, or 0 for neutral.
*
* @static
* @property dir4
* @type Number
*/
//定义属性
Object.defineProperty(Input, 'dir4', {
get: function() {
return this._dir4;
},
configurable: true
});
/**[只读]八个方向值为数字小键盘上,或者为0中性。
* [read-only] The eight direction value as a number of the numpad, or 0 for neutral.
*
* @static
* @property dir8
* @type Number
*/
//定义属性
Object.defineProperty(Input, 'dir8', {
get: function() {
return this._dir8;
},
configurable: true
});
/**[只读]的最后一个输入的时间 毫秒
* [read-only] The time of the last input in milliseconds.
*
* @static
* @property date
* @type Number
*/
//定义属性
Object.defineProperty(Input, 'date', {
get: function() {
return this._date;
},
configurable: true
});
/**
* @static
* @method _wrapNwjsAlert
* @private
*/
Input._wrapNwjsAlert = function() {
if (Utils.isNwjs()) {
var _alert = window.alert;
window.alert = function() {
var gui = require('nw.gui');
var win = gui.Window.get();
_alert.apply(this, arguments);
win.focus();
Input.clear();
};
}
};
/**安装操作事件
* @static
* @method _setupEventHandlers
* @private
*/
Input._setupEventHandlers = function() {
document.addEventListener('keydown', this._onKeyDown.bind(this));
document.addEventListener('keyup', this._onKeyUp.bind(this));
window.addEventListener('blur', this._onLostFocus.bind(this));
};
/**当键按下
* @static
* @method _onKeyDown
* @param {KeyboardEvent} event
* @private
*/
Input._onKeyDown = function(event) {
if (this._shouldPreventDefault(event.keyCode)) {
event.preventDefault();
}
if (event.keyCode === 144) { // Numlock
this.clear();
}
var buttonName = this.keyMapper[event.keyCode];
if (buttonName) {
this._currentState[buttonName] = true;
}
};
/**需要避免默认
* @static
* @method _shouldPreventDefault
* @param {Number} keyCode
* @private
*/
Input._shouldPreventDefault = function(keyCode) {
switch (keyCode) {
case 8: // backspace
case 33: // pageup
case 34: // pagedown
case 37: // left arrow
case 38: // up arrow
case 39: // right arrow
case 40: // down arrow
return true;
}
return false;
};
/**当键抬起
* @static
* @method _onKeyUp
* @param {KeyboardEvent} event
* @private
*/
Input._onKeyUp = function(event) {
var buttonName = this.keyMapper[event.keyCode];
if (buttonName) {
this._currentState[buttonName] = false;
}
if (event.keyCode === 0) { // For QtWebEngine on OS X
this.clear();
}
};
/**当失去焦点
* @static
* @method _onLostFocus
* @private
*/
Input._onLostFocus = function() {
this.clear();
};
/**轮询Gamepads
* @static
* @method _pollGamepads
* @private
*/
Input._pollGamepads = function() {
if (navigator.getGamepads) {
var gamepads = navigator.getGamepads();
if (gamepads) {
for (var i = 0; i < gamepads.length; i++) {
var gamepad = gamepads[i];
if (gamepad && gamepad.connected) {
this._updateGamepadState(gamepad);
}
}
}
}
};
/**更新Gamepads状态
* @static
* @method _updateGamepadState
* @param {Gamepad} gamepad
* @param {Number} index
* @private
*/
Input._updateGamepadState = function(gamepad) {
var lastState = this._gamepadStates[gamepad.index] || [];
var newState = [];
var buttons = gamepad.buttons;
var axes = gamepad.axes;
var threshold = 0.5;
for (var i = 0; i < buttons.length; i++) {
newState[i] = buttons[i].pressed;
}
if (axes[1] < -threshold) {
newState[12] = true; // up
} else if (axes[1] > threshold) {
newState[13] = true; // down
}
if (axes[0] < -threshold) {
newState[14] = true; // left
} else if (axes[0] > threshold) {
newState[15] = true; // right
}
for (var j = 0; j < newState.length; j++) {
if (newState[j] !== lastState[j]) {
var buttonName = this.gamepadMapper[j];
if (buttonName) {
this._currentState[buttonName] = newState[j];
}
}
}
this._gamepadStates[gamepad.index] = newState;
};
/**更新方向
* @static
* @method _updateDirection
* @private
*/
Input._updateDirection = function() {
var x = this._signX();
var y = this._signY();
this._dir8 = this._makeNumpadDirection(x, y);
if (x !== 0 && y !== 0) {
if (this._preferredAxis === 'x') {
y = 0;
} else {
x = 0;
}
} else if (x !== 0) {
this._preferredAxis = 'y';
} else if (y !== 0) {
this._preferredAxis = 'x';
}
this._dir4 = this._makeNumpadDirection(x, y);
};
/**标记x
* @static
* @method _signX
* @private
*/
Input._signX = function() {
var x = 0;
if (this.isPressed('left')) {
x--;
}
if (this.isPressed('right')) {
x++;
}
return x;
};
/**标记y
* @static
* @method _signY
* @private
*/
Input._signY = function() {
var y = 0;
if (this.isPressed('up')) {
y--;
}
if (this.isPressed('down')) {
y++;
}
return y;
};
/**制作数字方向
* @static
* @method _makeNumpadDirection
* @param {Number} x
* @param {Number} y
* @return {Number}
* @private
*/
Input._makeNumpadDirection = function(x, y) {
if (x !== 0 || y !== 0) {
return 5 - y * 3 + x;
}
return 0;
};
/**是逃跑一致
* @static
* @method _isEscapeCompatible
* @param {String} keyName
* @return {Boolean}
* @private
*/
Input._isEscapeCompatible = function(keyName) {
return keyName === 'cancel' || keyName === 'menu';
};
站长信箱:[email protected]|手机版|小黑屋|无图版|Project1游戏制作
GMT+8, 2024-5-12 03:47
Powered by Discuz! X3.1
© 2001-2013 Comsenz Inc.