From dd43d59cce22ba9df581964b8d50785cbd91a8cc Mon Sep 17 00:00:00 2001 From: photonstorm Date: Fri, 7 Feb 2014 18:01:58 +0000 Subject: [PATCH] InputManager.getLocalPosition(displayObject, pointer, output) will return the local coordinates of the specified displayObject and pointer. InputManager.hitTest will test for pointer hits against a Sprite/Image, its hitArea (if set) or any of its children. --- README.md | 2 + examples/wip/hitArea.js | 6 +- src/input/Input.js | 645 +++++++++++++++++--------------------- src/input/InputHandler.js | 2 +- 4 files changed, 294 insertions(+), 361 deletions(-) diff --git a/README.md b/README.md index dc3fd26af..6092877bd 100644 --- a/README.md +++ b/README.md @@ -76,6 +76,8 @@ New features: * Phaser.Image is a brand new display object perfect for logos, backgrounds, etc. You can scale, rotate, tint and blend and Image, but it has no animation, physics body or input events. * You can now use the hitArea property on Sprites and Image objects. hitArea can be a geometry object (Rectangle, Circle, Polygon, Ellipse) and is used in pointerOver checks. +* InputManager.getLocalPosition(displayObject, pointer, output) will return the local coordinates of the specified displayObject and pointer. +* InputManager.hitTest will test for pointer hits against a Sprite/Image, its hitArea (if set) or any of its children. New Examples: diff --git a/examples/wip/hitArea.js b/examples/wip/hitArea.js index f14c28a77..a5f6a1adf 100644 --- a/examples/wip/hitArea.js +++ b/examples/wip/hitArea.js @@ -71,11 +71,11 @@ function update() { function render() { // var p = game.input.getLocalPosition(image); - var p = game.input.getLocalPosition(image2); + // var p = game.input.getLocalPosition(image2); - game.debug.renderPointInfo(p, 32, 32); - game.debug.renderPoint(p); + // game.debug.renderPointInfo(p, 32, 32); + // game.debug.renderPoint(p); game.debug.renderCircle(image2.hitArea); } diff --git a/src/input/Input.js b/src/input/Input.js index 131de73f4..c9b7dbc11 100644 --- a/src/input/Input.js +++ b/src/input/Input.js @@ -40,6 +40,257 @@ Phaser.Input = function (game) { * @property {object} moveCallbackContext - The context in which the moveCallback will be sent. Defaults to Phaser.Input but can be set to any valid JS object. */ this.moveCallbackContext = this; + + /** + * @property {number} pollRate - How often should the input pointers be checked for updates? A value of 0 means every single frame (60fps); a value of 1 means every other frame (30fps) and so on. + * @default + */ + this.pollRate = 0; + + /** + * @property {number} _pollCounter - Internal var holding the current poll counter. + * @private + */ + this._pollCounter = 0; + + /** + * @property {Phaser.Point} _oldPosition - A point object representing the previous position of the Pointer. + * @private + */ + this._oldPosition = null; + + /** + * @property {number} _x - x coordinate of the most recent Pointer event + * @private + */ + this._x = 0; + + /** + * @property {number} _y - Y coordinate of the most recent Pointer event + * @private + */ + this._y = 0; + + /** + * You can disable all Input by setting Input.disabled = true. While set all new input related events will be ignored. + * If you need to disable just one type of input; for example mouse; use Input.mouse.disabled = true instead + * @property {boolean} disabled + * @default + */ + this.disabled = false; + + /** + * @property {number} multiInputOverride - Controls the expected behaviour when using a mouse and touch together on a multi-input device. + * @default + */ + this.multiInputOverride = Phaser.Input.MOUSE_TOUCH_COMBINE; + + /** + * @property {Phaser.Point} position - A point object representing the current position of the Pointer. + * @default + */ + this.position = null; + + /** + * @property {Phaser.Point} speed - A point object representing the speed of the Pointer. Only really useful in single Pointer games; otherwise see the Pointer objects directly. + */ + this.speed = null; + + /** + * A Circle object centered on the x/y screen coordinates of the Input. + * Default size of 44px (Apples recommended "finger tip" size) but can be changed to anything. + * @property {Phaser.Circle} circle + */ + this.circle = null; + + /** + * @property {Phaser.Point} scale - The scale by which all input coordinates are multiplied; calculated by the StageScaleMode. In an un-scaled game the values will be x = 1 and y = 1. + */ + this.scale = null; + + /** + * @property {number} maxPointers - The maximum number of Pointers allowed to be active at any one time. For lots of games it's useful to set this to 1. + * @default + */ + this.maxPointers = 10; + + /** + * @property {number} currentPointers - The current number of active Pointers. + * @default + */ + this.currentPointers = 0; + + /** + * @property {number} tapRate - The number of milliseconds that the Pointer has to be pressed down and then released to be considered a tap or click. + * @default + */ + this.tapRate = 200; + + /** + * @property {number} doubleTapRate - The number of milliseconds between taps of the same Pointer for it to be considered a double tap / click. + * @default + */ + this.doubleTapRate = 300; + + /** + * @property {number} holdRate - The number of milliseconds that the Pointer has to be pressed down for it to fire a onHold event. + * @default + */ + this.holdRate = 2000; + + /** + * @property {number} justPressedRate - The number of milliseconds below which the Pointer is considered justPressed. + * @default + */ + this.justPressedRate = 200; + + /** + * @property {number} justReleasedRate - The number of milliseconds below which the Pointer is considered justReleased . + * @default + */ + this.justReleasedRate = 200; + + /** + * Sets if the Pointer objects should record a history of x/y coordinates they have passed through. + * The history is cleared each time the Pointer is pressed down. + * The history is updated at the rate specified in Input.pollRate + * @property {boolean} recordPointerHistory + * @default + */ + this.recordPointerHistory = false; + + /** + * @property {number} recordRate - The rate in milliseconds at which the Pointer objects should update their tracking history. + * @default + */ + this.recordRate = 100; + + /** + * The total number of entries that can be recorded into the Pointer objects tracking history. + * If the Pointer is tracking one event every 100ms; then a trackLimit of 100 would store the last 10 seconds worth of history. + * @property {number} recordLimit + * @default + */ + this.recordLimit = 100; + + /** + * @property {Phaser.Pointer} pointer1 - A Pointer object. + */ + this.pointer1 = null; + + /** + * @property {Phaser.Pointer} pointer2 - A Pointer object. + */ + this.pointer2 = null; + + /** + * @property {Phaser.Pointer} pointer3 - A Pointer object. + */ + this.pointer3 = null; + + /** + * @property {Phaser.Pointer} pointer4 - A Pointer object. + */ + this.pointer4 = null; + + /** + * @property {Phaser.Pointer} pointer5 - A Pointer object. + */ + this.pointer5 = null; + + /** + * @property {Phaser.Pointer} pointer6 - A Pointer object. + */ + this.pointer6 = null; + + /** + * @property {Phaser.Pointer} pointer7 - A Pointer object. + */ + this.pointer7 = null; + + /** + * @property {Phaser.Pointer} pointer8 - A Pointer object. + */ + this.pointer8 = null; + + /** + * @property {Phaser.Pointer} pointer9 - A Pointer object. + */ + this.pointer9 = null; + + /** + * @property {Phaser.Pointer} pointer10 - A Pointer object. + */ + this.pointer10 = null; + + /** + * The most recently active Pointer object. + * When you've limited max pointers to 1 this will accurately be either the first finger touched or mouse. + * @property {Phaser.Pointer} activePointer + */ + this.activePointer = null; + + /** + * @property {Pointer} mousePointer - The mouse has its own unique Phaser.Pointer object which you can use if making a desktop specific game. + */ + this.mousePointer = null; + + /** + * @property {Phaser.Mouse} mouse - The Mouse Input manager. + */ + this.mouse = null; + + /** + * @property {Phaser.Keyboard} keyboard - The Keyboard Input manager. + */ + this.keyboard = null; + + /** + * @property {Phaser.Touch} touch - the Touch Input manager. + */ + this.touch = null; + + /** + * @property {Phaser.MSPointer} mspointer - The MSPointer Input manager. + */ + this.mspointer = null; + + /** + * @property {Phaser.Gamepad} gamepad - The Gamepad Input manager. + */ + this.gamepad = null; + + /** + * @property {Phaser.Signal} onDown - A Signal that is dispatched each time a pointer is pressed down. + */ + this.onDown = null; + + /** + * @property {Phaser.Signal} onUp - A Signal that is dispatched each time a pointer is released. + */ + this.onUp = null; + + /** + * @property {Phaser.Signal} onTap - A Signal that is dispatched each time a pointer is tapped. + */ + this.onTap = null; + + /** + * @property {Phaser.Signal} onHold - A Signal that is dispatched each time a pointer is held down. + */ + this.onHold = null; + + /** + * A linked list of interactive objects; the InputHandler components (belonging to Sprites) register themselves with this. + * @property {Phaser.LinkedList} interactiveItems + */ + this.interactiveItems = new Phaser.LinkedList(); + + /** + * @property {Phaser.Point} _localPoint - Internal cache var. + * @private + */ + this._localPoint = new Phaser.Point(); }; @@ -63,300 +314,6 @@ Phaser.Input.MOUSE_TOUCH_COMBINE = 2; Phaser.Input.prototype = { - /** - * How often should the input pointers be checked for updates? - * A value of 0 means every single frame (60fps), a value of 1 means every other frame (30fps) and so on. - * @property {number} pollRate - * @default - */ - pollRate: 0, - - /** - * @property {number} _pollCounter - Internal var holding the current poll counter. - * @private - * @default - */ - _pollCounter: 0, - - /** - * @property {Phaser.Point} _oldPosition - A point object representing the previous position of the Pointer. - * @private - * @default - */ - _oldPosition: null, - - /** - * @property {number} _x - x coordinate of the most recent Pointer event - * @private - * @default - */ - _x: 0, - - /** - * @property {number} _y - Y coordinate of the most recent Pointer event - * @private - * @default - */ - _y: 0, - - /** - * You can disable all Input by setting Input.disabled: true. While set all new input related events will be ignored. - * If you need to disable just one type of input, for example mouse, use Input.mouse.disabled: true instead - * @property {boolean} disabled - * @default - */ - disabled: false, - - /** - * Controls the expected behaviour when using a mouse and touch together on a multi-input device. - * @property {Description} multiInputOverride - */ - multiInputOverride: Phaser.Input.MOUSE_TOUCH_COMBINE, - - /** - * @property {Phaser.Point} position - A point object representing the current position of the Pointer. - * @default - */ - position: null, - - /** - * A point object representing the speed of the Pointer. Only really useful in single Pointer games, otherwise see the Pointer objects directly. - * @property {Phaser.Point} speed - */ - speed: null, - - /** - * A Circle object centered on the x/y screen coordinates of the Input. - * Default size of 44px (Apples recommended "finger tip" size) but can be changed to anything. - * @property {Phaser.Circle} circle - */ - circle: null, - - /** - * The scale by which all input coordinates are multiplied, calculated by the StageScaleMode. - * In an un-scaled game the values will be x: 1 and y: 1. - * @property {Phaser.Point} scale - */ - scale: null, - - /** - * The maximum number of Pointers allowed to be active at any one time. - * For lots of games it's useful to set this to 1. - * @property {number} maxPointers - * @default - */ - maxPointers: 10, - - /** - * The current number of active Pointers. - * @property {number} currentPointers - * @default - */ - currentPointers: 0, - - /** - * The number of milliseconds that the Pointer has to be pressed down and then released to be considered a tap or clicke - * @property {number} tapRate - * @default - */ - tapRate: 200, - - /** - * The number of milliseconds between taps of the same Pointer for it to be considered a double tap / click - * @property {number} doubleTapRate - * @default - */ - doubleTapRate: 300, - - /** - * The number of milliseconds that the Pointer has to be pressed down for it to fire a onHold event - * @property {number} holdRate - * @default - */ - holdRate: 2000, - - /** - * The number of milliseconds below which the Pointer is considered justPressed - * @property {number} justPressedRate - * @default - */ - justPressedRate: 200, - - /** - * The number of milliseconds below which the Pointer is considered justReleased - * @property {number} justReleasedRate - * @default - */ - justReleasedRate: 200, - - /** - * Sets if the Pointer objects should record a history of x/y coordinates they have passed through. - * The history is cleared each time the Pointer is pressed down. - * The history is updated at the rate specified in Input.pollRate - * @property {boolean} recordPointerHistory - * @default - */ - recordPointerHistory: false, - - /** - * The rate in milliseconds at which the Pointer objects should update their tracking history - * @property {number} recordRate - * @default - */ - recordRate: 100, - - /** - * The total number of entries that can be recorded into the Pointer objects tracking history. - * If the Pointer is tracking one event every 100ms, then a trackLimit of 100 would store the last 10 seconds worth of history. - * @property {number} recordLimit - * @default - */ - recordLimit: 100, - - /** - * A Pointer object - * @property {Phaser.Pointer} pointer1 - */ - pointer1: null, - - /** - * A Pointer object - * @property {Phaser.Pointer} pointer2 - */ - pointer2: null, - - /** - * A Pointer object - * @property {Phaser.Pointer} pointer3 - */ - pointer3: null, - - /** - * A Pointer object - * @property {Phaser.Pointer} pointer4 - */ - pointer4: null, - - /** - * A Pointer object - * @property {Phaser.Pointer} pointer5 - */ - pointer5: null, - - /** - * A Pointer object - * @property {Phaser.Pointer} pointer6 - */ - pointer6: null, - - /** - * A Pointer object - * @property {Phaser.Pointer} pointer7 - */ - pointer7: null, - - /** - * A Pointer object - * @property {Phaser.Pointer} pointer8 - */ - pointer8: null, - - /** - * A Pointer object - * @property {Phaser.Pointer} pointer9 - */ - pointer9: null, - - /** - * A Pointer object. - * @property {Phaser.Pointer} pointer10 - */ - pointer10: null, - - /** - * The most recently active Pointer object. - * When you've limited max pointers to 1 this will accurately be either the first finger touched or mouse. - * @property {Phaser.Pointer} activePointer - * @default - */ - activePointer: null, - - /** - * The mouse has its own unique Phaser.Pointer object which you can use if making a desktop specific game. - * @property {Pointer} mousePointer - * @default - */ - mousePointer: null, - - /** - * The Mouse Input manager. - * @property {Phaser.Mouse} mouse - The Mouse Input manager. - * @default - */ - mouse: null, - - /** - * The Keyboard Input manager. - * @property {Phaser.Keyboard} keyboard - The Keyboard Input manager. - * @default - */ - keyboard: null, - - /** - * The Touch Input manager. - * @property {Phaser.Touch} touch - the Touch Input manager. - * @default - */ - touch: null, - - /** - * The MSPointer Input manager. - * @property {Phaser.MSPointer} mspointer - The MSPointer Input manager. - * @default - */ - mspointer: null, - - /** - * The Gamepad Input manager. - * @property {Phaser.Gamepad} gamepad - The Gamepad Input manager. - * @default - */ - gamepad: null, - - /** - * A Signal that is dispatched each time a pointer is pressed down. - * @property {Phaser.Signal} onDown - * @default - */ - onDown: null, - - /** - * A Signal that is dispatched each time a pointer is released. - * @property {Phaser.Signal} onUp - * @default - */ - onUp: null, - - /** - * A Signal that is dispatched each time a pointer is tapped. - * @property {Phaser.Signal} onTap - * @default - */ - onTap: null, - - /** - * A Signal that is dispatched each time a pointer is held down. - * @property {Phaser.Signal} onHold - * @default - */ - onHold: null, - - /** - * A linked list of interactive objects, the InputHandler components (belonging to Sprites) register themselves with this. - * @property {Phaser.LinkedList} interactiveItems - */ - interactiveItems: new Phaser.LinkedList(), - /** * Starts the Input Manager running. * @method Phaser.Input#boot @@ -727,99 +684,73 @@ Phaser.Input.prototype = { }, /** - * This will return the local coordinates of the specified displayObject for this InteractionData - * - * @method getLocalPosition - * @param displayObject {DisplayObject} The DisplayObject that you would like the local coords off - * @return {Point} A point containing the coordinates of the InteractionData position relative to the DisplayObject - */ - getLocalPosition: function (displayObject) { + * This will return the local coordinates of the specified displayObject based on the given Pointer. + * @method Phaser.Input#getLocalPosition + * @param {Phaser.Sprite|Phaser.Image} displayObject - The DisplayObject to get the local coordinates for. + * @param {Phaser.Pointer} pointer - The Pointer to use in the check against the displayObject. + * @return {Phaser.Point} A point containing the coordinates of the Pointer position relative to the DisplayObject. + */ + getLocalPosition: function (displayObject, pointer, output) { - var worldTransform = displayObject.worldTransform; - var global = new Phaser.Point(this.x, this.y); + if (typeof output === 'undefined') { output = new Phaser.Point(); } - // do a cheeky transform to get the mouse coords; - var a00 = worldTransform.a, a01 = worldTransform.b, a02 = worldTransform.tx, - a10 = worldTransform.c, a11 = worldTransform.d, a12 = worldTransform.ty, - id = 1 / (a00 * a11 + a01 * -a10); - // set the mouse coords... - return new Phaser.Point(a11 * id * global.x + -a01 * id * global.y + (a12 * a01 - a02 * a11) * id, - a00 * id * global.y + -a10 * id * global.x + (-a12 * a00 + a02 * a10) * id); + var wt = displayObject.worldTransform; + var id = 1 / (wt.a * wt.d + wt.b * -wt.c); + + return output.setTo( + wt.d * id * pointer.x + -wt.b * id * pointer.y + (wt.ty * wt.b - wt.tx * wt.d) * id, + wt.a * id * pointer.y + -wt.c * id * pointer.x + (-wt.ty * wt.a + wt.tx * wt.c) * id + ); + }, /** * Tests if the current mouse coordinates hit a sprite * * @method hitTest - * @param item {DisplayObject} The displayObject to test for a hit - * @param interactionData {InteractionData} The interactionData object to update in the case there is a hit - * @private + * @param displayObject {DisplayObject} The displayObject to test for a hit */ - // hitTest: function (item, interactionData) { - hitTest: function (item, pointer) { + hitTest: function (displayObject, pointer, localPoint) { - // var global = interactionData.global; - var global = new Phaser.Point(pointer.x, pointer.y); + if (!displayObject.worldVisible) + { + return false; + } - if( !item.worldVisible )return false; + this.getLocalPosition(displayObject, pointer, this._localPoint); - // temp fix for if the element is in a non visible - - var isSprite = (item instanceof PIXI.Sprite), - worldTransform = item.worldTransform, - a00 = worldTransform.a, a01 = worldTransform.b, a02 = worldTransform.tx, - a10 = worldTransform.c, a11 = worldTransform.d, a12 = worldTransform.ty, - id = 1 / (a00 * a11 + a01 * -a10), - x = a11 * id * global.x + -a01 * id * global.y + (a12 * a01 - a02 * a11) * id, - y = a00 * id * global.y + -a10 * id * global.x + (-a12 * a00 + a02 * a10) * id; - - // interactionData.target = item; - - //a sprite or display object with a hit area defined - if(item.hitArea && item.hitArea.contains) { - if(item.hitArea.contains(x, y)) { - console.log('AREA HIT!', x, y); - //if(isSprite) - // interactionData.target = item; + localPoint.copyFrom(this._localPoint); + if (displayObject.hitArea && displayObject.hitArea.contains) + { + if (displayObject.hitArea.contains(this._localPoint.x, this._localPoint.y)) + { return true; } return false; } - // a sprite with no hitarea defined - else if(isSprite) + else if (displayObject instanceof PIXI.Sprite) { - var width = item.texture.frame.width, - height = item.texture.frame.height, - x1 = -width * item.anchor.x, - y1; + var width = displayObject.texture.frame.width; + var height = displayObject.texture.frame.height; + var x1 = -width * displayObject.anchor.x; - if(x > x1 && x < x1 + width) + if (this._localPoint.x > x1 && this._localPoint.x < x1 + width) { - y1 = -height * item.anchor.y; + var y1 = -height * displayObject.anchor.y; - if(y > y1 && y < y1 + height) + if (this._localPoint.y > y1 && this._localPoint.y < y1 + height) { - // set the target property if a hit is true! - // interactionData.target = item; - console.log('HIT!', x, y, x1, y1); return true; } } } - var length = item.children.length; - - for (var i = 0; i < length; i++) + for (var i = 0, len = displayObject.children.length; i < len; i++) { - var tempItem = item.children[i]; - // var hit = this.hitTest(tempItem, interactionData); - var hit = this.hitTest(tempItem); - if(hit) + if (this.hitTest(displayObject.children[i], pointer, localPoint)) { - // hmm.. TODO SET CORRECT TARGET? - // interactionData.target = item; return true; } } diff --git a/src/input/InputHandler.js b/src/input/InputHandler.js index e39514d43..6f89756b0 100644 --- a/src/input/InputHandler.js +++ b/src/input/InputHandler.js @@ -498,7 +498,7 @@ Phaser.InputHandler.prototype = { } // Need to pass it a temp point, in case we need it again for the pixel check - if (this.game.input.hitTest(this.sprite, pointer)) + if (this.game.input.hitTest(this.sprite, pointer, this._tempPoint)) { return true; }