diff --git a/README.md b/README.md index e115e584..dc3fd26a 100644 --- a/README.md +++ b/README.md @@ -75,6 +75,8 @@ Significant API changes: 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. + New Examples: @@ -85,6 +87,7 @@ Updates: * Phaser.AnimationParser now sets the trimmed data directly for Pixi Texture frames. Tested across JSON Hash, JSON Data, Sprite Sheet and XML. * Game.add.renderTexture now has the addToCache parameter. If set the texture will be stored in Game.Cache and can be retrieved with Cache.getTexture(key). * Game.add.bitmapData now has the addToCache parameter. If set the texture will be stored in Game.Cache and can be retrieved with Cache.getBitmapData(key). +* The InputManager now sets the canvas style cursor to 'inherit' instead of 'default'. Bug Fixes: diff --git a/examples/wip/grid-drag.js b/examples/wip/grid-drag.js new file mode 100644 index 00000000..3be98cb4 --- /dev/null +++ b/examples/wip/grid-drag.js @@ -0,0 +1,86 @@ + +var game = new Phaser.Game(800, 600, Phaser.CANVAS, 'phaser-example', { preload: preload, create: create, update: update, render: render }); + +function preload() { + + game.load.image('block', 'assets/sprites/block.png'); + +} + +var grid = []; +var currentTile = new Phaser.Point(); + +function create() { + + // The block.png is 95x95, so for this we'll create a little grid or it won't fit: + + for (var y = 0; y < 5; y++) + { + grid[y] = []; + + for (var x = 0; x < 5; x++) + { + // grid[y][x] = game.add.sprite(x * 95, y * 95, 'block'); + // coz the grid is empty like + grid[y][x] = null; + } + } + + var block1 = game.add.sprite(600, 100, 'block'); + block1.name = 'block1'; + block1.inputEnabled = true; + block1.input.enableDrag(true); + block1.events.onDragStop.add(dropBlock, this); + + var block2 = game.add.sprite(600, 300, 'block'); + block2.name = 'block2'; + block2.inputEnabled = true; + block2.input.enableDrag(true); + block2.events.onDragStop.add(dropBlock, this); + +} + +function dropBlock(sprite, pointer) { + + // Convert the pointer into a grid location + var x = this.game.math.snapToFloor(pointer.x, 95) / 95; + var y = this.game.math.snapToFloor(pointer.y, 95) / 95; + + // Bounds check it + if (x >= 0 && x <= 4 && y >= 0 && y <= 4) + { + // something in there already? + if (grid[y][x] !== null) + { + // This is very hacky - what you SHOULD do is have a Pipe object which has properties startX and startY or something, and snap back to those. + if (sprite.name === 'block1') + { + game.add.tween(sprite).to( { x: 600, y: 100 }, 1000, Phaser.Easing.Linear.None, true); + } + else + { + game.add.tween(sprite).to( { x: 600, y: 300 }, 1000, Phaser.Easing.Linear.None, true); + } + } + else + { + grid[y][x] = sprite; + sprite.inputEnabled = false; + } + } + +} + +function update() { + + // 95 = width and height of the block.png + currentTile.x = this.game.math.snapToFloor(game.input.x, 95) / 95; + currentTile.y = this.game.math.snapToFloor(game.input.y, 95) / 95; + +} + +function render() { + + game.debug.renderText('Tile X: ' + currentTile.x + ' Y: ' + currentTile.y, 32, 32); + +} \ No newline at end of file diff --git a/examples/wip/grid.js b/examples/wip/grid.js new file mode 100644 index 00000000..2faf5364 --- /dev/null +++ b/examples/wip/grid.js @@ -0,0 +1,54 @@ + +var game = new Phaser.Game(800, 600, Phaser.CANVAS, 'phaser-example', { preload: preload, create: create, update: update, render: render }); + +function preload() { + + game.load.image('block', 'assets/sprites/block.png'); + +} + +var grid = []; +var currentTile = new Phaser.Point(); + +function create() { + + // The block.png is 95x95, so for this we'll create a little grid or it won't fit: + + for (var y = 0; y < 5; y++) + { + grid[y] = []; + + for (var x = 0; x < 5; x++) + { + grid[y][x] = game.add.sprite(x * 95, y * 95, 'block'); + } + } + + game.input.onDown.add(clickedBlock, this); + +} + +function clickedBlock() { + + // Bounds check + if (currentTile.x >= 0 && currentTile.x <= 4 && currentTile.y >= 0 && currentTile.y <= 4) + { + block = grid[currentTile.y][currentTile.x]; + block.alpha = 0.5; + } + +} + +function update() { + + // 95 = width and height of the block.png + currentTile.x = this.game.math.snapToFloor(game.input.x, 95) / 95; + currentTile.y = this.game.math.snapToFloor(game.input.y, 95) / 95; + +} + +function render() { + + game.debug.renderText('Tile X: ' + currentTile.x + ' Y: ' + currentTile.y, 32, 32); + +} \ No newline at end of file diff --git a/examples/wip/hitArea.js b/examples/wip/hitArea.js new file mode 100644 index 00000000..f14c28a7 --- /dev/null +++ b/examples/wip/hitArea.js @@ -0,0 +1,81 @@ + +var game = new Phaser.Game(800, 600, Phaser.CANVAS, 'phaser-example', { preload: preload, create: create, update: update, render: render }); + +function preload() { + + game.load.image('mushroom', 'assets/sprites/mushroom2.png'); + game.load.image('ball', 'assets/sprites/wizball.png'); + +} + +var image; +var image2; + +var down; +var p; +var c; + +function create() { + + image = game.add.image(200, 200, 'mushroom'); + + image2 = game.add.image(400, 200, 'ball'); + + // image.rotation = 0.8; + + image.inputEnabled = true; + + image.events.onInputDown.add(clicked, this); + image.events.onInputOver.add(over, this); + image.events.onInputOut.add(out, this); + + + + image2.hitArea = new Phaser.Circle(image2.width / 2, image2.height / 2, 90); + image2.inputEnabled = true; + + image2.events.onInputDown.add(clicked, this); + image2.events.onInputOver.add(over, this); + image2.events.onInputOut.add(out, this); + + + // game.input.mouse.mouseDownCallback = onMouseDown; + // game.input.mouse.mouseUpCallback = onMouseUp; + // game.input.mouse.mouseMoveCallback = onMouseMove; + +} + +function over(object, pointer) { + + object.alpha = 0.5; + +} + +function out(object, pointer) { + + object.alpha = 1; + +} + +function clicked(object, pointer) { + + console.log('boom'); + +} + +function update() { + + +} + +function render() { + + // var p = game.input.getLocalPosition(image); + var p = game.input.getLocalPosition(image2); + + + game.debug.renderPointInfo(p, 32, 32); + game.debug.renderPoint(p); + game.debug.renderCircle(image2.hitArea); + +} diff --git a/src/core/Game.js b/src/core/Game.js index 9d841e4b..c7ddb647 100644 --- a/src/core/Game.js +++ b/src/core/Game.js @@ -457,6 +457,7 @@ Phaser.Game.prototype = { this.world.boot(); this.input.boot(); this.sound.boot(); + this.state.boot(); this.load.onLoadComplete.add(this.loadComplete, this); @@ -467,8 +468,6 @@ Phaser.Game.prototype = { this.raf = new Phaser.RequestAnimationFrame(this); this.raf.start(); - - this.state.boot(); } }, diff --git a/src/core/World.js b/src/core/World.js index 8a341a02..601ec6f6 100644 --- a/src/core/World.js +++ b/src/core/World.js @@ -70,6 +70,8 @@ Phaser.World.prototype.boot = function () { */ Phaser.World.prototype.preUpdate = function () { + this.currentRenderOrderID = 0; + for (var i = 0, len = this.children.length; i < len; i++) { if (this.children[i]['preUpdate']) diff --git a/src/gameobjects/GameObjectFactory.js b/src/gameobjects/GameObjectFactory.js index 16f06485..9a721045 100644 --- a/src/gameobjects/GameObjectFactory.js +++ b/src/gameobjects/GameObjectFactory.js @@ -40,7 +40,8 @@ Phaser.GameObjectFactory.prototype = { }, /** - * Create a new Image at the given coordinates, using the cache key and frame if set. + * Create a new `Image` object. An Image is a light-weight object you can use to display anything that doesn't need physics or animation. + * It can still rotate, scale, crop and receive input events. This makes it perfect for logos, backgrounds, simple buttons and other non-Sprite graphics. * * @method Phaser.GameObjectFactory#image * @param {number} x - X position of the image. diff --git a/src/gameobjects/Image.js b/src/gameobjects/Image.js index 64a9dfa0..bae88878 100644 --- a/src/gameobjects/Image.js +++ b/src/gameobjects/Image.js @@ -7,8 +7,8 @@ /** * @class Phaser.Image * -* @classdesc Create a new `Image` object. An Image is a light-weight object you can use to display anything that doesn't need physics, animation or input events. -* It can still rotate, scale and crop. This makes it perfect for logos, backgrounds and other non-Sprite graphics. +* @classdesc Create a new `Image` object. An Image is a light-weight object you can use to display anything that doesn't need physics or animation. +* It can still rotate, scale, crop and receive input events. This makes it perfect for logos, backgrounds, simple buttons and other non-Sprite graphics. * * @constructor * @param {Phaser.Game} game - A reference to the currently running game. @@ -90,10 +90,15 @@ Phaser.Image = function (game, x, y, key, frame) { this.fixedToCamera = false; /** - * @property {array} _cache - A small cache for previous step values. + * @property {Phaser.InputHandler|null} input - The Input Handler for this object. Needs to be enabled with image.inputEnabled = true before you can use it. + */ + this.input = null; + + /** + * @property {array} _cache - A small cache for previous step values. 0 = x, 1 = y, 2 = rotation, 3 = renderID * @private */ - this._cache = [0, 0, 0]; + this._cache = [0, 0, 0, 0]; }; @@ -125,6 +130,11 @@ Phaser.Image.prototype.preUpdate = function() { this.world.setTo(this.game.camera.x + this.worldTransform[2], this.game.camera.y + this.worldTransform[5]); + if (this.visible) + { + this._cache[3] = this.game.world.currentRenderOrderID++; + } + return true; }; @@ -339,6 +349,11 @@ Phaser.Image.prototype.destroy = function() { this.events.destroy(); } + if (this.input) + { + this.input.destroy(); + } + this.alive = false; this.exists = false; this.visible = false; @@ -566,3 +581,53 @@ Object.defineProperty(Phaser.Image.prototype, "frameName", { }); +/** +* @name Phaser.Image#renderOrderID +* @property {number} renderOrderID - The render order ID, reset every frame. +* @readonly +*/ +Object.defineProperty(Phaser.Image.prototype, "renderOrderID", { + + get: function() { + + return this._cache[3]; + + } + +}); + +/** +* By default an Image won't process any input events at all. By setting inputEnabled to true the Phaser.InputHandler is +* activated for this object and it will then start to process click/touch events and more. +* +* @name Phaser.Image#inputEnabled +* @property {boolean} inputEnabled - Set to true to allow this object to receive input events. +*/ +Object.defineProperty(Phaser.Image.prototype, "inputEnabled", { + + get: function () { + + return (this.input && this.input.enabled); + + }, + + set: function (value) { + + if (value) + { + if (this.input === null) + { + this.input = new Phaser.InputHandler(this); + this.input.start(); + } + } + else + { + if (this.input && this.input.enabled) + { + this.input.stop(); + } + } + } + +}); diff --git a/src/geom/Circle.js b/src/geom/Circle.js index d8a390b3..7e21cdd6 100644 --- a/src/geom/Circle.js +++ b/src/geom/Circle.js @@ -384,21 +384,18 @@ Object.defineProperty(Phaser.Circle.prototype, "empty", { */ Phaser.Circle.contains = function (a, x, y) { - if (a.radius <= 0) - { - return false; - } - // Check if x/y are within the bounds first - if (x >= a.left && x <= a.right && y >= a.top && y <= a.bottom) + if (a.radius > 0 && x >= a.left && x <= a.right && y >= a.top && y <= a.bottom) { var dx = (a.x - x) * (a.x - x); var dy = (a.y - y) * (a.y - y); return (dx + dy) <= (a.radius * a.radius); } - - return false; + else + { + return false; + } }; diff --git a/src/input/Input.js b/src/input/Input.js index f25e5ab0..131de73f 100644 --- a/src/input/Input.js +++ b/src/input/Input.js @@ -530,7 +530,7 @@ Phaser.Input.prototype = { if (this.game.canvas.style.cursor !== 'none') { - this.game.canvas.style.cursor = 'default'; + this.game.canvas.style.cursor = 'inherit'; } if (hard === true) @@ -724,6 +724,107 @@ Phaser.Input.prototype = { return null; + }, + + /** + * 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) { + + var worldTransform = displayObject.worldTransform; + var global = new Phaser.Point(this.x, this.y); + + // 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); + }, + + /** + * 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 + */ + // hitTest: function (item, interactionData) { + hitTest: function (item, pointer) { + + // var global = interactionData.global; + var global = new Phaser.Point(pointer.x, pointer.y); + + if( !item.worldVisible )return false; + + // 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; + + return true; + } + + return false; + } + // a sprite with no hitarea defined + else if(isSprite) + { + var width = item.texture.frame.width, + height = item.texture.frame.height, + x1 = -width * item.anchor.x, + y1; + + if(x > x1 && x < x1 + width) + { + y1 = -height * item.anchor.y; + + if(y > y1 && 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++) + { + var tempItem = item.children[i]; + // var hit = this.hitTest(tempItem, interactionData); + var hit = this.hitTest(tempItem); + if(hit) + { + // hmm.. TODO SET CORRECT TARGET? + // interactionData.target = item; + return true; + } + } + + return false; } }; diff --git a/src/input/InputHandler.js b/src/input/InputHandler.js index 658735f0..e39514d4 100644 --- a/src/input/InputHandler.js +++ b/src/input/InputHandler.js @@ -215,7 +215,7 @@ Phaser.InputHandler.prototype = { this.enabled = true; // Create the signals the Input component will emit - if (this.sprite.events && this.sprite.events.onInputOver == null) + if (this.sprite.events && this.sprite.events.onInputOver === null) { this.sprite.events.onInputOver = new Phaser.Signal(); this.sprite.events.onInputOut = new Phaser.Signal(); @@ -492,11 +492,18 @@ Phaser.InputHandler.prototype = { */ checkPointerOver: function (pointer) { - if (this.enabled === false || this.sprite.visible === false || (this.sprite.group && this.sprite.group.visible === false)) + if (this.enabled === false || this.sprite.visible === false || this.sprite.parent.visible === false) { return false; } + // 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)) + { + return true; + } + + /* this.sprite.getLocalUnmodifiedPosition(this._tempPoint, pointer.x, pointer.y); if (this._tempPoint.x >= 0 && this._tempPoint.x <= this.sprite.currentFrame.width && this._tempPoint.y >= 0 && this._tempPoint.y <= this.sprite.currentFrame.height) @@ -510,6 +517,9 @@ Phaser.InputHandler.prototype = { return true; } } + */ + + return false; },