diff --git a/README.md b/README.md index 84a35d9b..67dba6f8 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,8 @@ Change Log Version 1.1.3 - in build +* New: The object returned by Math.sinCosGenerator now contains a length property. +* New: Phaser.BitmapData object. Can be used as a texture for a Sprite or Tiling Sprite. See the new examples and docs for details. * New: RenderTexture.render now takes a Phaser.Group. Also added renderXY for when you don't want to make a new Point object. * New: Implementing PluginManager.remove, added PluginManager.removeAll (thanks crazysam) * New: Added scrollFactorX/scrollFactorY to TilemapLayers (thanks jcd-as) @@ -69,6 +71,8 @@ Version 1.1.3 - in build * Updated: If running in Canvas mode and you have a render function it will save the context and reset the transform before running your render function * Updated: Sprite will now check the exists property of the Group it is in, if the Group.exists = false the Sprite won't update. * Updated: Lots of small documentation tweaks across various files such as Pointer. +* Updated: If you specify 'null' as a Group parent it will now revert to using the World as the parent (before only 'undefined' worked) + diff --git a/build/config.php b/build/config.php index f6d76bc4..2739eb39 100644 --- a/build/config.php +++ b/build/config.php @@ -59,7 +59,6 @@ - @@ -88,6 +87,7 @@ + diff --git a/examples/_site/view_full.html b/examples/_site/view_full.html index ce7a420b..140add1c 100644 --- a/examples/_site/view_full.html +++ b/examples/_site/view_full.html @@ -95,6 +95,7 @@ + diff --git a/examples/_site/view_lite.html b/examples/_site/view_lite.html index 8e51476a..f0b01269 100644 --- a/examples/_site/view_lite.html +++ b/examples/_site/view_lite.html @@ -94,6 +94,7 @@ + diff --git a/examples/display/bitmapdata wobble.js b/examples/display/bitmapdata wobble.js new file mode 100644 index 00000000..53190772 --- /dev/null +++ b/examples/display/bitmapdata wobble.js @@ -0,0 +1,77 @@ + +var game = new Phaser.Game(800, 600, Phaser.AUTO, 'phaser-example', { preload: preload, create: create, update: update }); + +function preload() { + + game.load.image('ball', 'assets/sprites/shinyball.png'); + +} + +var bmd; + +var waveSize = 8; +var wavePixelChunk = 2; +var waveData; +var waveDataCounter; + +function create() { + + // Create our BitmapData object at a size of 32x64 + bmd = game.add.bitmapData('ball', 32, 64); + + // And apply it to 100 randomly positioned sprites + for (var i = 0; i < 100; i++) + { + game.add.sprite(game.world.randomX, game.world.randomY, bmd); + } + + // Populate the wave with some data + waveData = game.math.sinCosGenerator(32, 8, 8, 2); + +} + +function update() { + + // Clear the BitmapData + bmd.clear(); + + updateWobblyBall(); + +} + +// This creates a simple sine-wave effect running through our BitmapData. +// This is then duplicated across all 100 sprites using it, meaning we only have to calculate it and upload it to the GPU once. + +function updateWobblyBall() +{ + var s = 0; + var copyRect = { x: 0, y: 0, w: wavePixelChunk, h: 32 }; + var copyPoint = { x: 0, y: 0 }; + + for (var x = 0; x < 32; x += wavePixelChunk) + { + copyPoint.x = x; + copyPoint.y = waveSize + (waveSize / 2) + waveData.sin[s]; + + bmd.context.drawImage(game.cache.getImage('ball'), copyRect.x, copyRect.y, copyRect.w, copyRect.h, copyPoint.x, copyPoint.y, copyRect.w, copyRect.h); + + copyRect.x += wavePixelChunk; + + s++; + } + + // Now all the pixel data has been redrawn we render it to the BitmapData object. + // In CANVAS mode this doesn't do anything, but on WebGL it pushes the new texture to the GPU. + // If your game is exclusively running under Canvas you ca safely ignore this step. + bmd.render(); + + // Cycle through the wave data - this is what causes the image to "undulate" + game.math.shift(waveData.sin); + + waveDataCounter++; + + if (waveDataCounter == waveData.length) + { + waveDataCounter = 0; + } +} diff --git a/examples/tilemaps/sci fly.js b/examples/tilemaps/sci fly.js index fb2ea411..76b599c1 100644 --- a/examples/tilemaps/sci fly.js +++ b/examples/tilemaps/sci fly.js @@ -52,6 +52,7 @@ function create() { sprite = game.add.sprite(450, 80, 'phaser'); sprite.anchor.setTo(0.5, 0.5); + sprite.angle = 45; game.camera.follow(sprite); // game.camera.deadzone = new Phaser.Rectangle(160, 160, layer.renderWidth-320, layer.renderHeight-320); @@ -111,7 +112,7 @@ function update() { function render() { - // game.debug.renderSpriteCorners(sprite); + game.debug.renderSpriteBounds(sprite); // game.debug.renderSpriteInfo(sprite, 32, 32); // game.debug.renderSpriteCoords(sprite, 32, 32); diff --git a/examples/wip/bmd.js b/examples/wip/bmd.js new file mode 100644 index 00000000..dda21b51 --- /dev/null +++ b/examples/wip/bmd.js @@ -0,0 +1,82 @@ + +var game = new Phaser.Game(800, 600, Phaser.AUTO, 'phaser-example', { preload: preload, create: create, update: update, render: render }); + +function preload() { + + game.load.image('atari1', 'assets/sprites/atari130xe.png'); + game.load.image('coke', 'assets/sprites/cokecan.png'); + game.load.image('mushroom', 'assets/sprites/mushroom2.png'); + game.load.image('ball', 'assets/sprites/shinyball.png'); + +} + +var bmd; + +function create() { + + bmd = game.add.bitmapData('ball', 32, 64); + + console.log(bmd); + + // And apply it to 100 randomly positioned sprites + for (var i = 0; i < 100; i++) + { + game.add.sprite(game.world.randomX - 32, game.world.randomY - 64, bmd); + } + + // Populate the wave with some data + waveData = game.math.sinCosGenerator(32, 8, 8, 2); + +} + +function update() { + + bmd.clear(); + + updateWobblyBall(); + +} + +// This creates a simple sine-wave effect running through our DynamicTexture. +// This is then duplicated across all sprites using it, meaning we only have to calculate it once. + +var waveSize = 8; +var wavePixelChunk = 2; +var waveData; +var waveDataCounter; + +function updateWobblyBall() +{ + var s = 0; + var copyRect = { x: 0, y: 0, w: wavePixelChunk, h: 32 }; + var copyPoint = { x: 0, y: 0 }; + + for (var x = 0; x < 32; x += wavePixelChunk) + { + copyPoint.x = x; + copyPoint.y = waveSize + (waveSize / 2) + waveData.sin[s]; + + bmd.context.drawImage(game.cache.getImage('ball'), copyRect.x, copyRect.y, copyRect.w, copyRect.h, copyPoint.x, copyPoint.y, copyRect.w, copyRect.h); + + copyRect.x += wavePixelChunk; + + s++; + } + + // Cycle through the wave data - this is what causes the image to "undulate" + game.math.shift(waveData.sin); + + waveDataCounter++; + + if (waveDataCounter == waveData.length) + { + waveDataCounter = 0; + } +} + + +function render() { + + bmd.render(); + +} diff --git a/examples/wip/bmd2.js b/examples/wip/bmd2.js new file mode 100644 index 00000000..9f7d6a45 --- /dev/null +++ b/examples/wip/bmd2.js @@ -0,0 +1,47 @@ + +var game = new Phaser.Game(800, 600, Phaser.AUTO, 'phaser-example', { preload: preload, create: create, update: update, render: render }); + +function preload() { + + game.load.image('atari1', 'assets/sprites/atari130xe.png'); + game.load.image('coke', 'assets/sprites/cokecan.png'); + game.load.image('mushroom', 'assets/sprites/mushroom2.png'); + game.load.image('ball', 'assets/sprites/shinyball.png'); + +} + +var bmd; + +function create() { + + //game.stage.backgroundColor = '#450034'; + + bmd = game.add.bitmapData('bob', 800, 600); + + // And apply it to 100 randomly positioned sprites + for (var i = 0; i < 100; i++) + { + bmd.setPixel(game.world.randomX, game.world.randomY, Math.random() * 255, Math.random() * 255, 255); + } + + bmd.context.fillStyle = '#ffffff'; + bmd.context.fillRect(20,20,16,16); + + var d = game.add.sprite(0, 0, bmd); + +} + +function update() { + + // bmd.clear(); + + // updateWobblyBall(); + +} + + +function render() { + + // bmd.render(); + +} diff --git a/examples/wip/index.php b/examples/wip/index.php index 5f7a6174..3db7c03a 100644 --- a/examples/wip/index.php +++ b/examples/wip/index.php @@ -77,7 +77,6 @@ phaser - +* @copyright 2013 Photon Storm Ltd. +* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License} +*/ + +/** +* Creates a new `BitmapData` object. +* +* @class Phaser.BitmapData +* @constructor +* @param {Phaser.Game} game - A reference to the currently running game. +* @param {string} [key] - A key the BitmapData will use when added to the Phaser.Cache. If none is given a UUID is generated. +* @param {number} [width=256] - The width of the BitmapData in pixels. +* @param {number} [height=256] - The height of the BitmapData in pixels. +*/ +Phaser.BitmapData = function (game, key, width, height) { + + if (typeof key === 'undefined') { key = 'bitmapData' . game.rnd.uuid(); } + if (typeof width === 'undefined') { width = 256; } + if (typeof height === 'undefined') { height = 256; } + + /** + * @property {Phaser.Game} game - A reference to the currently running game. + */ + this.game = game; + + /** + * @property {string} name - The name of the BitmapData. + */ + this.name = key; + + /** + * @property {number} width - The width of the BitmapData in pixels. + */ + this.width = width; + + /** + * @property {number} height - The height of the BitmapData in pixels. + */ + this.height = height; + + /** + * @property {HTMLCanvasElement} canvas - The canvas to which this BitmapData draws. + * @default + */ + this.canvas = Phaser.Canvas.create(width, height); + + /** + * @property {HTMLCanvasContextElement} context - The 2d context of the canvas. + * @default + */ + this.context = this.canvas.getContext('2d'); + + /** + * @property {array} imageData - The canvas image data. + */ + this.imageData = this.context.getImageData(0, 0, width, height); + + /** + * @property {ArrayBuffer} buffer - A TypedArray storing the canvas image data. + * TODO: = (typeof Float32Array !== 'undefined') ? Float32Array : Array; + */ + this.buffer = new ArrayBuffer(this.imageData.data.length); + + /** + * @property {Uint8ClampedArray} buffer - A uint8 clamped view on the buffer. + */ + this.data8 = new Uint8ClampedArray(this.buffer); + + /** + * @property {Uint32Array} buffer - A Uint32 view on the buffer. + */ + this.data32 = new Uint32Array(this.buffer); + + // Little or big-endian? + this.data32[1] = 0x0a0b0c0d; + + /** + * @property {boolean} isLittleEndian - . + */ + this.isLittleEndian = true; + + if (this.data32[4] === 0x0a && this.data32[5] === 0x0b && this.data32[6] === 0x0c && this.data32[7] === 0x0d) + { + this.isLittleEndian = false; + } + + /** + * @property {PIXI.BaseTexture} baseTexture - The PIXI.BaseTexture. + * @default + */ + this.baseTexture = new PIXI.BaseTexture(this.canvas); + + /** + * @property {PIXI.Texture} texture - The PIXI.Texture. + * @default + */ + this.texture = new PIXI.Texture(this.baseTexture); + + /** + * @property {Phaser.Frame} textureFrame - The Frame this BitmapData uses for rendering. + * @default + */ + this.textureFrame = new Phaser.Frame(0, 0, 0, width, height, 'bitmapData', game.rnd.uuid()); + + /** + * @property {number} type - The const type of this object. + * @default + */ + this.type = Phaser.BITMAPDATA; + + /** + * You can set a globalCompositeOperation that will be applied to this BitmapData. + * This is useful if you wish to apply an effect like 'lighten'. + * If this value is set it will call a canvas context save and restore before and after the render pass, so use it sparingly. + * Set to null to disable. + * @property {string} globalCompositeOperation + * @default + */ + this.globalCompositeOperation = null; + +} + +Phaser.BitmapData.prototype = { + + clear: function () { + + this.context.clearRect(0, 0, this.width, this.height); + + }, + + /** + * Sets the color of the given pixel. + * @param {number} x - The X coordinate of the pixel to be set. + * @param {number} y - The Y coordinate of the pixel to be set. + * @param {number} red - The red color value (between 0 and 255) + * @param {number} green - The green color value (between 0 and 255) + * @param {number} blue - The blue color value (between 0 and 255) + * @param {number} alpha - The alpha color value (between 0 and 255) + */ + setPixel32: function (x, y, red, green, blue, alpha) { + + if (x >= 0 && x <= this.width && y >= 0 && y <= this.height) + { + var value = x * y & 0xff; + + if (this.isLittleEndian) + { + this.data32[y * this.width + x] = (alpha << 24) | (blue << 16) | (green << 8) | red; + } + else + { + this.data32[y * this.width + x] = (red << 24) | (green << 16) | (blue << 8) | alpha; + } + + this.imageData.data.set(this.data8); + + this.context.putImageData(this.imageData, 0, 0); + } + + }, + + /** + * Sets the color of the given pixel. + * @param {number} x - The X coordinate of the pixel to be set. + * @param {number} y - The Y coordinate of the pixel to be set. + * @param {number} red - The red color value (between 0 and 255) + * @param {number} green - The green color value (between 0 and 255) + * @param {number} blue - The blue color value (between 0 and 255) + * @param {number} alpha - The alpha color value (between 0 and 255) + */ + setPixel: function (x, y, red, green, blue) { + + this.setPixel32(x, y, red, green, blue, 255); + + }, + + /** + * Get a color of a specific pixel. + * @param x {number} X position of the pixel in this texture. + * @param y {number} Y position of the pixel in this texture. + * @return {number} A native color value integer (format: 0xRRGGBB) + */ + getPixel: function (x, y) { + + if (x >= 0 && x <= this.width && y >= 0 && y <= this.height) + { + if (this.isLittleEndian) + { + } + else + { + } + } + + //r = imageData.data[0]; + //g = imageData.data[1]; + //b = imageData.data[2]; + //a = imageData.data[3]; + // var imageData = this.context.getImageData(x, y, 1, 1); + + // return Phaser.ColorUtils.getColor(imageData.data[0], imageData.data[1], imageData.data[2]); + + }, + + /** + * Get a color of a specific pixel (including alpha value). + * @param x {number} X position of the pixel in this texture. + * @param y {number} Y position of the pixel in this texture. + * @return A native color value integer (format: 0xAARRGGBB) + */ + getPixel32: function (x, y) { + + // var imageData = this.context.getImageData(x, y, 1, 1); + + // return Phaser.ColorUtils.getColor32(imageData.data[3], imageData.data[0], imageData.data[1], imageData.data[2]); + + }, + + /** + * Get pixels in array in a specific Rectangle. + * @param rect {Rectangle} The specific Rectangle. + * @returns {array} CanvasPixelArray. + */ + getPixels: function (rect) { + + // return this.context.getImageData(rect.x, rect.y, rect.width, rect.height); + + }, + + render: function () { + + // Only needed if running in WebGL, otherwise this array will never get cleared down I don't think! + if (this.game.renderType == Phaser.WEBGL) + { + PIXI.texturesToUpdate.push(this.baseTexture); + } + + } + +} + diff --git a/src/gameobjects/GameObjectFactory.js b/src/gameobjects/GameObjectFactory.js index 9f1fd101..c753018f 100644 --- a/src/gameobjects/GameObjectFactory.js +++ b/src/gameobjects/GameObjectFactory.js @@ -274,6 +274,23 @@ Phaser.GameObjectFactory.prototype = { return texture; + }, + + /** + * A BitmapData object which can be manipulated and drawn to like a traditional Canvas object and used to texture Sprites. + * + * @method Phaser.GameObjectFactory#bitmapData + * @param {string} [key] - A key the BitmapData will use when added to the Phaser.Cache. If none is given a UUID is generated. + * @param {number} [width=256] - The width of the BitmapData in pixels. + * @param {number} [height=256] - The height of the BitmapData in pixels. + * @return {Phaser.BitmapData} The newly created BitmapData object. + */ + bitmapData: function (key, width, height) { + + var bmd = new Phaser.BitmapData(this.game, key, width, height); + + return this.game.cache.addBitmapData(bmd.name, bmd); + } }; \ No newline at end of file diff --git a/src/gameobjects/Sprite.js b/src/gameobjects/Sprite.js index d95b03b2..30c324a1 100644 --- a/src/gameobjects/Sprite.js +++ b/src/gameobjects/Sprite.js @@ -15,7 +15,7 @@ * @param {Phaser.Game} game - A reference to the currently running game. * @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 {string|Phaser.RenderTexture|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 or PIXI.Texture. +* @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 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. */ Phaser.Sprite = function (game, x, y, key, frame) { @@ -89,7 +89,7 @@ Phaser.Sprite = function (game, x, y, key, frame) { this.input = new Phaser.InputHandler(this); /** - * @property {string|Phaser.RenderTexture|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 or PIXI.Texture. + * @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. */ this.key = key; @@ -104,6 +104,12 @@ Phaser.Sprite = function (game, x, y, key, frame) { this.currentFrame = this.game.cache.getTextureFrame(key.name); } + else if (key instanceof Phaser.BitmapData) + { + PIXI.Sprite.call(this, key.texture, key.textureFrame); + + this.currentFrame = key.textureFrame; + } else if (key instanceof PIXI.Texture) { PIXI.Sprite.call(this, key); diff --git a/src/loader/Cache.js b/src/loader/Cache.js index 90cb2af2..764666f5 100644 --- a/src/loader/Cache.js +++ b/src/loader/Cache.js @@ -62,6 +62,12 @@ Phaser.Cache = function (game) { */ this._tilesets = {}; + /** + * @property {object} _bitmapDatas - BitmapData key-value container. + * @private + */ + this._bitmapDatas = {}; + this.addDefaultImage(); /** @@ -86,12 +92,27 @@ Phaser.Cache.prototype = { }, + /** + * Add a BitmapData object in to the cache. + * @method Phaser.Cache#addBitmapData + * @param {string} key - Asset key for this BitmapData. + * @param {Phaser.BitmapData} bitmapData - The BitmapData object to be addded to the cache. + * @return {Phaser.BitmapData} The BitmapData object to be addded to the cache. + */ + addBitmapData: function (key, bitmapData) { + + this._bitmapDatas[key] = bitmapData; + + return bitmapData; + + }, + /** * Add a new Phaser.RenderTexture in to the cache. * * @method Phaser.Cache#addRenderTexture * @param {string} key - The unique key by which you will reference this object. - * @param {Phaser.Texture} textue - The texture to use as the base of the RenderTexture. + * @param {Phaser.Texture} texture - The texture to use as the base of the RenderTexture. */ addRenderTexture: function (key, texture) { @@ -364,7 +385,7 @@ Phaser.Cache.prototype = { }, /** - * Get acanvas object from the cache by its key. + * Get a canvas object from the cache by its key. * * @method Phaser.Cache#getCanvas * @param {string} key - Asset key of the canvas you want. @@ -378,6 +399,25 @@ Phaser.Cache.prototype = { } return null; + + }, + + /** + * Get a BitmapData object from the cache by its key. + * + * @method Phaser.Cache#getBitmapData + * @param {string} key - Asset key of the BitmapData object you want. + * @return {Phaser.BitmapData} The requested BitmapData object if found, or null if not. + */ + getBitmapData: function (key) { + + if (this._bitmapDatas[key]) + { + return this._bitmapDatas[key]; + } + + return null; + }, /** @@ -413,6 +453,7 @@ Phaser.Cache.prototype = { } return null; + }, /** diff --git a/src/math/Math.js b/src/math/Math.js index 086d622d..45e3cb10 100644 --- a/src/math/Math.js +++ b/src/math/Math.js @@ -938,7 +938,7 @@ Phaser.Math = { } - return { sin: sinTable, cos: cosTable }; + return { sin: sinTable, cos: cosTable, length: length }; }, diff --git a/src/tilemap/TilemapLayer.js b/src/tilemap/TilemapLayer.js index 6a5c6747..1d6b014b 100644 --- a/src/tilemap/TilemapLayer.js +++ b/src/tilemap/TilemapLayer.js @@ -249,52 +249,75 @@ Phaser.TilemapLayer.prototype.updateMapData = function (tilemap, layer) { * @param {number} x - x coordinate in camera space * @return {number} x coordinate in scrollFactor-adjusted dimensions */ -Phaser.TilemapLayer.prototype._fixX = function( x ) -{ - if( this.scrollFactorX === 1 ) - return x; +Phaser.TilemapLayer.prototype._fixX = function(x) { + + if (this.scrollFactorX === 1) + { + return x; + } + var left_edge = x - (this._x / this.scrollFactorX); + return this._x + left_edge; -}; + +} + /** * Take an x coordinate that _does_ account for scrollFactorY and 'unfix' it * back to camera space. Used primarily internally * @param {number} x - x coordinate in scrollFactor-adjusted dimensions * @return {number} x coordinate in camera space */ -Phaser.TilemapLayer.prototype._unfixX = function( x ) -{ - if( this.scrollFactorX === 1 ) - return x; +Phaser.TilemapLayer.prototype._unfixX = function(x) { + + if (this.scrollFactorX === 1) + { + return x; + } + var left_edge = x - this._x; + return (this._x / this.scrollFactorX) + left_edge; -}; + +} + /** * Take a y coordinate that doesn't account for scrollFactorY and 'fix' it * into a scrolled local space. Used primarily internally * @param {number} y - y coordinate in camera space * @return {number} y coordinate in scrollFactor-adjusted dimensions */ -Phaser.TilemapLayer.prototype._fixY = function( y ) -{ - if( this.scrollFactorY === 1 ) - return y; +Phaser.TilemapLayer.prototype._fixY = function(y) { + + if (this.scrollFactorY === 1) + { + return y; + } + var top_edge = y - (this._y / this.scrollFactorY); + return this._y + top_edge; -}; + +} + /** * Take a y coordinate that _does_ account for scrollFactorY and 'unfix' it * back to camera space. Used primarily internally * @param {number} y - y coordinate in scrollFactor-adjusted dimensions * @return {number} y coordinate in camera space */ -Phaser.TilemapLayer.prototype._unfixY = function( y ) -{ - if( this.scrollFactorY === 1 ) - return y; +Phaser.TilemapLayer.prototype._unfixY = function(y) { + + if (this.scrollFactorY === 1) + { + return y; + } + var top_edge = y - this._y; + return (this._y / this.scrollFactorY) + top_edge; -}; + +} /** * Convert a pixel value to a tile coordinate.