diff --git a/examples/a_template.php b/examples/a_template.php index 9b4ec4c31..69811ff73 100644 --- a/examples/a_template.php +++ b/examples/a_template.php @@ -22,10 +22,13 @@ function create() { - // var tempSprite = game.add.sprite(game.world.randomX, game.world.randomY, game.rnd.pick(images)); + // var tempSprite = game.add.sprite(game.world.randomX, game.world.randomY, 'atari1'); } + function update() { + } + function render() { } diff --git a/examples/camerafollow.php b/examples/camerafollow.php new file mode 100644 index 000000000..97e4dcc5c --- /dev/null +++ b/examples/camerafollow.php @@ -0,0 +1,79 @@ + + + + phaser.js - a new beginning + + + + + + + + \ No newline at end of file diff --git a/examples/pixelpick.php b/examples/pixelpick.php new file mode 100644 index 000000000..4d37e8f26 --- /dev/null +++ b/examples/pixelpick.php @@ -0,0 +1,66 @@ + + + + phaser.js - a new beginning + + + + + + + + \ No newline at end of file diff --git a/examples/pixelpick2.php b/examples/pixelpick2.php new file mode 100644 index 000000000..697b5c50b --- /dev/null +++ b/examples/pixelpick2.php @@ -0,0 +1,69 @@ + + + + phaser.js - a new beginning + + + + + + + + \ No newline at end of file diff --git a/examples/pixelpick3.php b/examples/pixelpick3.php new file mode 100644 index 000000000..000acd7ce --- /dev/null +++ b/examples/pixelpick3.php @@ -0,0 +1,118 @@ + + + + phaser.js - a new beginning + + + + + + + + \ No newline at end of file diff --git a/examples/world.php b/examples/world.php new file mode 100644 index 000000000..1061a3a89 --- /dev/null +++ b/examples/world.php @@ -0,0 +1,106 @@ + + + + phaser.js - a new beginning + + + + + + + + \ No newline at end of file diff --git a/src/core/Camera.js b/src/core/Camera.js index 584c5bc31..4d0cf1c19 100644 --- a/src/core/Camera.js +++ b/src/core/Camera.js @@ -21,6 +21,10 @@ Phaser.Camera = function (game, id, x, y, width, height) { */ this.view = new Phaser.Rectangle(x, y, width, height); + /** + * Used by Sprites to work out Camera culling. + * @type {Rectangle} + */ this.screenView = new Phaser.Rectangle(x, y, width, height); /** @@ -35,11 +39,19 @@ Phaser.Camera = function (game, id, x, y, width, height) { */ this.visible = true; + /** + * Whether this camera is flush with the World Bounds or not. + * @type {bool} + */ + this.atLimit = { x: false, y: false }; + /** * If the camera is tracking a Sprite, this is a reference to it, otherwise null * @type {Sprite} */ this.target = null; + + this._edge = 0; }; @@ -66,23 +78,23 @@ Phaser.Camera.prototype = { switch (style) { - case Phaser.Types.CAMERA_FOLLOW_PLATFORMER: + case Phaser.Camera.FOLLOW_PLATFORMER: var w = this.width / 8; var h = this.height / 3; this.deadzone = new Phaser.Rectangle((this.width - w) / 2, (this.height - h) / 2 - h * 0.25, w, h); break; - case Phaser.Types.CAMERA_FOLLOW_TOPDOWN: + case Phaser.Camera.FOLLOW_TOPDOWN: helper = Math.max(this.width, this.height) / 4; this.deadzone = new Phaser.Rectangle((this.width - helper) / 2, (this.height - helper) / 2, helper, helper); break; - case Phaser.Types.CAMERA_FOLLOW_TOPDOWN_TIGHT: + case Phaser.Camera.FOLLOW_TOPDOWN_TIGHT: helper = Math.max(this.width, this.height) / 8; this.deadzone = new Phaser.Rectangle((this.width - helper) / 2, (this.height - helper) / 2, helper, helper); break; - case Phaser.Types.CAMERA_FOLLOW_LOCKON: + case Phaser.Camera.FOLLOW_LOCKON: default: this.deadzone = null; break; @@ -97,9 +109,6 @@ Phaser.Camera.prototype = { */ focusOnXY: function (x, y) { - x += (x > 0) ? 0.0000001 : -0.0000001; - y += (y > 0) ? 0.0000001 : -0.0000001; - this.view.x = Math.round(x - this.view.halfWidth); this.view.y = Math.round(y - this.view.halfHeight); @@ -110,83 +119,89 @@ Phaser.Camera.prototype = { */ update: function () { - // this.plugins.preUpdate(); - // Add dirty flag if (this.target !== null) { - if (this.deadzone == null) + if (this.deadzone) { - this.focusOnXY(this.target.x, this.target.y); + this._edge = this.target.x - this.deadzone.x; + + if (this.view.x > this._edge) + { + this.view.x = this._edge; + } + + this._edge = this.target.x + this.target.width - this.deadzone.x - this.deadzone.width; + + if (this.view.x < this._edge) + { + this.view.x = this._edge; + } + + this._edge = this.target.y - this.deadzone.y; + + if (this.view.y > this._edge) + { + this.view.y = this._edge; + } + + this._edge = this.target.y + this.target.height - this.deadzone.y - this.deadzone.height; + + if (this.view.y < this._edge) + { + this.view.y = this._edge; + } } else { - var edge; - var targetX = this.target.x + ((this.target.x > 0) ? 0.0000001 : -0.0000001); - var targetY = this.target.y + ((this.target.y > 0) ? 0.0000001 : -0.0000001); - - edge = targetX - this.deadzone.x; - - if (this.view.x > edge) - { - this.view.x = edge; - } - - edge = targetX + this.target.width - this.deadzone.x - this.deadzone.width; - - if (this.view.x < edge) - { - this.view.x = edge; - } - - edge = targetY - this.deadzone.y; - - if (this.view.y > edge) - { - this.view.y = edge; - } - - edge = targetY + this.target.height - this.deadzone.y - this.deadzone.height; - - if (this.view.y < edge) - { - this.view.y = edge; - } + this.focusOnXY(this.target.x, this.target.y); } } + this.checkWorldBounds(); + + }, + + checkWorldBounds: function () { + + this.atLimit.x = false; + this.atLimit.y = false; + // Make sure we didn't go outside the cameras worldBounds if (this.view.x < this.world.bounds.left) { + this.atLimit.x = true; this.view.x = this.world.bounds.left; } if (this.view.x > this.world.bounds.right - this.width) { + this.atLimit.x = true; this.view.x = (this.world.bounds.right - this.width) + 1; } if (this.view.y < this.world.bounds.top) { + this.atLimit.y = true; this.view.y = this.world.bounds.top; } if (this.view.y > this.world.bounds.bottom - this.height) { + this.atLimit.y = true; this.view.y = (this.world.bounds.bottom - this.height) + 1; } this.view.floor(); - // this.plugins.update(); - }, setPosition: function (x, y) { this.view.x = x; this.view.y = y; + this.checkWorldBounds(); }, @@ -207,6 +222,7 @@ Object.defineProperty(Phaser.Camera.prototype, "x", { set: function (value) { this.view.x = value; + this.checkWorldBounds(); } }); @@ -219,6 +235,7 @@ Object.defineProperty(Phaser.Camera.prototype, "y", { set: function (value) { this.view.y = value; + this.checkWorldBounds(); } }); diff --git a/src/gameobjects/Sprite.js b/src/gameobjects/Sprite.js index 0d1dce61e..07c5383fe 100644 --- a/src/gameobjects/Sprite.js +++ b/src/gameobjects/Sprite.js @@ -23,6 +23,24 @@ Phaser.Sprite = function (game, x, y, key, frame) { // The lifespan is decremented by game.time.elapsed each update, once it reaches zero the kill() function is called. this.lifespan = 0; + /** + * The Signals you can subscribe to that are dispatched when certain things happen on this Sprite or its components + * @type Events + */ + this.events = new Phaser.Events(this); + + /** + * This manages animations of the sprite. You can modify animations through it. (see AnimationManager) + * @type AnimationManager + */ + this.animations = new Phaser.AnimationManager(this); + + /** + * The Input Handler Component + * @type InputHandler + */ + this.input = new Phaser.InputHandler(this); + this.key = key; if (key instanceof Phaser.RenderTexture) @@ -62,24 +80,6 @@ Phaser.Sprite = function (game, x, y, key, frame) { } } - /** - * The Signals you can subscribe to that are dispatched when certain things happen on this Sprite or its components - * @type Events - */ - this.events = new Phaser.Events(this); - - /** - * This manages animations of the sprite. You can modify animations through it. (see AnimationManager) - * @type AnimationManager - */ - this.animations = new Phaser.AnimationManager(this); - - /** - * The Input Handler Component - * @type InputHandler - */ - this.input = new Phaser.InputHandler(this); - /** * The anchor sets the origin point of the texture. * The default is 0,0 this means the textures origin is the top left diff --git a/src/input/Input.js b/src/input/Input.js index caa26f461..14bc5c5e2 100644 --- a/src/input/Input.js +++ b/src/input/Input.js @@ -7,6 +7,9 @@ Phaser.Input = function (game) { this.game = game; + + this.hitCanvas = null; + this.hitContext = null; }; @@ -283,10 +286,18 @@ Phaser.Input.prototype = { this.activePointer = this.mousePointer; this.currentPointers = 0; - // this.hitCanvas = document.createElement('canvas'); - // this.hitCanvas.width = 1; - // this.hitCanvas.height = 1; - // this.hitContext = this.hitCanvas.getContext('2d'); + this.hitCanvas = document.createElement('canvas'); + this.hitCanvas.width = 1; + this.hitCanvas.height = 1; + this.hitContext = this.hitCanvas.getContext('2d'); + + // Debugging + // this.hitCanvas.style['width'] = '200px'; + // this.hitCanvas.style['height'] = '200px'; + // this.hitCanvas.style['position'] = 'absolute'; + // this.hitCanvas.style['left'] = '810px'; + // this.hitCanvas.style['backgroundColor'] = '#ef0000'; + // Phaser.Canvas.addToDOM(this.hitCanvas); this.mouse.start(); this.keyboard.start(); diff --git a/src/input/InputHandler.js b/src/input/InputHandler.js index fecde368e..35173284d 100644 --- a/src/input/InputHandler.js +++ b/src/input/InputHandler.js @@ -16,17 +16,31 @@ Phaser.InputHandler = function (sprite) { * The PriorityID controls which Sprite receives an Input event first if they should overlap. */ this.priorityID = 0; + this.useHandCursor = false; this.isDragged = false; - this.dragPixelPerfect = false; this.allowHorizontalDrag = true; this.allowVerticalDrag = true; this.bringToTop = false; + + this.snapOffset = null; this.snapOnDrag = false; this.snapOnRelease = false; this.snapX = 0; this.snapY = 0; + /** + * Should we use pixel perfect hit detection? Warning: expensive. Only enable if you really need it! + * @default false + */ + this.pixelPerfect = false; + + /** + * The alpha tolerance threshold. If the alpha value of the pixel matches or is above this value, it's considered a hit. + * @default 255 + */ + this.pixelPerfectAlpha = 255; + /** * Is this sprite allowed to be dragged by the mouse? true = yes, false = no * @default false @@ -58,10 +72,9 @@ Phaser.InputHandler = function (sprite) { Phaser.InputHandler.prototype = { - start: function (priority, checkBody, useHandCursor) { + start: function (priority, useHandCursor) { priority = priority || 0; - checkBody = checkBody || false; useHandCursor = useHandCursor || false; // Turning on @@ -69,7 +82,6 @@ Phaser.InputHandler.prototype = { { // Register, etc this.game.input.interactiveItems.add(this); - this.checkBody = checkBody; this.useHandCursor = useHandCursor; this.priorityID = priority; this._pointerData = []; @@ -317,19 +329,24 @@ Phaser.InputHandler.prototype = { { this.sprite.getLocalUnmodifiedPosition(this._tempPoint, pointer.x, pointer.y); - // Check against bounds - var width = this.sprite.texture.frame.width, - height = this.sprite.texture.frame.height, - x1 = -width * this.sprite.anchor.x, - y1; + // Check against bounds first (move these to private vars) + var x1 = -(this.sprite.texture.frame.width) * this.sprite.anchor.x; + var y1; - if (this._tempPoint.x > x1 && this._tempPoint.x < x1 + width) + if (this._tempPoint.x > x1 && this._tempPoint.x < x1 + this.sprite.texture.frame.width) { - y1 = -height * this.sprite.anchor.y; + y1 = -(this.sprite.texture.frame.height) * this.sprite.anchor.y; - if (this._tempPoint.y > y1 && this._tempPoint.y < y1 + height) + if (this._tempPoint.y > y1 && this._tempPoint.y < y1 + this.sprite.texture.frame.height) { - return true; + if (this.pixelPerfect) + { + return this.checkPixel(this._tempPoint.x, this._tempPoint.y); + } + else + { + return true; + } } } } @@ -338,6 +355,30 @@ Phaser.InputHandler.prototype = { return false; } + }, + + checkPixel: function (x, y) { + + x += (this.sprite.texture.frame.width * this.sprite.anchor.x); + y += (this.sprite.texture.frame.height * this.sprite.anchor.y); + + // Grab a pixel from our image into the hitCanvas and then test it + + if (this.sprite.texture.baseTexture.source) + { + this.game.input.hitContext.clearRect(0, 0, 1, 1); + this.game.input.hitContext.drawImage(this.sprite.texture.baseTexture.source, x, y, 1, 1, 0, 0, 1, 1); + + var rgb = this.game.input.hitContext.getImageData(0, 0, 1, 1); + + if (rgb.data[3] >= this.pixelPerfectAlpha) + { + return true; + } + } + + return false; + }, /** @@ -620,8 +661,9 @@ Phaser.InputHandler.prototype = { this.bringToTop = bringToTop; this.dragOffset = new Phaser.Point(); this.dragFromCenter = lockCenter; - this.dragPixelPerfect = pixelPerfect; - this.dragPixelPerfectAlpha = alphaThreshold; + + this.pixelPerfect = pixelPerfect; + this.pixelPerfectAlpha = alphaThreshold; if (boundsRect) { diff --git a/src/physics/arcade/Body.js b/src/physics/arcade/Body.js index 6f5f0fb87..5385e6b39 100644 --- a/src/physics/arcade/Body.js +++ b/src/physics/arcade/Body.js @@ -124,8 +124,11 @@ Phaser.Physics.Arcade.Body.prototype = { postUpdate: function () { - this.sprite.x = this.x - this.offset.x + (this.sprite.anchor.x * this.width); - this.sprite.y = this.y - this.offset.y + (this.sprite.anchor.y * this.height); + // this.sprite.x = this.x - this.offset.x + (this.sprite.anchor.x * this.width); + // this.sprite.y = this.y - this.offset.y + (this.sprite.anchor.y * this.height); + + this.sprite.x = (this.x - this.offset.x + (this.sprite.anchor.x * this.width)) - (this.game.world.camera.x * this.sprite.scrollFactor.x); + this.sprite.y = (this.y - this.offset.y + (this.sprite.anchor.y * this.height)) - (this.game.world.camera.y * this.sprite.scrollFactor.y); if (this.allowRotation) { diff --git a/src/utils/Debug.js b/src/utils/Debug.js index 5444606fe..917971937 100644 --- a/src/utils/Debug.js +++ b/src/utils/Debug.js @@ -15,17 +15,17 @@ Phaser.Utils.Debug = function (game) { this.game = game; this.context = game.context; + this.font = '14px Courier'; + this.lineHeight = 16; + this.renderShadow = true; + this.currentX = 0; + this.currentY = 0; + this.currentAlpha = 1; + }; Phaser.Utils.Debug.prototype = { - font: '14px Courier', - lineHeight: 16, - renderShadow: true, - currentX: 0, - currentY: 0, - currentAlpha: 1, - context: null, /** * Internal method that resets the debug output values.