phaser
Version:
A fast, free and fun HTML5 Game Framework for Desktop and Mobile web browsers from the team at Phaser Studio Inc.
318 lines (285 loc) • 11.6 kB
JavaScript
/**
* @author Benjamin D. Richards <benjamindrichards@gmail.com>
* @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 Controller = require('./Controller');
/**
* @classdesc
* The Mask Filter Controller.
*
* This filter controller manages a mask effect.
*
* A mask uses a texture to hide parts of an input.
* It multiplies the color and alpha of the input
* by the alpha of the mask in the corresponding texel.
*
* Masks can be inverted, which switches what they hide and what they show.
*
* Masks can use either a texture or a GameObject.
* If a GameObject is used, the mask will render the GameObject
* to a DynamicTexture and use that.
* The mask will automatically update when the GameObject changes,
* unless the `autoUpdate` flag is set to `false`.
*
* When the mask filter is used as an internal filter,
* the mask will match the object/view being filtered.
* This is useful for creating effects that follow the object,
* such as effects intended to match an animated sprite.
*
* When the mask filter is used as an external filter,
* the mask will match the context of the camera.
* This is useful for creating effects that cover the entire view.
*
* An optional `viewCamera` can be specified when creating the mask.
* If not used, mask objects will be viewed through the current camera,
* or through a default camera if no other option is set.
* For example, when rendering to a DynamicTexture outside the normal rendering
* flow.
*
* A Mask effect is added to a Camera via the FilterList component:
*
* ```js
* const camera = this.cameras.main;
* const texture = 'MyMask';
*
* camera.filters.internal.addMask(texture);
* camera.filters.external.addMask(texture, true, myCamera);
* ```
*
* @class Mask
* @memberof Phaser.Filters
* @constructor
* @since 4.0.0
* @extends Phaser.Filters.Controller
* @param {Phaser.Cameras.Scene2D.Camera} camera - The Camera that owns this filter.
* @param {string|Phaser.GameObjects.GameObject} [mask='__WHITE'] - The source of the mask. This can be a unique string-based key of the texture to use for the mask, which must exist in the Texture Manager. Or it can be a GameObject, in which case the mask will render the GameObject to a DynamicTexture and use that.
* @param {boolean} [invert=false] - Whether to invert the mask.
* @param {Phaser.Cameras.Scene2D.Camera} [viewCamera] - The Camera to use when rendering the mask with a GameObject. If not specified, uses the scene's `main` camera.
* @param {'local'|'world'} [viewTransform='world'] - The transform to use when rendering the mask with a GameObject. 'local' uses the GameObject's own properties. 'world' uses the GameObject's `parentContainer` value to compute a world position.
* @param {number} [scaleFactor=1] - The scale factor to apply to the underlying mask texture. Can be used to balance memory usage and needed mask precision. This just adjusts the size of the texture; you must also adjust mask size to match, e.g. if scaleFactor is 0.5, your mask might be a Container with scale 0.5. It's easy to make things complicated when combining scale factor, object transform, and camera transform, so try to be precise when using this option.
*/
var Mask = new Class({
Extends: Controller,
initialize: function Mask (camera, mask, invert, viewCamera, viewTransform, scaleFactor)
{
if (mask === undefined) { mask = '__WHITE'; }
if (invert === undefined) { invert = false; }
if (scaleFactor === undefined) { scaleFactor = 1; }
Controller.call(this, camera, 'FilterMask');
/**
* The underlying texture used for the mask.
*
* @name Phaser.Filters.Mask#glTexture
* @type {Phaser.Renderer.WebGL.Wrappers.WebGLTextureWrapper}
* @since 4.0.0
*/
this.glTexture;
/**
* The dynamic texture used for the mask.
* This is only set if the mask is a GameObject.
*
* @name Phaser.Filters.Mask#_dynamicTexture
* @type {Phaser.Textures.DynamicTexture}
* @private
* @since 4.0.0
* @default null
*/
this._dynamicTexture = null;
/**
* The GameObject used for the mask.
* This is only set if the mask is a GameObject.
*
* @name Phaser.Filters.Mask#maskGameObject
* @type {Phaser.GameObjects.GameObject}
* @since 4.0.0
* @default null
*/
this.maskGameObject = null;
/**
* Whether to invert the mask.
* An inverted mask switches what it hides and what it shows.
*
* @name Phaser.Filters.Mask#invert
* @type {boolean}
* @since 4.0.0
* @default false
*/
this.invert = invert;
/**
* Whether the mask should automatically update.
* This only applies when the mask is a GameObject.
* If `false`, the mask will not change even if the GameObject changes.
*
* @name Phaser.Filters.Mask#autoUpdate
* @type {boolean}
* @since 4.0.0
* @default true
*/
this.autoUpdate = true;
/**
* Whether the mask needs updating, once.
* This only applies when the mask is a GameObject.
* If `true`, the mask will be updated before the next render.
* This is automatically set to `true` when the mask is a GameObject,
* but it turns off after the mask is updated.
*
* @name Phaser.Filters.Mask#needsUpdate
* @type {boolean}
* @since 4.0.0
* @default false
*/
this.needsUpdate = false;
/**
* The transform type to use when rendering the mask with a GameObject.
* 'local' uses the GameObject's own properties.
* 'world' uses the GameObject's `parentContainer` value to compute a world position.
* This only applies when the mask is a GameObject.
*
* @name Phaser.Filters.Mask#viewTransform
* @type {'local'|'world'}
* @since 4.0.0
* @default 'world'
*/
this.viewTransform = viewTransform || 'world';
/**
* The Camera to use when rendering the mask.
* If not specified, uses the currently rendering camera,
* or failing that, an internal Camera.
*
* @name Phaser.Filters.Mask#viewCamera
* @type {?Phaser.Cameras.Scene2D.Camera}
* @since 4.0.0
*/
this.viewCamera = viewCamera;
/**
* The scale factor to apply to the underlying mask texture.
* A value less than 1 reduces the texture resolution to save memory
* at the cost of mask precision. A value greater than 1 increases
* resolution for sharper masking but uses more memory. When changing
* this value, you must also scale the mask GameObject or Container
* to match, so that its rendered output fills the texture correctly.
*
* @name Phaser.Filters.Mask#scaleFactor
* @type {number}
* @default 1
* @since 4.0.0
*/
this.scaleFactor = scaleFactor;
if (typeof mask === 'string')
{
this.setTexture(mask);
}
else
{
this.setGameObject(mask);
}
},
/**
* Updates the DynamicTexture for the mask.
* The DynamicTexture is created or resized if necessary.
* This is called automatically during rendering
* when the mask is a GameObject
* and the `needsUpdate` or `autoUpdate` flags are set.
* It should not be called directly.
*
* @method Phaser.Filters.Mask#updateDynamicTexture
* @since 4.0.0
* @param {number} width - The width of the DynamicTexture.
* @param {number} height - The height of the DynamicTexture.
*/
updateDynamicTexture: function (width, height)
{
var scaleFactor = this.scaleFactor;
var scaledWidth = width * scaleFactor;
var scaledHeight = height * scaleFactor;
var gameObject = this.maskGameObject;
if (!gameObject)
{
return;
}
if (!this._dynamicTexture)
{
var textureManager = this.camera.scene.sys.textures;
this._dynamicTexture = textureManager.addDynamicTexture(UUID(), scaledWidth, scaledHeight, false);
}
else if (this._dynamicTexture.width !== scaledWidth || this._dynamicTexture.height !== scaledHeight)
{
this._dynamicTexture.setSize(scaledWidth, scaledHeight, false);
}
else
{
this._dynamicTexture.clear();
}
this.glTexture = this._dynamicTexture.get().glTexture;
var camera = this.viewCamera || gameObject.scene.renderer.currentViewCamera;
// Draw the GameObject to the DynamicTexture.
this._dynamicTexture.capture(gameObject, { transform: this.viewTransform, camera: camera });
this._dynamicTexture.render();
this.needsUpdate = false;
},
/**
* Sets the GameObject used for the mask. The GameObject will be rendered
* to an internal DynamicTexture on the next render pass. Setting a new
* GameObject also sets `needsUpdate` to `true`, ensuring the texture is
* refreshed before the next frame is drawn.
*
* @method Phaser.Filters.Mask#setGameObject
* @since 4.0.0
* @param {Phaser.GameObjects.GameObject} gameObject - The GameObject to use for the mask.
* @return {this} This Filter Controller.
*/
setGameObject: function (gameObject)
{
this.maskGameObject = gameObject;
this.needsUpdate = true;
// `_dynamicTexture` will be generated at render time,
// using the camera of the current context.
// The camera which owns this filter is only the correct camera
// if this filter is being used as an internal filter.
return this;
},
/**
* Sets a static texture to use as the mask, looked up by key from the
* Texture Manager. Any previously assigned mask GameObject is cleared.
* Unlike a GameObject mask, a static texture mask does not update
* automatically between frames.
*
* @method Phaser.Filters.Mask#setTexture
* @since 4.0.0
* @param {string} [texture='__WHITE'] - The unique string-based key of the texture to use for the mask, which must exist in the Texture Manager.
* @return {this} This Filter Controller.
*/
setTexture: function (texture)
{
var phaserTexture = this.camera.scene.sys.textures.getFrame(texture);
if (phaserTexture)
{
this.maskGameObject = null;
this.glTexture = phaserTexture.glTexture;
}
return this;
},
/**
* Destroys this filter, releasing all references and resources.
*
* If a dynamic texture was created for a mask GameObject,
* it will also be destroyed.
*
* @method Phaser.Filters.Mask#destroy
* @since 4.0.0
*/
destroy: function ()
{
if (this._dynamicTexture)
{
this._dynamicTexture.destroy();
}
this.maskGameObject = null;
this._dynamicTexture = null;
Controller.prototype.destroy.call(this);
}
});
module.exports = Mask;