diff --git a/README.md b/README.md index 0a87f962..7d5771de 100644 --- a/README.md +++ b/README.md @@ -115,6 +115,8 @@ New features: * Added Cache.updateFrameData which is really useful for swapping FrameData blocks in the cache. * Loader.physics now lets you load Lime + Corona JSON Physics data, which can be used with Body.loadPolygon and Body.loadData. * Cache.addPhysicsData and Cache.getPhysicsData allow you to store parsed JSON physics data in the cache, for sharing between Bodies. +* fixedToCamera now works across all display objects. When enabled it will fix at its current x/y coordinate, but can be changed via cameraOffset. +* fixedToCamrea now works for Groups as well :) You can fix a Group to the camera and it will influence its children. Updates: diff --git a/examples/wip/fixed to cam.js b/examples/wip/fixed to cam.js new file mode 100644 index 00000000..eb62ea43 --- /dev/null +++ b/examples/wip/fixed to cam.js @@ -0,0 +1,83 @@ + +var game = new Phaser.Game(800, 600, Phaser.CANVAS, 'phaser-example', { preload: preload, create: create, update: update, render: render }); + +function preload() { + + game.load.image('backdrop', 'assets/pics/remember-me.jpg'); + game.load.image('mushroom', 'assets/sprites/mushroom2.png'); + game.load.image('coke', 'assets/sprites/cokecan.png'); + game.load.bitmapFont('desyrel', 'assets/fonts/desyrel.png', 'assets/fonts/desyrel.xml', null, 0, -32); + +} + +var cursors; +var mushroom; + +function create() { + + game.world.setBounds(0, 0, 1920, 1200); + game.add.image(0, 0, 'backdrop'); + + mushroom = game.add.sprite(400, 400, 'mushroom'); + + // Test Fixing an Image to the Camera + var fixie = game.add.image(100, 100, 'coke'); + fixie.fixedToCamera = true; + + // And tween it + game.add.tween(fixie.cameraOffset).to({ y: 500 }, 2000, Phaser.Easing.Bounce.Out, true, 0, 1000, true); + + // Test Fixing a Sprite to the Camera + var fixie2 = game.add.sprite(600, 100, 'coke'); + fixie2.fixedToCamera = true; + fixie2.inputEnabled = true; + fixie2.events.onInputDown.add(clicked, this); + + // Test Fixing a Text to the Camera + var text = game.add.text(300, 32, '-phaser-'); + text.fixedToCamera = true; + + // Test fixing a BitmapText to the Camera + var text2 = game.add.bitmapText(200, 500, 'desyrel', 'camera fixies', 32); + text2.fixedToCamera = true; + + // Button! do mouse events still work then? + + game.camera.follow(mushroom); + + cursors = game.input.keyboard.createCursorKeys(); + +} + +function clicked() { + + console.log('boom'); + +} + +function update() { + + if (cursors.left.isDown) + { + mushroom.x -= 8; + } + else if (cursors.right.isDown) + { + mushroom.x += 8; + } + + if (cursors.up.isDown) + { + mushroom.y -= 8; + } + else if (cursors.down.isDown) + { + mushroom.y += 8; + } + +} + +function render() { + + +} diff --git a/examples/wip/group fixed to cam.js b/examples/wip/group fixed to cam.js new file mode 100644 index 00000000..4b1af98d --- /dev/null +++ b/examples/wip/group fixed to cam.js @@ -0,0 +1,68 @@ + +var game = new Phaser.Game(800, 600, Phaser.CANVAS, 'phaser-example', { preload: preload, create: create, update: update, render: render }); + +function preload() { + + game.load.image('backdrop', 'assets/pics/remember-me.jpg'); + game.load.image('mushroom', 'assets/sprites/mushroom2.png'); + game.load.image('coke', 'assets/sprites/cokecan.png'); + +} + +var cursors; +var mushroom; + +function create() { + + game.world.setBounds(0, 0, 1920, 1200); + game.add.image(0, 0, 'backdrop'); + + mushroom = game.add.sprite(400, 400, 'mushroom'); + + var group = game.add.group(); + + for (var i = 0; i < 20; i++) + { + group.create(Math.random() * 800, Math.random() * 600, 'coke'); + } + + group.fixedToCamera = true; + + game.camera.follow(mushroom); + + cursors = game.input.keyboard.createCursorKeys(); + +} + +function clicked() { + + console.log('boom'); + +} + +function update() { + + if (cursors.left.isDown) + { + mushroom.x -= 8; + } + else if (cursors.right.isDown) + { + mushroom.x += 8; + } + + if (cursors.up.isDown) + { + mushroom.y -= 8; + } + else if (cursors.down.isDown) + { + mushroom.y += 8; + } + +} + +function render() { + + +} diff --git a/examples/wip/tilesprite fixed to cam.js b/examples/wip/tilesprite fixed to cam.js new file mode 100644 index 00000000..a99d6426 --- /dev/null +++ b/examples/wip/tilesprite fixed to cam.js @@ -0,0 +1,59 @@ + +var game = new Phaser.Game(800, 600, Phaser.AUTO, 'phaser-example', { preload: preload, create: create, update: update, render: render }); + +function preload() { + + game.load.image('backdrop', 'assets/pics/remember-me.jpg'); + game.load.image('mushroom', 'assets/sprites/mushroom2.png'); + game.load.image('starfield', 'assets/misc/starfield.jpg'); + +} + +var sprite; +var cursors; +var mushroom; + +function create() { + + game.world.setBounds(0, 0, 1920, 1200); + game.add.image(0, 0, 'backdrop'); + + sprite = game.add.tileSprite(100, 100, 200, 200, 'starfield'); + sprite.autoScroll(0, 100); + sprite.fixedToCamera = true; + + mushroom = game.add.sprite(400, 400, 'mushroom'); + + game.camera.follow(mushroom); + + cursors = game.input.keyboard.createCursorKeys(); + +} + +function update() { + + if (cursors.left.isDown) + { + mushroom.x -= 8; + } + else if (cursors.right.isDown) + { + mushroom.x += 8; + } + + if (cursors.up.isDown) + { + mushroom.y -= 8; + } + else if (cursors.down.isDown) + { + mushroom.y += 8; + } + +} + +function render() { + + // game.debug.renderText(sprite.frame, 32, 32); + +} diff --git a/src/core/Group.js b/src/core/Group.js index 74bb21dc..c1183520 100644 --- a/src/core/Group.js +++ b/src/core/Group.js @@ -89,6 +89,26 @@ Phaser.Group = function (game, parent, name, addToStage) { this._cursorIndex = 0; + /** + * @property {Phaser.Point} cameraOffset - If this object is fixedToCamera then this stores the x/y offset that its drawn at, from the top-left of the camera view. + */ + this.cameraOffset = new Phaser.Point(); + + /** + * A small internal cache: + * 0 = previous position.x + * 1 = previous position.y + * 2 = previous rotation + * 3 = renderID + * 4 = fresh? (0 = no, 1 = yes) + * 5 = outOfBoundsFired (0 = no, 1 = yes) + * 6 = exists (0 = no, 1 = yes) + * 7 = fixed to camera (0 = no, 1 = yes) + * @property {Int16Array} _cache + * @private + */ + this._cache = new Int16Array([0, 0, 0, 0, 1, 0, 1, 0]); + }; Phaser.Group.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); @@ -708,11 +728,19 @@ Phaser.Group.prototype.callAll = function (method, context) { */ Phaser.Group.prototype.preUpdate = function () { + if (!this.exists || !this.parent.exists) + { + this.renderOrderID = -1; + return false; + } + for (var i = this.children.length - 1; i >= 0; i--) { this.children[i].preUpdate(); } + return true; + } /** @@ -736,6 +764,13 @@ Phaser.Group.prototype.update = function () { */ Phaser.Group.prototype.postUpdate = function () { + // Fixed to Camera? + if (this._cache[7] === 1) + { + this.x = this.game.camera.view.x + this.cameraOffset.x; + this.y = this.game.camera.view.y + this.cameraOffset.y; + } + for (var i = this.children.length - 1; i >= 0; i--) { this.children[i].postUpdate(); @@ -1177,6 +1212,37 @@ Object.defineProperty(Phaser.Group.prototype, "angle", { }); +/** +* A Group that is fixed to the camera uses its x/y coordinates as offsets from the top left of the camera. These are stored in Group.cameraOffset. +* Note that the cameraOffset values are in addition to any parent in the display list. +* So if this Group was in a Group that has x: 200, then this will be added to the cameraOffset.x +* +* @name Phaser.Group#fixedToCamera +* @property {boolean} fixedToCamera - Set to true to fix this Group to the Camera at its current world coordinates. +*/ +Object.defineProperty(Phaser.Group.prototype, "fixedToCamera", { + + get: function () { + + return !!this._cache[7]; + + }, + + set: function (value) { + + if (value) + { + this._cache[7] = 1; + this.cameraOffset.set(this.x, this.y); + } + else + { + this._cache[7] = 0; + } + } + +}); + // Documentation stubs /** diff --git a/src/core/Stage.js b/src/core/Stage.js index fb23944a..5ee01cd3 100644 --- a/src/core/Stage.js +++ b/src/core/Stage.js @@ -82,7 +82,7 @@ Phaser.Stage.prototype.preUpdate = function () { var i = this.children.length; - while(i--) + while (i--) { this.children[i].preUpdate(); } @@ -98,7 +98,7 @@ Phaser.Stage.prototype.update = function () { var i = this.children.length; - while(i--) + while (i--) { this.children[i].update(); } @@ -123,7 +123,7 @@ Phaser.Stage.prototype.postUpdate = function () { var i = this.children.length; - while(i--) + while (i--) { if (this.children[i] !== this.game.world.camera.target) { @@ -137,7 +137,7 @@ Phaser.Stage.prototype.postUpdate = function () { var i = this.children.length; - while(i--) + while (i--) { this.children[i].postUpdate(); } diff --git a/src/core/World.js b/src/core/World.js index 421e44f8..91e6b2d4 100644 --- a/src/core/World.js +++ b/src/core/World.js @@ -95,6 +95,81 @@ Phaser.World.prototype.setBounds = function (x, y, width, height) { } +/** +* This is called automatically after the plugins preUpdate and before the State.update. +* Most objects have preUpdate methods and it's where initial movement and positioning is done. +* +* @method Phaser.World#preUpdate +*/ +Phaser.World.prototype.preUpdate = function () { + + this.currentRenderOrderID = 0; + + var i = this.children.length; + + while (i--) + { + this.children[i].preUpdate(); + } + +} + +/** +* This is called automatically after the State.update, but before particles or plugins update. +* +* @method Phaser.World#update +*/ +Phaser.World.prototype.update = function () { + + var i = this.children.length; + + while (i--) + { + this.children[i].update(); + } + +} + +/** +* This is called automatically before the renderer runs and after the plugins have updated. +* In postUpdate this is where all the final physics calculatations and object positioning happens. +* The objects are processed in the order of the display list. +* The only exception to this is if the camera is following an object, in which case that is updated first. +* +* @method Phaser.World#postUpdate +*/ +Phaser.World.prototype.postUpdate = function () { + + if (this.game.world.camera.target) + { + this.game.world.camera.target.postUpdate(); + + this.game.world.camera.update(); + + var i = this.children.length; + + while (i--) + { + if (this.children[i] !== this.game.world.camera.target) + { + this.children[i].postUpdate(); + } + } + } + else + { + this.game.world.camera.update(); + + var i = this.children.length; + + while (i--) + { + this.children[i].postUpdate(); + } + } + +} + /** * Destroyer of worlds. * @method Phaser.World#destroy diff --git a/src/gameobjects/BitmapText.js b/src/gameobjects/BitmapText.js index 2424f64d..da069969 100644 --- a/src/gameobjects/BitmapText.js +++ b/src/gameobjects/BitmapText.js @@ -59,13 +59,6 @@ Phaser.BitmapText = function (game, x, y, font, text, size) { */ this.world = new Phaser.Point(x, y); - /** - * An object that is fixed to the camera ignores the position of any ancestors in the display list and uses its x/y coordinates as offsets from the top left of the camera. - * @property {boolean} fixedToCamera - Fixes this object to the Camera. - * @default - */ - this.fixedToCamera = false; - /** * @property {string} _text - Internal cache var. * @private @@ -106,10 +99,30 @@ Phaser.BitmapText = function (game, x, y, font, text, size) { */ this.input = null; + /** + * @property {Phaser.Point} cameraOffset - If this object is fixedToCamera then this stores the x/y offset that its drawn at, from the top-left of the camera view. + */ + this.cameraOffset = new Phaser.Point(); + PIXI.BitmapText.call(this, text); this.position.set(x, y); + /** + * A small internal cache: + * 0 = previous position.x + * 1 = previous position.y + * 2 = previous rotation + * 3 = renderID + * 4 = fresh? (0 = no, 1 = yes) + * 5 = outOfBoundsFired (0 = no, 1 = yes) + * 6 = exists (0 = no, 1 = yes) + * 7 = fixed to camera (0 = no, 1 = yes) + * @property {Int16Array} _cache + * @private + */ + this._cache = new Int16Array([0, 0, 0, 0, 1, 0, 1, 0]); + }; Phaser.BitmapText.prototype = Object.create(PIXI.BitmapText.prototype); @@ -134,13 +147,30 @@ Phaser.BitmapText.prototype.setStyle = function() { */ Phaser.BitmapText.prototype.preUpdate = function () { + this._cache[0] = this.world.x; + this._cache[1] = this.world.y; + this._cache[2] = this.rotation; + if (!this.exists || !this.parent.exists) { - // Reset the renderOrderID + this.renderOrderID = -1; return false; } - this.world.setTo(this.game.camera.x + this.worldTransform.tx, this.game.camera.y + this.worldTransform.ty); + if (this.autoCull) + { + // Won't get rendered but will still get its transform updated + this.renderable = this.game.world.camera.screenView.intersects(this.getBounds()); + } + + 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; } @@ -160,14 +190,13 @@ Phaser.BitmapText.prototype.update = function() { */ Phaser.BitmapText.prototype.postUpdate = function () { - if (this.exists) + // Fixed to Camera? + if (this._cache[7] === 1) { - if (this.fixedToCamera) - { - // this.position.x = this.game.camera.view.x + this.x; - // this.position.y = this.game.camera.view.y + this.y; - } + this.position.x = this.game.camera.view.x + this.cameraOffset.x; + this.position.y = this.game.camera.view.y + this.cameraOffset.y; } + } /** @@ -370,3 +399,34 @@ Object.defineProperty(Phaser.BitmapText.prototype, "inputEnabled", { } }); + +/** +* An BitmapText that is fixed to the camera uses its x/y coordinates as offsets from the top left of the camera. These are stored in BitmapText.cameraOffset. +* Note that the cameraOffset values are in addition to any parent in the display list. +* So if this BitmapText was in a Group that has x: 200, then this will be added to the cameraOffset.x +* +* @name Phaser.BitmapText#fixedToCamera +* @property {boolean} fixedToCamera - Set to true to fix this BitmapText to the Camera at its current world coordinates. +*/ +Object.defineProperty(Phaser.BitmapText.prototype, "fixedToCamera", { + + get: function () { + + return !!this._cache[7]; + + }, + + set: function (value) { + + if (value) + { + this._cache[7] = 1; + this.cameraOffset.set(this.x, this.y); + } + else + { + this._cache[7] = 0; + } + } + +}); diff --git a/src/gameobjects/Image.js b/src/gameobjects/Image.js index d53cc3df..65679081 100644 --- a/src/gameobjects/Image.js +++ b/src/gameobjects/Image.js @@ -30,13 +30,13 @@ Phaser.Image = function (game, x, y, key, frame) { this.game = game; /** - * @property {boolean} exists - If exists = false then the Sprite isn't updated by the core game loop or physics subsystem at all. + * @property {boolean} exists - If exists = false then the Image isn't updated by the core game loop. * @default */ this.exists = true; /** - * @property {string} name - The user defined name given to this Sprite. + * @property {string} name - The user defined name given to this Image. * @default */ this.name = ''; @@ -48,12 +48,12 @@ Phaser.Image = function (game, x, y, key, frame) { this.type = Phaser.IMAGE; /** - * @property {Phaser.Events} events - The Events you can subscribe to that are dispatched when certain things happen on this Sprite or its components. + * @property {Phaser.Events} events - The Events you can subscribe to that are dispatched when certain things happen on this Image or its components. */ this.events = new Phaser.Events(this); /** - * @property {string|Phaser.RenderTexture|Phaser.BitmapData|PIXI.Texture} key - This is the image or texture used by the Sprite during rendering. It can be a string which is a reference to the Cache entry, or an instance of a RenderTexture, BitmapData or PIXI.Texture. + * @property {string|Phaser.RenderTexture|Phaser.BitmapData|PIXI.Texture} key - This is the image or texture used by the Image during rendering. It can be a string which is a reference to the Cache entry, or an instance of a RenderTexture, BitmapData or PIXI.Texture. */ this.key = key; @@ -76,38 +76,44 @@ Phaser.Image = function (game, x, y, key, frame) { this.position.set(x, y); /** - * @property {Phaser.Point} world - The world coordinates of this Sprite. This differs from the x/y coordinates which are relative to the Sprites container. + * @property {Phaser.Point} world - The world coordinates of this Image. This differs from the x/y coordinates which are relative to the Images container. */ this.world = new Phaser.Point(x, y); /** - * Should this Sprite be automatically culled if out of range of the camera? + * Should this Image be automatically culled if out of range of the camera? * A culled sprite has its renderable property set to 'false'. * Be advised this is quite an expensive operation, as it has to calculate the bounds of the object every frame, so only enable it if you really need it. * - * @property {boolean} autoCull - A flag indicating if the Sprite should be automatically camera culled or not. + * @property {boolean} autoCull - A flag indicating if the Image should be automatically camera culled or not. * @default */ this.autoCull = false; - /** - * A Sprite that is fixed to the camera uses its x/y coordinates as offsets from the top left of the camera. - * Note that if this Image is a child of a display object that has changed its position then the offset will be calculated from that. - * @property {boolean} fixedToCamera - Fixes this Sprite to the Camera. - * @default - */ - this.fixedToCamera = false; - /** * @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 + * @property {Phaser.Point} cameraOffset - If this object is fixedToCamera then this stores the x/y offset that its drawn at, from the top-left of the camera view. + */ + this.cameraOffset = new Phaser.Point(); + + /** + * A small internal cache: + * 0 = previous position.x + * 1 = previous position.y + * 2 = previous rotation + * 3 = renderID + * 4 = fresh? (0 = no, 1 = yes) + * 5 = outOfBoundsFired (0 = no, 1 = yes) + * 6 = exists (0 = no, 1 = yes) + * 7 = fixed to camera (0 = no, 1 = yes) + * @property {Int16Array} _cache * @private */ - this._cache = [0, 0, 0, 0]; + this._cache = new Int16Array([0, 0, 0, 0, 1, 0, 1, 0]); }; @@ -172,22 +178,23 @@ Phaser.Image.prototype.postUpdate = function() { this.key.render(); } - if (this.fixedToCamera) + // Fixed to Camera? + if (this._cache[7] === 1) { - this.position.x = this.game.camera.view.x + this.x; - this.position.y = this.game.camera.view.y + this.y; + this.position.x = this.game.camera.view.x + this.cameraOffset.x; + this.position.y = this.game.camera.view.y + this.cameraOffset.y; } } /** -* Changes the Texture the Sprite is using entirely. The old texture is removed and the new one is referenced or fetched from the Cache. +* Changes the Texture the Image is using entirely. The old texture is removed and the new one is referenced or fetched from the Cache. * This causes a WebGL texture update, so use sparingly or in low-intensity portions of your game. * * @method Phaser.Image#loadTexture * @memberof Phaser.Image -* @param {string|Phaser.RenderTexture|Phaser.BitmapData|PIXI.Texture} key - This is the image or texture used by the Sprite during rendering. It can be a string which is a reference to the Cache entry, or an instance of a RenderTexture, BitmapData or PIXI.Texture. -* @param {string|number} frame - If this Sprite is using part of a sprite sheet or texture atlas you can specify the exact frame to use by giving a string or numeric index. +* @param {string|Phaser.RenderTexture|Phaser.BitmapData|PIXI.Texture} key - This is the image or texture used by the Image during rendering. It can be a string which is a reference to the Cache entry, or an instance of a RenderTexture, BitmapData or PIXI.Texture. +* @param {string|number} frame - If this Image is using part of a sprite sheet or texture atlas you can specify the exact frame to use by giving a string or numeric index. */ Phaser.Image.prototype.loadTexture = function (key, frame) { @@ -305,9 +312,9 @@ Phaser.Image.prototype.crop = function(rect) { } /** -* Brings a 'dead' Sprite back to life, optionally giving it the health value specified. -* A resurrected Sprite has its alive, exists and visible properties all set to true. -* It will dispatch the onRevived event, you can listen to Sprite.events.onRevived for the signal. +* Brings a 'dead' Image back to life, optionally giving it the health value specified. +* A resurrected Image has its alive, exists and visible properties all set to true. +* It will dispatch the onRevived event, you can listen to Image.events.onRevived for the signal. * * @method Phaser.Image#revive * @memberof Phaser.Image @@ -329,10 +336,10 @@ Phaser.Image.prototype.revive = function() { } /** -* Kills a Sprite. A killed Sprite has its alive, exists and visible properties all set to false. -* It will dispatch the onKilled event, you can listen to Sprite.events.onKilled for the signal. -* Note that killing a Sprite is a way for you to quickly recycle it in a Sprite pool, it doesn't free it up from memory. -* If you don't need this Sprite any more you should call Sprite.destroy instead. +* Kills a Image. A killed Image has its alive, exists and visible properties all set to false. +* It will dispatch the onKilled event, you can listen to Image.events.onKilled for the signal. +* Note that killing a Image is a way for you to quickly recycle it in a Image pool, it doesn't free it up from memory. +* If you don't need this Image any more you should call Image.destroy instead. * * @method Phaser.Image#kill * @memberof Phaser.Image @@ -354,7 +361,7 @@ Phaser.Image.prototype.kill = function() { } /** -* Destroys the Sprite. This removes it from its parent group, destroys the input, event and animation handlers if present +* Destroys the Image. This removes it from its parent group, destroys the input, event and animation handlers if present * and nulls its reference to game, freeing it up for garbage collection. * * @method Phaser.Image#destroy @@ -391,12 +398,12 @@ Phaser.Image.prototype.destroy = function() { } /** -* Resets the Sprite. This places the Sprite at the given x/y world coordinates and then sets alive, exists, visible and renderable all to true. +* Resets the Image. This places the Image at the given x/y world coordinates and then sets alive, exists, visible and renderable all to true. * * @method Phaser.Image#reset * @memberof Phaser.Image -* @param {number} x - The x coordinate (in world space) to position the Sprite at. -* @param {number} y - The y coordinate (in world space) to position the Sprite at. +* @param {number} x - The x coordinate (in world space) to position the Image at. +* @param {number} y - The y coordinate (in world space) to position the Image at. * @return {Phaser.Image} This instance. */ Phaser.Image.prototype.reset = function(x, y) { @@ -414,7 +421,7 @@ Phaser.Image.prototype.reset = function(x, y) { } /** -* Brings the Sprite to the top of the display list it is a child of. Sprites that are members of a Phaser.Group are only +* Brings the Image to the top of the display list it is a child of. Images that are members of a Phaser.Group are only * bought to the top of that Group, not the entire display list. * * @method Phaser.Image#bringToTop @@ -440,9 +447,9 @@ Phaser.Image.prototype.bringToTop = function(child) { } /** -* Indicates the rotation of the Sprite, in degrees, from its original orientation. Values from 0 to 180 represent clockwise rotation; values from 0 to -180 represent counterclockwise rotation. +* Indicates the rotation of the Image, in degrees, from its original orientation. Values from 0 to 180 represent clockwise rotation; values from 0 to -180 represent counterclockwise rotation. * Values outside this range are added to or subtracted from 360 to obtain a value within the range. For example, the statement player.angle = 450 is the same as player.angle = 90. -* If you wish to work in radians instead of degrees use the property Sprite.rotation instead. Working in radians is also a little faster as it doesn't have to convert the angle. +* If you wish to work in radians instead of degrees use the property Image.rotation instead. Working in radians is also a little faster as it doesn't have to convert the angle. * * @name Phaser.Image#angle * @property {number} angle - The angle of this Image in degrees. @@ -656,3 +663,34 @@ Object.defineProperty(Phaser.Image.prototype, "inputEnabled", { } }); + +/** +* An Image that is fixed to the camera uses its x/y coordinates as offsets from the top left of the camera. These are stored in Image.cameraOffset. +* Note that the cameraOffset values are in addition to any parent in the display list. +* So if this Image was in a Group that has x: 200, then this will be added to the cameraOffset.x +* +* @name Phaser.Image#fixedToCamera +* @property {boolean} fixedToCamera - Set to true to fix this Image to the Camera at its current world coordinates. +*/ +Object.defineProperty(Phaser.Image.prototype, "fixedToCamera", { + + get: function () { + + return !!this._cache[7]; + + }, + + set: function (value) { + + if (value) + { + this._cache[7] = 1; + this.cameraOffset.set(this.x, this.y); + } + else + { + this._cache[7] = 0; + } + } + +}); diff --git a/src/gameobjects/Sprite.js b/src/gameobjects/Sprite.js index b13634a1..ed1d4a81 100644 --- a/src/gameobjects/Sprite.js +++ b/src/gameobjects/Sprite.js @@ -92,14 +92,6 @@ Phaser.Sprite = function (game, x, y, key, frame) { */ this.autoCull = false; - /** - * A Sprite that is fixed to the camera uses its x/y coordinates as offsets from the top left of the camera. - * Note that if this Image is a child of a display object that has changed its position then the offset will be calculated from that. - * @property {boolean} fixedToCamera - Fixes this Sprite to the Camera. - * @default - */ - this.fixedToCamera = false; - /** * @property {Phaser.InputHandler|null} input - The Input Handler for this object. Needs to be enabled with image.inputEnabled = true before you can use it. */ @@ -144,6 +136,11 @@ Phaser.Sprite = function (game, x, y, key, frame) { */ this.debug = false; + /** + * @property {Phaser.Point} cameraOffset - If this object is fixedToCamera then this stores the x/y offset that its drawn at, from the top-left of the camera view. + */ + this.cameraOffset = new Phaser.Point(); + /** * A small internal cache: * 0 = previous position.x @@ -153,10 +150,11 @@ Phaser.Sprite = function (game, x, y, key, frame) { * 4 = fresh? (0 = no, 1 = yes) * 5 = outOfBoundsFired (0 = no, 1 = yes) * 6 = exists (0 = no, 1 = yes) + * 7 = fixed to camera (0 = no, 1 = yes) * @property {Int16Array} _cache * @private */ - this._cache = new Int16Array([0, 0, 0, 0, 1, 0, 1]); + this._cache = new Int16Array([0, 0, 0, 0, 1, 0, 1, 0]); /** * @property {Phaser.Rectangle} _bounds - Internal cache var. @@ -301,12 +299,13 @@ Phaser.Sprite.prototype.postUpdate = function() { { this.body.postUpdate(); } + } - if (this.fixedToCamera) - { - // this.position.x = this.game.camera.view.x + this.x; - // this.position.y = this.game.camera.view.y + this.y; - } + // Fixed to Camera? + if (this._cache[7] === 1) + { + this.position.x = this.game.camera.view.x + this.cameraOffset.x; + this.position.y = this.game.camera.view.y + this.cameraOffset.y; } }; @@ -384,12 +383,12 @@ Phaser.Sprite.prototype.loadTexture = function (key, frame) { }; /** -* Crop allows you to crop the texture used to display this Image. -* Cropping takes place from the top-left of the Image and can be modified in real-time by providing an updated rectangle object. +* Crop allows you to crop the texture used to display this Sprite. +* Cropping takes place from the top-left of the Sprite and can be modified in real-time by providing an updated rectangle object. * * @method Phaser.Sprite#crop * @memberof Phaser.Sprite -* @param {Phaser.Rectangle} rect - The Rectangle to crop the Image to. Pass null or no parameters to clear a previously set crop rectangle. +* @param {Phaser.Rectangle} rect - The Rectangle to crop the Sprite to. Pass null or no parameters to clear a previously set crop rectangle. */ Phaser.Sprite.prototype.crop = function(rect) { @@ -714,10 +713,10 @@ Object.defineProperty(Phaser.Sprite.prototype, "deltaZ", { }); /** -* Checks if the Image bounds are within the game world, otherwise false if fully outside of it. +* Checks if the Sprite bounds are within the game world, otherwise false if fully outside of it. * * @name Phaser.Sprite#inWorld -* @property {boolean} inWorld - True if the Image bounds is within the game world, even if only partially. Otherwise false if fully outside of it. +* @property {boolean} inWorld - True if the Sprite bounds is within the game world, even if only partially. Otherwise false if fully outside of it. * @readonly */ Object.defineProperty(Phaser.Sprite.prototype, "inWorld", { @@ -731,10 +730,10 @@ Object.defineProperty(Phaser.Sprite.prototype, "inWorld", { }); /** -* Checks if the Image bounds are within the game camera, otherwise false if fully outside of it. +* Checks if the Sprite bounds are within the game camera, otherwise false if fully outside of it. * * @name Phaser.Sprite#inCamera -* @property {boolean} inCamera - True if the Image bounds is within the game camera, even if only partially. Otherwise false if fully outside of it. +* @property {boolean} inCamera - True if the Sprite bounds is within the game camera, even if only partially. Otherwise false if fully outside of it. * @readonly */ Object.defineProperty(Phaser.Sprite.prototype, "inCamera", { @@ -916,3 +915,35 @@ Object.defineProperty(Phaser.Sprite.prototype, "exists", { } }); + + +/** +* An Sprite that is fixed to the camera uses its x/y coordinates as offsets from the top left of the camera. These are stored in Sprite.cameraOffset. +* Note that the cameraOffset values are in addition to any parent in the display list. +* So if this Sprite was in a Group that has x: 200, then this will be added to the cameraOffset.x +* +* @name Phaser.Sprite#fixedToCamera +* @property {boolean} fixedToCamera - Set to true to fix this Sprite to the Camera at its current world coordinates. +*/ +Object.defineProperty(Phaser.Sprite.prototype, "fixedToCamera", { + + get: function () { + + return !!this._cache[7]; + + }, + + set: function (value) { + + if (value) + { + this._cache[7] = 1; + this.cameraOffset.set(this.x, this.y); + } + else + { + this._cache[7] = 0; + } + } + +}); diff --git a/src/gameobjects/Text.js b/src/gameobjects/Text.js index b89281fa..e2c42af1 100644 --- a/src/gameobjects/Text.js +++ b/src/gameobjects/Text.js @@ -49,13 +49,6 @@ Phaser.Text = function (game, x, y, text, style) { */ this.world = new Phaser.Point(x, y); - /** - * An object that is fixed to the camera ignores the position of any ancestors in the display list and uses its x/y coordinates as offsets from the top left of the camera. - * @property {boolean} fixedToCamera - Fixes this object to the Camera. - * @default - */ - this.fixedToCamera = false; - /** * @property {string} _text - Internal cache var. * @private @@ -96,10 +89,30 @@ Phaser.Text = function (game, x, y, text, style) { */ this.input = null; + /** + * @property {Phaser.Point} cameraOffset - If this object is fixedToCamera then this stores the x/y offset that its drawn at, from the top-left of the camera view. + */ + this.cameraOffset = new Phaser.Point(); + PIXI.Text.call(this, text, style); this.position.set(x, y); + /** + * A small internal cache: + * 0 = previous position.x + * 1 = previous position.y + * 2 = previous rotation + * 3 = renderID + * 4 = fresh? (0 = no, 1 = yes) + * 5 = outOfBoundsFired (0 = no, 1 = yes) + * 6 = exists (0 = no, 1 = yes) + * 7 = fixed to camera (0 = no, 1 = yes) + * @property {Int16Array} _cache + * @private + */ + this._cache = new Int16Array([0, 0, 0, 0, 1, 0, 1, 0]); + }; Phaser.Text.prototype = Object.create(PIXI.Text.prototype); @@ -111,13 +124,30 @@ Phaser.Text.prototype.constructor = Phaser.Text; */ Phaser.Text.prototype.preUpdate = function () { + this._cache[0] = this.world.x; + this._cache[1] = this.world.y; + this._cache[2] = this.rotation; + if (!this.exists || !this.parent.exists) { - // Reset the renderOrderID + this.renderOrderID = -1; return false; } - this.world.setTo(this.game.camera.x + this.worldTransform.tx, this.game.camera.y + this.worldTransform.ty); + if (this.autoCull) + { + // Won't get rendered but will still get its transform updated + this.renderable = this.game.world.camera.screenView.intersects(this.getBounds()); + } + + 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; } @@ -137,14 +167,13 @@ Phaser.Text.prototype.update = function() { */ Phaser.Text.prototype.postUpdate = function () { - if (this.exists) + // Fixed to Camera? + if (this._cache[7] === 1) { - if (this.fixedToCamera) - { - // this.position.x = this.game.camera.view.x + this.x; - // this.position.y = this.game.camera.view.y + this.y; - } + this.position.x = this.game.camera.view.x + this.cameraOffset.x; + this.position.y = this.game.camera.view.y + this.cameraOffset.y; } + } /** @@ -749,3 +778,34 @@ Object.defineProperty(Phaser.Text.prototype, "inputEnabled", { } }); + +/** +* An Text that is fixed to the camera uses its x/y coordinates as offsets from the top left of the camera. These are stored in Text.cameraOffset. +* Note that the cameraOffset values are in addition to any parent in the display list. +* So if this Text was in a Group that has x: 200, then this will be added to the cameraOffset.x +* +* @name Phaser.Text#fixedToCamera +* @property {boolean} fixedToCamera - Set to true to fix this Text to the Camera at its current world coordinates. +*/ +Object.defineProperty(Phaser.Text.prototype, "fixedToCamera", { + + get: function () { + + return !!this._cache[7]; + + }, + + set: function (value) { + + if (value) + { + this._cache[7] = 1; + this.cameraOffset.set(this.x, this.y); + } + else + { + this._cache[7] = 0; + } + } + +}); diff --git a/src/gameobjects/TileSprite.js b/src/gameobjects/TileSprite.js index 644047d2..e147145f 100644 --- a/src/gameobjects/TileSprite.js +++ b/src/gameobjects/TileSprite.js @@ -88,6 +88,26 @@ Phaser.TileSprite = function (game, x, y, width, height, key, frame) { */ this.world = new Phaser.Point(x, y); + /** + * @property {Phaser.Point} cameraOffset - If this object is fixedToCamera then this stores the x/y offset that its drawn at, from the top-left of the camera view. + */ + this.cameraOffset = new Phaser.Point(); + + /** + * A small internal cache: + * 0 = previous position.x + * 1 = previous position.y + * 2 = previous rotation + * 3 = renderID + * 4 = fresh? (0 = no, 1 = yes) + * 5 = outOfBoundsFired (0 = no, 1 = yes) + * 6 = exists (0 = no, 1 = yes) + * 7 = fixed to camera (0 = no, 1 = yes) + * @property {Int16Array} _cache + * @private + */ + this._cache = new Int16Array([0, 0, 0, 0, 1, 0, 1, 0]); + }; Phaser.TileSprite.prototype = Object.create(PIXI.TilingSprite.prototype); @@ -107,12 +127,12 @@ Phaser.TileSprite.prototype.preUpdate = function() { if (this._scroll.x !== 0) { - this.tilePosition.x += Math.floor(this._scroll.x * this.game.time.physicsElapsed); + this.tilePosition.x += this._scroll.x * this.game.time.physicsElapsed; } if (this._scroll.y !== 0) { - this.tilePosition.y += Math.floor(this._scroll.y * this.game.time.physicsElapsed); + this.tilePosition.y += this._scroll.y * this.game.time.physicsElapsed; } return true; @@ -137,10 +157,11 @@ Phaser.TileSprite.prototype.update = function() { */ Phaser.TileSprite.prototype.postUpdate = function() { - if (this.fixedToCamera) + // Fixed to Camera? + if (this._cache[7] === 1) { - this.position.x = this.game.camera.view.x + this.x; - this.position.y = this.game.camera.view.y + this.y; + this.position.x = this.game.camera.view.x + this.cameraOffset.x; + this.position.y = this.game.camera.view.y + this.cameraOffset.y; } } @@ -357,3 +378,34 @@ Object.defineProperty(Phaser.TileSprite.prototype, "frameName", { } }); + +/** +* An TileSprite that is fixed to the camera uses its x/y coordinates as offsets from the top left of the camera. These are stored in TileSprite.cameraOffset. +* Note that the cameraOffset values are in addition to any parent in the display list. +* So if this TileSprite was in a Group that has x: 200, then this will be added to the cameraOffset.x +* +* @name Phaser.TileSprite#fixedToCamera +* @property {boolean} fixedToCamera - Set to true to fix this TileSprite to the Camera at its current world coordinates. +*/ +Object.defineProperty(Phaser.TileSprite.prototype, "fixedToCamera", { + + get: function () { + + return !!this._cache[7]; + + }, + + set: function (value) { + + if (value) + { + this._cache[7] = 1; + this.cameraOffset.set(this.x, this.y); + } + else + { + this._cache[7] = 0; + } + } + +}); diff --git a/src/physics/Body.js b/src/physics/Body.js index c0937d88..fe52efb0 100644 --- a/src/physics/Body.js +++ b/src/physics/Body.js @@ -675,6 +675,32 @@ Phaser.Physics.Body.prototype = { }, + /** + * Reads the physics data from a physics data file stored in the Game.Cache. + * It will add the shape data to this Body, as well as set the density (mass), friction and bounce (restitution) values. + * + * @method Phaser.Physics.Body#loadPolygon + * @param {string} key - The key of the Physics Data file as stored in Game.Cache. + * @param {string} object - The key of the object within the Physics data file that you wish to load the shape data from. + * @param {object} options - An object containing the build options: + * @param {boolean} [options.optimalDecomp=false] - Set to true if you need optimal decomposition. Warning: very slow for polygons with more than 10 vertices. + * @param {boolean} [options.skipSimpleCheck=false] - Set to true if you already know that the path is not intersecting itself. + * @param {boolean|number} [options.removeCollinearPoints=false] - Set to a number (angle threshold value) to remove collinear points, or false to keep all points. + * @return {boolean} True on success, else false. + */ + loadData: function (key, object, options) { + + var data = game.cache.getPhysicsData(key, object); + + if (data && data.shape) + { + this.mass = data.density; + // set friction + bounce here + this.loadPolygon(key, object); + } + + }, + /** * Convert p2 physics value to pixel scale. *