UNPKG

phaser

Version:

A fast, free and fun HTML5 Game Framework for Desktop and Mobile web browsers from the team at Phaser Studio Inc.

697 lines (640 loc) 29.8 kB
/** * @author Richard Davey <rich@phaser.io> * @copyright 2013-2026 Phaser Studio Inc. * @license {@link https://opensource.org/licenses/MIT|MIT License} */ var Class = require('../../utils/Class'); var UUID = require('../../utils/string/UUID'); var Image = require('../image/Image'); var RenderTextureRender = require('./RenderTextureRender'); var RenderTextureRenderModes = require('./RenderTextureRenderModes'); /** * @classdesc * A Render Texture is a combination of Dynamic Texture and an Image Game Object, that uses the * Dynamic Texture to display itself with. * * A Dynamic Texture is a special texture that allows you to draw textures, frames and most kind of * Game Objects directly to it. * * You can take many complex objects and draw them to this one texture, which can then be used as the * base texture for other Game Objects, such as Sprites. Should you then update this texture, all * Game Objects using it will instantly be updated as well, reflecting the changes immediately. * * It's a powerful way to generate dynamic textures at run-time that are WebGL friendly and don't invoke * expensive GPU uploads on each change. * * In versions of Phaser before 3.60 a Render Texture was the only way you could create a texture * like this, that had the ability to be drawn on. But in 3.60 we split the core functions out to * the Dynamic Texture class as it made a lot more sense for them to reside in there. As a result, * the Render Texture is now a light-weight shim that sits on-top of an Image Game Object and offers * proxy methods to the features available from a Dynamic Texture. * * **When should you use a Render Texture vs. a Dynamic Texture?** * * You should use a Dynamic Texture if the texture is going to be used by multiple Game Objects, * or you want to use it across multiple Scenes, because textures are globally stored. * * You should use a Dynamic Texture if the texture isn't going to be displayed in-game, but is * instead going to be used for something like a mask or shader. * * You should use a Render Texture if you need to display the texture in-game on a single Game Object, * as it provides the convenience of wrapping an Image and Dynamic Texture together for you. * * Under WebGL1, a FrameBuffer, which is what this Dynamic Texture uses internally, cannot be anti-aliased. * This means that when drawing objects such as Shapes or Graphics instances to this texture, they may appear * to be drawn with no aliasing around the edges. This is a technical limitation of WebGL1. To get around it, * create your shape as a texture in an art package, then draw that to this texture. * * If you activate mipmap support in your game, it will not automatically * be applied to DynamicTextures. * This is because regenerating the mipmap for a texture * costs over 10 microseconds, a big performance loss for a single frame. * If you want to render your DynamicTextures with mipmaps, * you must also activate the render config option `mipmapRegeneration`. * * In the event that the WebGL context is lost, this DynamicTexture will * lose its contents. Once context is restored (signalled by the `restorewebgl` * event), you can choose to redraw the contents of the DynamicTexture. * You are responsible for the redrawing logic. * * @class RenderTexture * @extends Phaser.GameObjects.Image * @memberof Phaser.GameObjects * @constructor * @since 3.2.0 * * @param {Phaser.Scene} scene - The Scene to which this Game Object belongs. A Game Object can only belong to one Scene at a time. * @param {number} [x=0] - The horizontal position of this Game Object in the world. * @param {number} [y=0] - The vertical position of this Game Object in the world. * @param {number} [width=32] - The width of the Render Texture. * @param {number} [height=32] - The height of the Render Texture. * @param {boolean} [forceEven=true] - Force the given width and height to be rounded to even values. This significantly improves the rendering quality. Set to false if you know you need an odd sized texture. */ var RenderTexture = new Class({ Extends: Image, Mixins: [ RenderTextureRender ], initialize: function RenderTexture (scene, x, y, width, height, forceEven) { if (x === undefined) { x = 0; } if (y === undefined) { y = 0; } if (width === undefined) { width = 32; } if (height === undefined) { height = 32; } if (forceEven === undefined) { forceEven = true; } var dynamicTexture = scene.sys.textures.addDynamicTexture(UUID(), width, height, forceEven); Image.call(this, scene, x, y, dynamicTexture); this.type = 'RenderTexture'; /** * An internal Camera that can be used to move around this Render Texture. * * Control it just like you would any Scene Camera. The difference is that it only impacts * the placement of Game Objects that you then draw to this texture. * * You can scroll, zoom and rotate this Camera. * * This property is a reference to `RenderTexture.texture.camera`. * * @name Phaser.GameObjects.RenderTexture#camera * @type {Phaser.Cameras.Scene2D.BaseCamera} * @since 3.12.0 */ this.camera = this.texture.camera; /** * Internal saved texture flag. * * @name Phaser.GameObjects.RenderTexture#_saved * @type {boolean} * @private * @since 3.12.0 */ this._saved = false; /** * The render mode of this Render Texture. * Set this property to change how the Render Texture is rendered. * * - 'render' mode draws the contents of the Render Texture to each frame. * - 'redraw' mode calls `render()` and redraws the texture every frame, * but does not render itself. This is useful for updating textures * for reuse by other objects. * - 'all' mode calls `render()` then draws the texture to the frame. * * @name Phaser.GameObjects.RenderTexture#renderMode * @type {'render'|'redraw'|'all'} * @default 'render' * @since 4.0.0 */ this.renderMode = RenderTextureRenderModes.RENDER; /** * Whether this RenderTexture is currently executing `renderWebGL`. * This is used to prevent infinite loops when drawing containers. * You should not set this property directly. * * @name Phaser.GameObjects.RenderTexture#isCurrentlyRendering * @type {boolean} * @readonly * @since 4.0.0 */ this.isCurrentlyRendering = false; }, /** * Sets the internal size of this Render Texture, as used for frame or physics body creation. * * This will not change the size that the Game Object is rendered in-game. * For that you need to either set the scale of the Game Object (`setScale`) or call the * `setDisplaySize` method, which is the same thing as changing the scale but allows you * to do so by giving pixel values. You could also call the `resize` method, as that * will resize the underlying texture. * * If you have enabled this Game Object for input, changing the size will also change the * size of the hit area, unless you have defined a custom hit area. * * @method Phaser.GameObjects.RenderTexture#setSize * @since 3.0.0 * * @param {number} width - The width of this Game Object. * @param {number} height - The height of this Game Object. * * @return {this} This Game Object instance. */ setSize: function (width, height) { this.width = width; this.height = height; this.updateDisplayOrigin(); var input = this.input; if (input && !input.customHitArea) { input.hitArea.width = width; input.hitArea.height = height; } return this; }, /** * Resizes the Render Texture to the new dimensions given. * * In WebGL it will destroy and then re-create the frame buffer being used by the Render Texture. * In Canvas it will resize the underlying canvas element. * * Both approaches will erase everything currently drawn to the Render Texture. * * Calling this will then invoke the `setSize` method, setting the internal size of this Game Object * to the values given to this method. * * Calling this will then invoke the `setSize` method, setting the internal size of this Game Object * to the values given to this method. * * If the dimensions given are the same as those already being used, calling this method will do nothing. * * @method Phaser.GameObjects.RenderTexture#resize * @since 3.10.0 * * @param {number} width - The new width of the Render Texture. * @param {number} [height=width] - The new height of the Render Texture. If not specified, will be set the same as the `width`. * @param {boolean} [forceEven=true] - Force the given width and height to be rounded to even values. This significantly improves the rendering quality. Set to false if you know you need an odd sized texture. * * @return {this} This Render Texture. */ resize: function (width, height, forceEven) { this.texture.setSize(width, height, forceEven); this.setSize(this.texture.width, this.texture.height); return this; }, /** * Stores a copy of this Render Texture in the Texture Manager using the given key. * * After doing this, any texture based Game Object, such as a Sprite, can use the contents of this * Render Texture by using the texture key: * * ```javascript * var rt = this.add.renderTexture(0, 0, 128, 128); * * // Draw something to the Render Texture * * rt.saveTexture('doodle'); * * this.add.image(400, 300, 'doodle'); * ``` * * Updating the contents of this Render Texture will automatically update _any_ Game Object * that is using it as a texture. Calling `saveTexture` again will not save another copy * of the same texture, it will just rename the key of the existing copy. * * By default it will create a single base texture. You can add frames to the texture * by using the `Texture.add` method. After doing this, you can then allow Game Objects * to use a specific frame from a Render Texture. * * If you destroy this Render Texture, any Game Object using it via the Texture Manager will * stop rendering. Ensure you remove the texture from the Texture Manager and any Game Objects * using it first, before destroying this Render Texture. * * Note that the texture is assigned a random key on creation. * This key will be replaced with the new key. * If the texture was previously removed from the texture manager, * it will be added back so it can be reused. * * @method Phaser.GameObjects.RenderTexture#saveTexture * @since 3.12.0 * * @param {string} key - The unique key to store the texture as within the global Texture Manager. * * @return {Phaser.Textures.DynamicTexture} The Texture that was saved. */ saveTexture: function (key) { var texture = this.texture; var oldKey = texture.key; var textureManager = texture.manager; if (textureManager.exists(oldKey) && textureManager.get(oldKey) === texture) { textureManager.renameTexture(oldKey, key); this._saved = true; } else { texture.key = key; if (texture.manager.addDynamicTexture(texture)) { this._saved = true; } } return texture; }, /** * Set the `renderMode` of this Render Texture. * Set this to change how the Render Texture is rendered. * * - 'render' mode draws the contents of the Render Texture to each frame. * - 'redraw' mode calls `render()` and redraws the texture every frame, * but does not render itself. This is useful for updating textures * for reuse by other objects. * - 'all' mode calls `render()` then draws the texture to the frame. * * @method Phaser.GameObjects.RenderTexture#setRenderMode * @since 4.0.0 * @param {'render'|'redraw'|'all'} mode - The render mode to set. * @param {boolean} [preserve=false] - Whether to call `preserve(true)` to preserve the current command buffer. * @return {this} This Render Texture instance. */ setRenderMode: function (mode, preserve) { this.renderMode = mode; if (preserve) { this.texture.preserve(true); } return this; }, /** * Render the buffered drawing commands to this Dynamic Texture. * You must do this in order to see anything drawn to it. * * @method Phaser.GameObjects.RenderTexture#render * @since 4.0.0 */ render: function () { this.texture.render(); return this; }, /** * Fills this Render Texture with the given color. * * By default it will fill the entire texture, however you can set it to fill a specific * rectangular area by using the x, y, width and height arguments. * * The color should be given in hex format, i.e. 0xff0000 for red, 0x00ff00 for green, etc. * * @method Phaser.GameObjects.RenderTexture#fill * @since 3.2.0 * * @param {number} rgb - The color to fill this Render Texture with, such as 0xff0000 for red. * @param {number} [alpha=1] - The alpha value used by the fill. * @param {number} [x=0] - The left coordinate of the fill rectangle. * @param {number} [y=0] - The top coordinate of the fill rectangle. * @param {number} [width=this.width] - The width of the fill rectangle. * @param {number} [height=this.height] - The height of the fill rectangle. * * @return {this} This Render Texture instance. */ fill: function (rgb, alpha, x, y, width, height) { this.texture.fill(rgb, alpha, x, y, width, height); return this; }, /** * Clears a portion or everything from this Render Texture by erasing it and resetting it back to * a blank, transparent, texture. To clear an area, specify the `x`, `y`, `width` and `height`. * * @method Phaser.GameObjects.RenderTexture#clear * @since 3.2.0 * * @param {number} [x=0] - The left coordinate of the clear rectangle. * @param {number} [y=0] - The top coordinate of the clear rectangle. * @param {number} [width=this.width] - The width of the clear rectangle. * @param {number} [height=this.height] - The height of the clear rectangle. * * @return {this} This Render Texture instance. */ clear: function (x, y, width, height) { this.texture.clear(x, y, width, height); return this; }, /** * Takes the given texture key and frame and then stamps it at the given * x and y coordinates. You can use the optional 'config' argument to provide * lots more options about how the stamp is applied, including the alpha, * tint, angle, scale and origin. * * By default, the frame will stamp on the x/y coordinates based on its center. * * If you wish to stamp from the top-left, set the config `originX` and * `originY` properties both to zero. * * This method ignores the `camera` property of the Dynamic Texture. * * @method Phaser.GameObjects.RenderTexture#stamp * @since 3.60.0 * * @param {string} key - The key of the texture to be used, as stored in the Texture Manager. * @param {(string|number)} [frame] - The name or index of the frame within the Texture. Set to `null` to skip this argument if not required. * @param {number} [x=0] - The x position to draw the frame at. * @param {number} [y=0] - The y position to draw the frame at. * @param {Phaser.Types.Textures.StampConfig} [config] - The stamp configuration object, allowing you to set the alpha, tint, angle, scale and origin of the stamp. * * @return {this} This Render Texture instance. */ stamp: function (key, frame, x, y, config) { this.texture.stamp(key, frame, x, y, config); return this; }, /** * Draws the given object, or an array of objects, to this Render Texture using a blend mode of ERASE. * This has the effect of erasing any filled pixels present in the objects from this texture. * * This method uses the `draw` method internally, * and the parameters behave the same way. * * @method Phaser.GameObjects.RenderTexture#erase * @since 3.16.0 * * @param {any} entries - Any renderable Game Object, or Group, Container, Display List, Render Texture, Texture Frame, or an array of any of these. * @param {number} [x=0] - The x position to draw the Frame at, or the offset applied to the object. * @param {number} [y=0] - The y position to draw the Frame at, or the offset applied to the object. * * @return {this} This Render Texture instance. */ erase: function (entries, x, y) { this.texture.erase(entries, x, y); return this; }, /** * Draws the given object, or an array of objects, to this RenderTexture. * * It can accept any of the following: * * * Any renderable Game Object, such as a Sprite, Text, Graphics or TileSprite. * * Tilemap Layers. * * A Group. The contents of which will be iterated and drawn in turn. * * A Container. The contents of which will be iterated fully, and drawn in turn. * * A Scene Display List. Pass in `Scene.children` to draw the whole list. * * Another Dynamic Texture, or a Render Texture. * * A Texture Frame instance. * * A string. This is used to look-up the texture from the Texture Manager. * * Note 1: You cannot draw a Render Texture to itself. * * Note 2: GameObjects will use the camera, while textures and frames will not. * Textures and frames are drawn using the `stamp` method. * * If passing in a Group or Container it will only draw children that return `true` * when their `willRender()` method is called. I.e. a Container with 10 children, * 5 of which have `visible=false` will only draw the 5 visible ones. * * If passing in an array of Game Objects it will draw them all, regardless if * they pass a `willRender` check or not. * * You can pass in a string in which case it will look for a texture in the Texture * Manager matching that string, and draw the base frame. If you need to specify * exactly which frame to draw then use the method `drawFrame` instead. * * You can pass in the `x` and `y` coordinates to draw the objects at. The use of * the coordinates differ based on what objects are being drawn. If the object is * a Group, Container or Display List, the coordinates are _added_ to the positions * of the children. For all other types of object, the coordinates are exact. * For textures and frames, the `x` and `y` values are the middle of the texture. * * The `alpha` and `tint` values are only used by Texture Frames. * Game Objects use their own alpha and tint values when being drawn. * * @method Phaser.GameObjects.RenderTexture#draw * @since 3.2.0 * * @param {any} entries - Any renderable Game Object, or Group, Container, Display List, other Render Texture, Texture Frame or an array of any of these. * @param {number} [x=0] - The x position to draw the Frame at, or the offset applied to the object. * @param {number} [y=0] - The y position to draw the Frame at, or the offset applied to the object. * @param {number} [alpha=1] - The alpha value. Only used when drawing Texture Frames to this texture. Game Objects use their own alpha. * @param {number} [tint=0xffffff] - The tint color value. Only used when drawing Texture Frames to this texture. Game Objects use their own tint. WebGL only. * * @return {this} This Render Texture instance. */ draw: function (entries, x, y, alpha, tint) { this.texture.draw(entries, x, y, alpha, tint); return this; }, /** * Draws the given object to this Render Texture. * This allows you to draw the object as it appears in the game world, * or with various parameter overrides in the config. * * @method Phaser.GameObjects.RenderTexture#capture * @since 4.0.0 * * @param {Phaser.GameObjects.GameObject} entry - Any renderable GameObject. * @param {Phaser.Types.Textures.CaptureConfig} config - The configuration object for the capture. * * @return {this} This Render Texture instance. */ capture: function (entry, config) { this.texture.capture(entry, config); return this; }, /** * Takes the given Texture Frame and draws it to this Dynamic Texture as a fill pattern, * i.e. in a grid-layout based on the frame dimensions. * It uses a `TileSprite` internally to draw the frame repeatedly. * * Textures are referenced by their string-based keys, as stored in the Texture Manager. * * You can optionally provide a position, width, height, alpha and tint value to apply to * the frames before they are drawn. The position controls the top-left where the repeating * fill will start from. The width and height control the size of the filled area. * * The position can be negative if required, but the dimensions cannot. * * This method respects the camera settings of the Dynamic Texture. * * @method Phaser.GameObjects.RenderTexture#repeat * @since 3.60.0 * * @param {string} key - The key of the texture to be used, as stored in the Texture Manager. * @param {(string|number)} [frame] - The name or index of the frame within the Texture. Set to `null` to skip this argument if not required. * @param {number} [x=0] - The x position to start drawing the frames from (can be negative to offset). * @param {number} [y=0] - The y position to start drawing the frames from (can be negative to offset). * @param {number} [width=this.width] - The width of the area to repeat the frame within. Defaults to the width of this Dynamic Texture. * @param {number} [height=this.height] - The height of the area to repeat the frame within. Defaults to the height of this Dynamic Texture. * @param {Phaser.Types.GameObjects.TileSprite.TileSpriteConfig} [config] - The configuration object for the TileSprite which repeats the texture, allowing you to set further properties on it. * * @return {this} This Render Texture instance. */ repeat: function (key, frame, x, y, width, height, config) { this.texture.repeat(key, frame, x, y, width, height, config); return this; }, /** * Sets the preserve flag for this Dynamic Texture. * Ordinarily, after each render, the command buffer is cleared. * When this flag is set to `true`, the command buffer is preserved between renders. * This makes it possible to repeat the same drawing commands on each render. * * Make sure to call `clear()` at the start if you don't want to accumulate * drawing detail over the top of itself. * * @method Phaser.GameObjects.RenderTexture#preserve * @since 4.0.0 * @param {boolean} preserve - Whether to preserve the command buffer after rendering. * @return {this} This Render Texture instance. */ preserve: function (preserve) { this.texture.preserve(preserve); return this; }, /** * Adds a callback to run during the render process. * This callback runs as a step in the command buffer. * It can be used to set up conditions for the next draw step. * * Note that this will only execute after `render()` is called. * * @method Phaser.GameObjects.RenderTexture#callback * @since 4.0.0 * @param {Function} callback - A callback function to run during the render process. * @return {this} This Render Texture instance. */ callback: function (callback) { this.texture.callback(callback); return this; }, /** * Takes a snapshot of the given area of this Render Texture. * * The snapshot is taken immediately, but the results are returned via the given callback. * * To capture the whole Render Texture see the `snapshot` method. * To capture just a specific pixel, see the `snapshotPixel` method. * * Snapshots work by using the WebGL `readPixels` feature to grab every pixel from the frame buffer * into an ArrayBufferView. It then parses this, copying the contents to a temporary Canvas and finally * creating an Image object from it, which is the image returned to the callback provided. * * All in all, this is a computationally expensive and blocking process, which gets more expensive * the larger the resolution this Render Texture has, so please be careful how you employ this in your game. * * @method Phaser.GameObjects.RenderTexture#snapshotArea * @since 3.19.0 * * @param {number} x - The x coordinate to grab from. * @param {number} y - The y coordinate to grab from. * @param {number} width - The width of the area to grab. * @param {number} height - The height of the area to grab. * @param {Phaser.Types.Renderer.Snapshot.SnapshotCallback} callback - The Function to invoke after the snapshot image is created. * @param {string} [type='image/png'] - The format of the image to create, usually `image/png` or `image/jpeg`. * @param {number} [encoderOptions=0.92] - The image quality, between 0 and 1. Used for image formats with lossy compression, such as `image/jpeg`. * * @return {this} This Render Texture instance. */ snapshotArea: function (x, y, width, height, callback, type, encoderOptions) { this.texture.snapshotArea(x, y, width, height, callback, type, encoderOptions); return this; }, /** * Takes a snapshot of the whole of this Render Texture. * * The snapshot is taken immediately, but the results are returned via the given callback. * * To capture a portion of this Render Texture see the `snapshotArea` method. * To capture just a specific pixel, see the `snapshotPixel` method. * * Snapshots work by using the WebGL `readPixels` feature to grab every pixel from the frame buffer * into an ArrayBufferView. It then parses this, copying the contents to a temporary Canvas and finally * creating an Image object from it, which is the image returned to the callback provided. * * All in all, this is a computationally expensive and blocking process, which gets more expensive * the larger the resolution this Render Texture has, so please be careful how you employ this in your game. * * @method Phaser.GameObjects.RenderTexture#snapshot * @since 3.19.0 * * @param {Phaser.Types.Renderer.Snapshot.SnapshotCallback} callback - The Function to invoke after the snapshot image is created. * @param {string} [type='image/png'] - The format of the image to create, usually `image/png` or `image/jpeg`. * @param {number} [encoderOptions=0.92] - The image quality, between 0 and 1. Used for image formats with lossy compression, such as `image/jpeg`. * * @return {this} This Render Texture instance. */ snapshot: function (callback, type, encoderOptions) { return this.texture.snapshot(callback, type, encoderOptions); }, /** * Takes a snapshot of the given pixel from this Render Texture. * * The snapshot is taken immediately, but the results are returned via the given callback. * * To capture the whole Render Texture see the `snapshot` method. * To capture a portion of this Render Texture see the `snapshotArea` method. * * Unlike the two other snapshot methods, this one will send your callback a `Color` object * containing the color data for the requested pixel. It doesn't need to create an internal * Canvas or Image object, so is a lot faster to execute, using less memory than the other snapshot methods. * * @method Phaser.GameObjects.RenderTexture#snapshotPixel * @since 3.19.0 * * @param {number} x - The x coordinate of the pixel to get. * @param {number} y - The y coordinate of the pixel to get. * @param {Phaser.Types.Renderer.Snapshot.SnapshotCallback} callback - The Function to invoke after the snapshot pixel data is extracted. * * @return {this} This Render Texture instance. */ snapshotPixel: function (x, y, callback) { return this.texture.snapshotPixel(x, y, callback); }, /** * Internal destroy handler, called as part of the destroy process. * * @method Phaser.GameObjects.RenderTexture#preDestroy * @protected * @since 3.9.0 */ preDestroy: function () { this.camera = null; if (!this._saved) { this.texture.destroy(); } } }); module.exports = RenderTexture;