From 6a24d6116b54c44bf9c79036fe7caf009c265869 Mon Sep 17 00:00:00 2001 From: Richard Davey Date: Wed, 13 Nov 2013 06:49:24 +0000 Subject: [PATCH] Lots of renderTexture updates and examples added --- README.md | 19 +- examples/display/render texture mirror.js | 35 +++ examples/display/render texture starfield.js | 81 ++++++ examples/display/render texture trail.js | 34 +++ examples/input/cursor key movement.js | 2 +- examples/wip/rendertexture1.js | 38 +++ examples/world/fixed to camera.js | 2 +- examples/world/move around world.js | 2 +- src/core/StateManager.js | 11 + src/gameobjects/Graphics.js | 9 +- src/gameobjects/RenderTexture.js | 260 ++++++++++++++++++- src/gameobjects/Sprite.js | 2 +- src/geom/Point.js | 2 +- src/input/Pointer.js | 33 +-- 14 files changed, 486 insertions(+), 44 deletions(-) create mode 100644 examples/display/render texture mirror.js create mode 100644 examples/display/render texture starfield.js create mode 100644 examples/display/render texture trail.js create mode 100644 examples/wip/rendertexture1.js diff --git a/README.md b/README.md index c45e4246..84a35d9b 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,10 @@ Change Log Version 1.1.3 - in build +* 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) +* New: Phaser.Game parent can now be a HTMLElement (thanks beeglebug) * New: Updated to use the latest version of Pixi.js - which means you can now use all the sexy new WebGL filters they added :) * New: Sprite.animations.getAnimation will return an animation instance which was added by name. * New: Added Mouse.button which is set to the button that was pressed: Phaser.Mouse.LEFT_BUTTON, MIDDLE_BUTTON or RIGHT_BUTTON (thanks wKLV) @@ -50,14 +54,23 @@ Version 1.1.3 - in build * New: Polygon class & drawPolygon method added to Graphics (thanks rjimenezda) * New: Added Group.iterate, a powerful way to count or return child that match a certain criteria. Refactored Group to use iterate, lots of repeated code cut. * New: Added Group.sort. You can now sort the Group based on any given numeric property (x, y, health), finally you can do depth-sorting :) Example created to show. +* New: Enhanced renderTexture so it can accept a Phaser.Group object and improved documentation and examples. +* Fixed: Tilemap commands use specified layer when one given (thanks Izzimach) * Fixed: Mouse.stop now uses the true useCapture, which means the event listeners stop listening correctly (thanks beeglebug) * Fixed: Input Keyboard example fix (thanks Atrodilla) * Fixed: BitmapText.destroy now checks if it has a canvas before calling parentNode on it. * Fixed: Group.swap had a hellish to find bug that only manifested when B-A upward swaps occured. Hours of debugging later = bug crushed. +* Fixed: Point.rotate asDegrees fixed (thanks BorisKozo) * Updated: ArcadePhysics.updateMotion applies the dt to the velocity calculations as well as position now (thanks jcs) * Updated: RequestAnimationFrame now retains the callbackID which is passed to cancelRequestAnimationFrame. * Updated: Button now goes back to over state when setFrames used in action (thanks beeglebug) * Updated: plugins now have a postUpdate callback (thanks cocoademon) +* Updated: Tided up the Graphics object (thanks BorisKozo) +* 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. + + You can view the complete Change Log for all previous versions at https://github.com/photonstorm/phaser/changelog.md @@ -184,12 +197,14 @@ Learn By Example Phaser comes with an ever growing suite of Examples. Personally I feel that we learn better by looking at small refined code examples, so we created over 150 of them and create new ones to test every new feature added. Inside the `examples` folder you'll find the current set. If you write a particularly good example then please send it to us. -The examples need running through a local web server (to avoid file access permission errors from your browser). +The examples need to be run through a local web server (in order to avoid file access permission errors from your browser). You can use your own web server, or start the included web server using grunt. -Browse to the examples folder via your web server. +Using a locally installed web server browse to the examples folder: examples/index.html +Alternatively in order to start the included web server, after you've cloned the repo, run npm install to install all dependencies, then run grunt connect to start a local server. After running this command, you should be able to access your local webserver at `http://127.0.0.1:8000`. Then browse to the examples folder: `http://127.0.0.1:8000/examples/index.html` + There is a new 'Side View' example viewer as well. This loads all the examples into a left-hand frame for faster navigation. You can also browse all [Phaser Examples](http://gametest.mobi/phaser/) online. diff --git a/examples/display/render texture mirror.js b/examples/display/render texture mirror.js new file mode 100644 index 00000000..9517924c --- /dev/null +++ b/examples/display/render texture mirror.js @@ -0,0 +1,35 @@ + +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/pangball.png'); + +} + +var ball; +var texture; + +function create() { + + // Here we'll create a renderTexture the same size as our game + texture = game.add.renderTexture('mousetrail', 800, 600); + + // This is the sprite that will be drawn to the texture, we set it to visible false as we only need its texture data + ball = game.add.sprite(0, 0, 'ball'); + ball.visible = false; + ball.anchor.setTo(0.5, 0.5); + + // This is the sprite that is drawn to the display. We've given it the renderTexture as its texture. + game.add.sprite(0, 0, texture); + +} + +function update() { + + // This time we'll draw the ball sprite twice, in a mirror effect + + texture.renderXY(ball, game.input.activePointer.x, game.input.activePointer.y, false); + texture.renderXY(ball, game.input.activePointer.x, 600 - game.input.activePointer.y, false); + +} diff --git a/examples/display/render texture starfield.js b/examples/display/render texture starfield.js new file mode 100644 index 00000000..43ab5186 --- /dev/null +++ b/examples/display/render texture starfield.js @@ -0,0 +1,81 @@ + +var game = new Phaser.Game(800, 600, Phaser.AUTO, 'phaser-example', { preload: preload, create: create, update: update }); + +function preload() { + + game.load.image('star', 'assets/sprites/bullet.png'); + +} + +var star; +var texture1; +var texture2; +var texture3; +var stars = []; + +function create() { + + // This is the sprite that will be drawn to the texture, we set it to visible false as we only need its texture data. + star = game.add.sprite(0, 0, 'star'); + star.visible = false; + + // For this effect we'll create a vertical scrolling starfield with 300 stars split across 3 layers. + // This will use only 3 textures / sprites in total. + texture1 = game.add.renderTexture('texture1', 800, 600); + texture2 = game.add.renderTexture('texture2', 800, 600); + texture3 = game.add.renderTexture('texture3', 800, 600); + + game.add.sprite(0, 0, texture1); + game.add.sprite(0, 0, texture2); + game.add.sprite(0, 0, texture3); + + var t = texture1; + var s = 4; + + // 100 sprites per layer + for (var i = 0; i < 300; i++) + { + if (i == 100) + { + // With each 100 stars we ramp up the speed a little and swap to the next texture + s = 6; + t = texture2; + } + else if (i == 200) + { + s = 7; + t = texture3; + } + + stars.push( { x: game.world.randomX, y: game.world.randomY, speed: s, texture: t }); + } + +} + +function update() { + + for (var i = 0; i < 300; i++) + { + // Update the stars y position based on its speed + stars[i].y += stars[i].speed; + + if (stars[i].y > 600) + { + // Off the bottom of the screen? Then wrap around to the top + stars[i].x = game.world.randomX; + stars[i].y = -32; + } + + if (i == 0 || i == 100 || i == 200) + { + // If it's the first star of the layer then we clear the texture + stars[i].texture.renderXY(star, stars[i].x, stars[i].y, true); + } + else + { + // Otherwise just draw the star sprite where we need it + stars[i].texture.renderXY(star, stars[i].x, stars[i].y, false); + } + } + +} diff --git a/examples/display/render texture trail.js b/examples/display/render texture trail.js new file mode 100644 index 00000000..b0c114f8 --- /dev/null +++ b/examples/display/render texture trail.js @@ -0,0 +1,34 @@ + +var game = new Phaser.Game(800, 600, Phaser.AUTO, 'phaser-example', { preload: preload, create: create, update: update }); + +function preload() { + + game.load.image('mushroom', 'assets/sprites/mushroom2.png'); + +} + +var mushroom; +var texture; + +function create() { + + // Here we'll create a renderTexture the same size as our game + texture = game.add.renderTexture('mousetrail', 800, 600); + + // This is the sprite that will be drawn to the texture, we set it to visible false as we only need its texture data + mushroom = game.add.sprite(0, 0, 'mushroom'); + mushroom.visible = false; + mushroom.anchor.setTo(0.5, 0.5); + + // This is the sprite that is drawn to the display. We've given it the renderTexture as its texture. + game.add.sprite(0, 0, texture); + +} + +function update() { + + // Here we draw the mushroom sprite to the renderTexture at the pointer coordinates. + // The 'false' parameter at the end tells it not to clear itself, causing the trail effect you see. + texture.render(mushroom, game.input.activePointer.position, false); + +} diff --git a/examples/input/cursor key movement.js b/examples/input/cursor key movement.js index 2174bc11..a95e9eda 100644 --- a/examples/input/cursor key movement.js +++ b/examples/input/cursor key movement.js @@ -15,7 +15,7 @@ var cursors; function create() { // Modify the world and camera bounds - game.world.setBounds(-1000, -1000, 2000, 2000); + game.world.setBounds(-1000, -1000, 1000, 1000); for (var i = 0; i < 100; i++) { diff --git a/examples/wip/rendertexture1.js b/examples/wip/rendertexture1.js new file mode 100644 index 00000000..7ddf2305 --- /dev/null +++ b/examples/wip/rendertexture1.js @@ -0,0 +1,38 @@ + +var game = new Phaser.Game(800, 600, Phaser.AUTO, 'phaser-example', { preload: preload, create: create, update: update }); + +function preload() { + + game.load.spritesheet('veggies', 'assets/sprites/fruitnveg32wh37.png', 32, 32); + +} + +var group; +var texture; + +function create() { + + group = game.add.group(); + group.create(0, 0, 'veggies', 0); + group.create(32, 0, 'veggies', 1); + group.create(0, 32, 'veggies', 2); + group.create(32, 32, 'veggies', 3); + group.visible = false; + + texture = game.add.renderTexture('texture', 800, 600); + + game.add.sprite(0, 0, texture); + +} + +function update() { + + var clear = false; + + for (var i = 0; i < 60; i++) + { + clear = (i == 0); + texture.renderXY(group, game.world.randomX, game.world.randomY, clear); + } + +} diff --git a/examples/world/fixed to camera.js b/examples/world/fixed to camera.js index a49b4e7c..54ee0372 100644 --- a/examples/world/fixed to camera.js +++ b/examples/world/fixed to camera.js @@ -18,7 +18,7 @@ var logo2; function create() { // Modify the world and camera bounds - game.world.setBounds(-1000, -1000, 2000, 2000); + game.world.setBounds(-1000, -1000, 1000, 1000); for (var i = 0; i < 200; i++) { diff --git a/examples/world/move around world.js b/examples/world/move around world.js index 52573551..10049ad0 100644 --- a/examples/world/move around world.js +++ b/examples/world/move around world.js @@ -16,7 +16,7 @@ var d; function create() { // Modify the world and camera bounds - game.world.setBounds(-1000, -1000, 2000, 2000); + game.world.setBounds(-1000, -1000, 1000, 1000); for (var i = 0; i < 100; i++) { diff --git a/src/core/StateManager.js b/src/core/StateManager.js index 87ae6a71..bb0058a9 100644 --- a/src/core/StateManager.js +++ b/src/core/StateManager.js @@ -484,7 +484,18 @@ Phaser.StateManager.prototype = { if (this._created && this.onRenderCallback) { + if (this.game.renderType === Phaser.CANVAS) + { + this.game.context.save(); + this.game.context.setTransform(1, 0, 0, 1, 0, 0); + } + this.onRenderCallback.call(this.callbackContext, this.game); + + if (this.game.renderType === Phaser.CANVAS) + { + this.game.context.restore(); + } } else { diff --git a/src/gameobjects/Graphics.js b/src/gameobjects/Graphics.js index a164dd71..98bb2b9a 100644 --- a/src/gameobjects/Graphics.js +++ b/src/gameobjects/Graphics.js @@ -25,6 +25,9 @@ Phaser.Graphics = function (game, x, y) { */ this.type = Phaser.GRAPHICS; + this.position.x = x; + this.position.y = y; + }; Phaser.Graphics.prototype = Object.create(PIXI.Graphics.prototype); @@ -56,10 +59,14 @@ Phaser.Graphics.prototype.destroy = function() { Phaser.Graphics.prototype.drawPolygon = function (poly) { graphics.moveTo(poly.points[0].x, poly.points[0].y); - for (var i = 1; i < poly.points.length; i += 1) { + + for (var i = 1; i < poly.points.length; i += 1) + { graphics.lineTo(poly.points[i].x, poly.points[i].y); } + graphics.lineTo(poly.points[0].x, poly.points[0].y); + } Object.defineProperty(Phaser.Graphics.prototype, 'angle', { diff --git a/src/gameobjects/RenderTexture.js b/src/gameobjects/RenderTexture.js index 597fd700..7b7aa34c 100644 --- a/src/gameobjects/RenderTexture.js +++ b/src/gameobjects/RenderTexture.js @@ -5,7 +5,7 @@ */ /** -* A dynamic initially blank canvas to which images can be drawn +* A RenderTexture is a special texture that allows any displayObject to be rendered to it. * @class Phaser.RenderTexture * @constructor * @param {Phaser.Game} game - Current game instance. @@ -25,7 +25,7 @@ Phaser.RenderTexture = function (game, key, width, height) { */ this.name = key; - PIXI.EventTarget.call( this ); + PIXI.EventTarget.call(this); /** * @property {number} width - the width. @@ -38,22 +38,22 @@ Phaser.RenderTexture = function (game, key, width, height) { this.height = height || 100; /** - * I know this has a typo in it, but it's because the PIXI.RenderTexture does and we need to pair-up with it - * once they update pixi to fix the typo, we'll fix it here too :) - * @property {Description} indetityMatrix - Description. + * @property {PIXI.mat3} indetityMatrix - Matrix object. */ this.indetityMatrix = PIXI.mat3.create(); /** - * @property {Description} frame - Description. + * @property {PIXI.Rectangle} frame - The frame for this texture. */ this.frame = new PIXI.Rectangle(0, 0, this.width, this.height); /** - * @property {Description} type - Description. + * @property {number} type - Base Phaser object type. */ this.type = Phaser.RENDERTEXTURE; + this._tempPoint = { x: 0, y: 0 }; + if (PIXI.gl) { this.initWebGL(); @@ -65,5 +65,247 @@ Phaser.RenderTexture = function (game, key, width, height) { }; -Phaser.RenderTexture.prototype = Phaser.Utils.extend(true, PIXI.RenderTexture.prototype); -Phaser.RenderTexture.prototype.constructor = Phaser.RenderTexture; +Phaser.RenderTexture.prototype = Object.create(PIXI.Texture.prototype); +Phaser.RenderTexture.prototype.constructor = PIXI.RenderTexture; + +/** +* This function will draw the display object to the texture. If the display object is a Group or has children it will +* draw all children as well. +* +* @method render +* @param {DisplayObject} displayObject - The display object to render this texture on. +* @param {Phaser.Point} [position] - Where to draw the display object. +* @param {boolean} [clear=false] - If true the texture will be cleared before the displayObject is drawn. +*/ +Phaser.RenderTexture.prototype.render = function(displayObject, position, clear) { + + if (typeof position === 'undefined') { position = false; } + if (typeof clear === 'undefined') { clear = false; } + + if (displayObject instanceof Phaser.Group) + { + displayObject = displayObject._container; + } + + if (PIXI.gl) + { + this.renderWebGL(displayObject, position, clear); + } + else + { + this.renderCanvas(displayObject, position, clear); + } + +} + +/** +* This function will draw the display object to the texture at the given x/y coordinates. +* If the display object is a Group or has children it will draw all children as well. +* +* @method renderXY +* @param {DisplayObject} displayObject - The display object to render this texture on. +* @param {number} x - The x coordinate to draw the display object at. +* @param {number} y - The y coordinate to draw the display object at. +* @param {boolean} [clear=false] - If true the texture will be cleared before the displayObject is drawn. +*/ +Phaser.RenderTexture.prototype.renderXY = function(displayObject, x, y, clear) { + + this._tempPoint.x = x; + this._tempPoint.y = y; + + this.render(displayObject, this._tempPoint, clear); + +} + +/** + * Initializes the webgl data for this texture + * + * @method initWebGL + * @private + */ +Phaser.RenderTexture.prototype.initWebGL = function() +{ + var gl = PIXI.gl; + this.glFramebuffer = gl.createFramebuffer(); + + gl.bindFramebuffer(gl.FRAMEBUFFER, this.glFramebuffer ); + + this.glFramebuffer.width = this.width; + this.glFramebuffer.height = this.height; + + this.baseTexture = new PIXI.BaseTexture(); + + this.baseTexture.width = this.width; + this.baseTexture.height = this.height; + + this.baseTexture._glTexture = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, this.baseTexture._glTexture); + + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, this.width, this.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + + this.baseTexture.isRender = true; + + gl.bindFramebuffer(gl.FRAMEBUFFER, this.glFramebuffer ); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.baseTexture._glTexture, 0); + + // create a projection matrix.. + this.projection = new PIXI.Point(this.width/2 , -this.height/2); + + // set the correct render function.. + // this.render = this.renderWebGL; +} + + +Phaser.RenderTexture.prototype.resize = function(width, height) +{ + + this.width = width; + this.height = height; + + if(PIXI.gl) + { + this.projection.x = this.width/2 + this.projection.y = -this.height/2; + + var gl = PIXI.gl; + gl.bindTexture(gl.TEXTURE_2D, this.baseTexture._glTexture); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, this.width, this.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + } + else + { + + this.frame.width = this.width + this.frame.height = this.height; + this.renderer.resize(this.width, this.height); + } +} + +/** + * Initializes the canvas data for this texture + * + * @method initCanvas + * @private + */ +Phaser.RenderTexture.prototype.initCanvas = function() +{ + this.renderer = new PIXI.CanvasRenderer(this.width, this.height, null, 0); + + this.baseTexture = new PIXI.BaseTexture(this.renderer.view); + this.frame = new PIXI.Rectangle(0, 0, this.width, this.height); + + // this.render = this.renderCanvas; +} + +/** + * This function will draw the display object to the texture. + * + * @method renderWebGL + * @param displayObject {DisplayObject} The display object to render this texture on + * @param clear {Boolean} If true the texture will be cleared before the displayObject is drawn + * @private + */ +Phaser.RenderTexture.prototype.renderWebGL = function(displayObject, position, clear) +{ + var gl = PIXI.gl; + + // enable the alpha color mask.. + gl.colorMask(true, true, true, true); + + gl.viewport(0, 0, this.width, this.height); + + gl.bindFramebuffer(gl.FRAMEBUFFER, this.glFramebuffer ); + + if(clear) + { + gl.clearColor(0,0,0, 0); + gl.clear(gl.COLOR_BUFFER_BIT); + } + + // THIS WILL MESS WITH HIT TESTING! + var children = displayObject.children; + + //TODO -? create a new one??? dont think so! + var originalWorldTransform = displayObject.worldTransform; + displayObject.worldTransform = PIXI.mat3.create();//sthis.indetityMatrix; + // modify to flip... + displayObject.worldTransform[4] = -1; + displayObject.worldTransform[5] = this.projection.y * -2; + + if(position) + { + displayObject.worldTransform[2] = position.x; + displayObject.worldTransform[5] -= position.y; + } + + PIXI.visibleCount++; + displayObject.vcount = PIXI.visibleCount; + + for(var i=0,j=children.length; i