diff --git a/README.md b/README.md index 4dc1554c..e90d9fe7 100644 --- a/README.md +++ b/README.md @@ -73,7 +73,9 @@ Significant API changes: * After defining tiles that collide on a Tilemap, you need to call Tilemap.generateCollisionData(layer) to populate the physics world with the data required. * Phaser.QuadTree has been removed from core and moved to a plugin. It's no longer required, nor works with the physics system. * Phaser.Animation.frame now returns the frame of the current animation, rather than the global frame from the sprite sheet / atlas. -* When adding a Group if the parent value is `null` the Group won't be added to the World, so you can add it when ready. If parent is `undefined` it's added to World by default. +* When adding a Group if the parent value is `null` the Group won't be added to the World, so you can add it when ready. If parent is `undefined` it's added by default. +* The Keyboard class has had a complete overhaul. Phaser.Key objects are created automatically, there are fixes against duration and keys reset properly on visibility loss. +* Keyboard.removeKey has been removed. The way the new keyboard manager works means it's no longer required. New features: @@ -107,6 +109,7 @@ New features: * Loader can now load JSON files specifically (game.load.json) and they are parsed and stored in the Game.Cache. Retrieve with game.cache.getJSON(key). * TileSprites can now receive full Input events, dragging, etc and be positioned in-world and fixed to cameras. * The StateManager now looks for a function called 'resumed' which is called when a game un-pauses. +* Key.onHold added. This event is dispatched every time the browser sends a keydown event and the key is already being held down. Updates: diff --git a/examples/wip/twokeys.js b/examples/wip/twokeys.js new file mode 100644 index 00000000..2a656a04 --- /dev/null +++ b/examples/wip/twokeys.js @@ -0,0 +1,33 @@ + +var game = new Phaser.Game(800, 600, Phaser.CANVAS, 'phaser-example', { preload: preload, create: create, update: update, render: render }); + +function preload() { + +} + +var text; +var key1; +var key2; + +function create() { + + game.stage.setBackgroundColor(0x0b7276); + + game.add.text(32, 16, "SPACE"); + game.add.text(32, 170, "A"); + + key1 = game.input.keyboard.addKey(Phaser.Keyboard.SPACEBAR); + key2 = game.input.keyboard.addKey(Phaser.Keyboard.A); + +} + +function update() { + +} + +function render() { + + game.debug.renderKey(key1, 32, 64); + game.debug.renderKey(key2, 32, 220); + +} diff --git a/src/core/Game.js b/src/core/Game.js index 2c0e3285..0366f248 100644 --- a/src/core/Game.js +++ b/src/core/Game.js @@ -741,6 +741,7 @@ Object.defineProperty(Phaser.Game.prototype, "paused", { if (this._paused) { this._paused = false; + this.input.reset(); this.onResume.dispatch(this); } } diff --git a/src/input/Input.js b/src/input/Input.js index 844c8011..774f67c2 100644 --- a/src/input/Input.js +++ b/src/input/Input.js @@ -428,6 +428,8 @@ Phaser.Input.prototype = { */ update: function () { + this.keyboard.update(); + if (this.pollRate > 0 && this._pollCounter < this.pollRate) { this._pollCounter++; diff --git a/src/input/Key.js b/src/input/Key.js index c90f9753..c5038eca 100644 --- a/src/input/Key.js +++ b/src/input/Key.js @@ -49,8 +49,7 @@ Phaser.Key = function (game, keycode) { this.shiftKey = false; /** - * @property {number} timeDown - The timestamp when the key was last pressed down. - * @default + * @property {number} timeDown - The timestamp when the key was last pressed down. This is based on Game.time.now. */ this.timeDown = 0; @@ -63,10 +62,10 @@ Phaser.Key = function (game, keycode) { this.duration = 0; /** - * @property {number} timeUp - The timestamp when the key was last released. + * @property {number} timeUp - The timestamp when the key was last released. This is based on Game.time.now. * @default */ - this.timeUp = 0; + this.timeUp = -2500; /** * @property {number} repeats - If a key is held down this holds down the number of times the key has 'repeated'. @@ -84,6 +83,16 @@ Phaser.Key = function (game, keycode) { */ this.onDown = new Phaser.Signal(); + /** + * @property {function} onHoldCallback - A callback that is called while this Key is held down. Warning: Depending on refresh rate that could be 60+ times per second. + */ + this.onHoldCallback = null; + + /** + * @property {object} onHoldContext - The context under which the onHoldCallback will be called. + */ + this.onHoldContext = null; + /** * @property {Phaser.Signal} onUp - This Signal is dispatched every time this Key is pressed down. It is only dispatched once (until the key is released again). */ @@ -93,6 +102,21 @@ Phaser.Key = function (game, keycode) { Phaser.Key.prototype = { + update: function () { + + if (this.isDown) + { + this.duration = this.game.time.now - this.timeDown; + this.repeats++; + + if (this.onHoldCallback) + { + this.onHoldCallback.call(this.onHoldContext, this); + } + } + + }, + /** * Called automatically by Phaser.Keyboard. * @method Phaser.Key#processKeyDown @@ -101,26 +125,22 @@ Phaser.Key.prototype = { */ processKeyDown: function (event) { + if (this.isDown) + { + return; + } + this.altKey = event.altKey; this.ctrlKey = event.ctrlKey; this.shiftKey = event.shiftKey; - if (this.isDown) - { - // Key was already held down, this must be a repeat rate based event - this.duration = event.timeStamp - this.timeDown; - this.repeats++; - } - else - { - this.isDown = true; - this.isUp = false; - this.timeDown = event.timeStamp; - this.duration = 0; - this.repeats = 0; + this.isDown = true; + this.isUp = false; + this.timeDown = this.game.time.now; + this.duration = 0; + this.repeats = 0; - this.onDown.dispatch(this); - } + this.onDown.dispatch(this); }, @@ -132,14 +152,34 @@ Phaser.Key.prototype = { */ processKeyUp: function (event) { + if (this.isUp) + { + return; + } + this.isDown = false; this.isUp = true; - this.timeUp = event.timeStamp; + this.timeUp = this.game.time.now; + this.duration = this.game.time.now - this.timeDown; this.onUp.dispatch(this); }, + /** + * Resets the state of this Key. + * + * @method Phaser.Key#reset + */ + reset: function () { + + this.isDown = false; + this.isUp = true; + this.timeUp = this.game.time.now; + this.duration = this.game.time.now - this.timeDown; + + }, + /** * Returns the "just pressed" state of the Key. Just pressed is considered true if the key was pressed down within the duration given (default 250ms) * @method Phaser.Key#justPressed @@ -148,7 +188,7 @@ Phaser.Key.prototype = { */ justPressed: function (duration) { - if (typeof duration === "undefined") { duration = 250; } + if (typeof duration === "undefined") { duration = 2500; } return (this.isDown && this.duration < duration); @@ -162,9 +202,9 @@ Phaser.Key.prototype = { */ justReleased: function (duration) { - if (typeof duration === "undefined") { duration = 250; } + if (typeof duration === "undefined") { duration = 2500; } - return (this.isDown === false && (this.game.time.now - this.timeUp < duration)); + return (!this.isDown && ((this.game.time.now - this.timeUp) < duration)); } diff --git a/src/input/Keyboard.js b/src/input/Keyboard.js index c0a4c875..17e5eb80 100644 --- a/src/input/Keyboard.js +++ b/src/input/Keyboard.js @@ -19,22 +19,16 @@ Phaser.Keyboard = function (game) { this.game = game; /** - * @property {object} _keys - The object the key values are stored in. + * @property {array} _keys - The array the Phaser.Key objects are stored in. * @private */ - this._keys = {}; + this._keys = []; /** - * @property {object} _hotkeys - The object the hot keys are stored in. + * @property {array} _capture - The array the key capture values are stored in. * @private */ - this._hotkeys = {}; - - /** - * @property {object} _capture - The object the key capture values are stored in. - * @private - */ - this._capture = {}; + this._capture = []; /** * You can disable all Keyboard Input by setting disabled to true. While true all new input related events will be ignored. @@ -78,6 +72,7 @@ Phaser.Keyboard.prototype = { /** * Add callbacks to the Keyboard handler so that each time a key is pressed down or releases the callbacks are activated. + * * @method Phaser.Keyboard#addCallbacks * @param {Object} context - The context under which the callbacks are run. * @param {function} onDown - This callback is invoked every time a key is pressed down. @@ -105,23 +100,14 @@ Phaser.Keyboard.prototype = { */ addKey: function (keycode) { - this._hotkeys[keycode] = new Phaser.Key(this.game, keycode); + if (!this._keys[keycode]) + { + this._keys[keycode] = new Phaser.Key(this.game, keycode); - this.addKeyCapture(keycode); + this.addKeyCapture(keycode); + } - return this._hotkeys[keycode]; - - }, - - /** - * Removes a Key object from the Keyboard manager. - * - * @method Phaser.Keyboard#removeKey - * @param {number} keycode - The keycode of the key to remove, i.e. Phaser.Keyboard.UP or Phaser.Keyboard.SPACEBAR - */ - removeKey: function (keycode) { - - delete (this._hotkeys[keycode]); + return this._keys[keycode]; }, @@ -182,8 +168,9 @@ Phaser.Keyboard.prototype = { * There are some keys this can be annoying for, like the arrow keys or space bar, which make the browser window scroll. * You can use addKeyCapture to consume the keyboard event for specific keys so it doesn't bubble up to the the browser. * Pass in either a single keycode or an array/hash of keycodes. + * * @method Phaser.Keyboard#addKeyCapture - * @param {Any} keycode + * @param {Any} keycode - Either a single numeric keycode or an array/hash of keycodes: [65, 67, 68]. */ addKeyCapture: function (keycode) { @@ -202,6 +189,7 @@ Phaser.Keyboard.prototype = { /** * Removes an existing key capture. + * * @method Phaser.Keyboard#removeKeyCapture * @param {number} keycode */ @@ -213,6 +201,7 @@ Phaser.Keyboard.prototype = { /** * Clear all set key captures. + * * @method Phaser.Keyboard#clearCaptures */ clearCaptures: function () { @@ -221,8 +210,28 @@ Phaser.Keyboard.prototype = { }, + /** + * Updates all currently defined keys. + * + * @method Phaser.Keyboard#update + */ + update: function () { + + var i = this._keys.length; + + while (i--) + { + if (this._keys[i]) + { + this._keys[i].update(); + } + } + + }, + /** * Process the keydown event. + * * @method Phaser.Keyboard#processKeyDown * @param {KeyboardEvent} event * @protected @@ -234,6 +243,7 @@ Phaser.Keyboard.prototype = { return; } + // The event is being captured but another hotkey may need it if (this._capture[event.keyCode]) { event.preventDefault(); @@ -244,41 +254,18 @@ Phaser.Keyboard.prototype = { this.onDownCallback.call(this.callbackContext, event); } - if (this._keys[event.keyCode] && this._keys[event.keyCode].isDown) + if (!this._keys[event.keyCode]) { - // Key already down and still down, so update - this._keys[event.keyCode].duration = this.game.time.now - this._keys[event.keyCode].timeDown; - } - else - { - if (!this._keys[event.keyCode]) - { - // Not used this key before, so register it - this._keys[event.keyCode] = { - isDown: true, - timeDown: this.game.time.now, - timeUp: 0, - duration: 0 - }; - } - else - { - // Key used before but freshly down - this._keys[event.keyCode].isDown = true; - this._keys[event.keyCode].timeDown = this.game.time.now; - this._keys[event.keyCode].duration = 0; - } - } - - if (this._hotkeys[event.keyCode]) - { - this._hotkeys[event.keyCode].processKeyDown(event); + this._keys[event.keyCode] = new Phaser.Key(this.game, event.keyCode); } + + this._keys[event.keyCode].processKeyDown(event); }, /** * Process the keyup event. + * * @method Phaser.Keyboard#processKeyUp * @param {KeyboardEvent} event * @protected @@ -300,44 +287,37 @@ Phaser.Keyboard.prototype = { this.onUpCallback.call(this.callbackContext, event); } - if (this._hotkeys[event.keyCode]) + if (!this._keys[event.keyCode]) { - this._hotkeys[event.keyCode].processKeyUp(event); + this._keys[event.keyCode] = new Phaser.Key(this.game, event.keyCode); } - if (this._keys[event.keyCode]) - { - this._keys[event.keyCode].isDown = false; - this._keys[event.keyCode].timeUp = this.game.time.now; - } - else - { - // Not used this key before, so register it - this._keys[event.keyCode] = { - isDown: false, - timeDown: this.game.time.now, - timeUp: this.game.time.now, - duration: 0 - }; - } + this._keys[event.keyCode].processKeyUp(event); }, /** - * Reset the "isDown" state of all keys. + * Resets all Keys. + * * @method Phaser.Keyboard#reset */ reset: function () { - for (var key in this._keys) + var i = this._keys.length; + + while (i--) { - this._keys[key].isDown = false; + if (this._keys[i]) + { + this._keys[i].reset(); + } } }, /** * Returns the "just pressed" state of the key. Just pressed is considered true if the key was pressed down within the duration given (default 250ms) + * * @method Phaser.Keyboard#justPressed * @param {number} keycode - The keycode of the key to remove, i.e. Phaser.Keyboard.UP or Phaser.Keyboard.SPACEBAR * @param {number} [duration=250] - The duration below which the key is considered as being just pressed. @@ -345,19 +325,20 @@ Phaser.Keyboard.prototype = { */ justPressed: function (keycode, duration) { - if (typeof duration === "undefined") { duration = 250; } - - if (this._keys[keycode] && this._keys[keycode].isDown && this._keys[keycode].duration < duration) + if (this._keys[keycode]) { - return true; + return this._keys[keycode].justPressed(duration); + } + else + { + return false; } - - return false; }, /** * Returns the "just released" state of the Key. Just released is considered as being true if the key was released within the duration given (default 250ms) + * * @method Phaser.Keyboard#justReleased * @param {number} keycode - The keycode of the key to remove, i.e. Phaser.Keyboard.UP or Phaser.Keyboard.SPACEBAR * @param {number} [duration=250] - The duration below which the key is considered as being just released. @@ -365,19 +346,20 @@ Phaser.Keyboard.prototype = { */ justReleased: function (keycode, duration) { - if (typeof duration === "undefined") { duration = 250; } - - if (this._keys[keycode] && this._keys[keycode].isDown === false && (this.game.time.now - this._keys[keycode].timeUp < duration)) + if (this._keys[keycode]) { - return true; + return this._keys[keycode].justReleased(duration); + } + else + { + return false; } - - return false; }, /** * Returns true of the key is currently pressed down. Note that it can only detect key presses on the web browser. + * * @method Phaser.Keyboard#isDown * @param {number} keycode - The keycode of the key to remove, i.e. Phaser.Keyboard.UP or Phaser.Keyboard.SPACEBAR * @return {boolean} True if the key is currently down. diff --git a/src/utils/Debug.js b/src/utils/Debug.js index a42a3bcd..457d9f48 100644 --- a/src/utils/Debug.js +++ b/src/utils/Debug.js @@ -324,6 +324,26 @@ Phaser.Utils.Debug.prototype = { }, + renderKey: function (key, x, y, color) { + + if (this.context === null) + { + return; + } + + color = color || 'rgb(255,255,255)'; + + this.start(x, y, color, 150); + + this.splitline('Key:', key.keyCode, 'isDown:', key.isDown); + this.splitline('justPressed:', key.justPressed(), 'justReleased:', key.justReleased()); + this.splitline('Time Down:', key.timeDown.toFixed(0), 'duration:', key.duration.toFixed(0)); + this.line('Repeats: ' + key.repeats); + + this.stop(); + + }, + /** * Render debug information about the Input object. * @method Phaser.Utils.Debug#renderInputInfo