UNPKG

phaser

Version:

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

1,572 lines (1,389 loc) 50 kB
/** * @author Richard Davey <rich@phaser.io> * @copyright 2013-2025 Phaser Studio Inc. * @license {@link https://opensource.org/licenses/MIT|MIT License} */ var BaseCamera = require('../../cameras/2d/BaseCamera'); var Class = require('../../utils/Class'); var Commands = require('./Commands'); var Components = require('../components'); var Ellipse = require('../../geom/ellipse/Ellipse'); var GameObject = require('../GameObject'); var GetFastValue = require('../../utils/object/GetFastValue'); var GetValue = require('../../utils/object/GetValue'); var MATH_CONST = require('../../math/const'); var Render = require('./GraphicsRender'); /** * @classdesc * A Graphics object is a way to draw primitive shapes to your game. Primitives include forms of geometry, such as * Rectangles, Circles, and Polygons. They also include lines, arcs and curves. When you initially create a Graphics * object it will be empty. * * To draw to it you must first specify a line style or fill style (or both), draw shapes using paths, and finally * fill or stroke them. For example: * * ```javascript * graphics.lineStyle(5, 0xFF00FF, 1.0); * graphics.beginPath(); * graphics.moveTo(100, 100); * graphics.lineTo(200, 200); * graphics.closePath(); * graphics.strokePath(); * ``` * * There are also many helpful methods that draw and fill/stroke common shapes for you. * * ```javascript * graphics.lineStyle(5, 0xFF00FF, 1.0); * graphics.fillStyle(0xFFFFFF, 1.0); * graphics.fillRect(50, 50, 400, 200); * graphics.strokeRect(50, 50, 400, 200); * ``` * * When a Graphics object is rendered it will render differently based on if the game is running under Canvas or WebGL. * Under Canvas it will use the HTML Canvas context drawing operations to draw the path. * Under WebGL the graphics data is decomposed into polygons. Both of these are expensive processes, especially with * complex shapes. * * If your Graphics object doesn't change much (or at all) once you've drawn your shape to it, then you will help * performance by calling {@link Phaser.GameObjects.Graphics#generateTexture}. This will 'bake' the Graphics object into * a Texture, and return it. You can then use this Texture for Sprites or other display objects. If your Graphics object * updates frequently then you should avoid doing this, as it will constantly generate new textures, which will consume * memory. * * As you can tell, Graphics objects are a bit of a trade-off. While they are extremely useful, you need to be careful * in their complexity and quantity of them in your game. * * @class Graphics * @extends Phaser.GameObjects.GameObject * @memberof Phaser.GameObjects * @constructor * @since 3.0.0 * * @extends Phaser.GameObjects.Components.AlphaSingle * @extends Phaser.GameObjects.Components.BlendMode * @extends Phaser.GameObjects.Components.Depth * @extends Phaser.GameObjects.Components.Mask * @extends Phaser.GameObjects.Components.Pipeline * @extends Phaser.GameObjects.Components.PostPipeline * @extends Phaser.GameObjects.Components.Transform * @extends Phaser.GameObjects.Components.Visible * @extends Phaser.GameObjects.Components.ScrollFactor * * @param {Phaser.Scene} scene - The Scene to which this Graphics object belongs. * @param {Phaser.Types.GameObjects.Graphics.Options} [options] - Options that set the position and default style of this Graphics object. */ var Graphics = new Class({ Extends: GameObject, Mixins: [ Components.AlphaSingle, Components.BlendMode, Components.Depth, Components.Mask, Components.Pipeline, Components.PostPipeline, Components.Transform, Components.Visible, Components.ScrollFactor, Render ], initialize: function Graphics (scene, options) { var x = GetValue(options, 'x', 0); var y = GetValue(options, 'y', 0); GameObject.call(this, scene, 'Graphics'); this.setPosition(x, y); this.initPipeline(); this.initPostPipeline(); /** * The horizontal display origin of the Graphics. * * @name Phaser.GameObjects.Graphics#displayOriginX * @type {number} * @default 0 * @since 3.0.0 */ this.displayOriginX = 0; /** * The vertical display origin of the Graphics. * * @name Phaser.GameObjects.Graphics#displayOriginY * @type {number} * @default 0 * @since 3.0.0 */ this.displayOriginY = 0; /** * The array of commands used to render the Graphics. * * @name Phaser.GameObjects.Graphics#commandBuffer * @type {array} * @default [] * @since 3.0.0 */ this.commandBuffer = []; /** * The default fill color for shapes rendered by this Graphics object. * Set this value with `setDefaultStyles()`. * * @name Phaser.GameObjects.Graphics#defaultFillColor * @type {number} * @readonly * @default -1 * @since 3.0.0 */ this.defaultFillColor = -1; /** * The default fill alpha for shapes rendered by this Graphics object. * Set this value with `setDefaultStyles()`. * * @name Phaser.GameObjects.Graphics#defaultFillAlpha * @type {number} * @readonly * @default 1 * @since 3.0.0 */ this.defaultFillAlpha = 1; /** * The default stroke width for shapes rendered by this Graphics object. * Set this value with `setDefaultStyles()`. * * @name Phaser.GameObjects.Graphics#defaultStrokeWidth * @type {number} * @readonly * @default 1 * @since 3.0.0 */ this.defaultStrokeWidth = 1; /** * The default stroke color for shapes rendered by this Graphics object. * Set this value with `setDefaultStyles()`. * * @name Phaser.GameObjects.Graphics#defaultStrokeColor * @type {number} * @readonly * @default -1 * @since 3.0.0 */ this.defaultStrokeColor = -1; /** * The default stroke alpha for shapes rendered by this Graphics object. * Set this value with `setDefaultStyles()`. * * @name Phaser.GameObjects.Graphics#defaultStrokeAlpha * @type {number} * @readonly * @default 1 * @since 3.0.0 */ this.defaultStrokeAlpha = 1; /** * Internal property that keeps track of the line width style setting. * * @name Phaser.GameObjects.Graphics#_lineWidth * @type {number} * @private * @since 3.0.0 */ this._lineWidth = 1; this.lineStyle(1, 0, 0); this.fillStyle(0, 0); this.setDefaultStyles(options); }, /** * Set the default style settings for this Graphics object. * * @method Phaser.GameObjects.Graphics#setDefaultStyles * @since 3.0.0 * * @param {Phaser.Types.GameObjects.Graphics.Styles} options - The styles to set as defaults. * * @return {this} This Game Object. */ setDefaultStyles: function (options) { if (GetValue(options, 'lineStyle', null)) { this.defaultStrokeWidth = GetValue(options, 'lineStyle.width', 1); this.defaultStrokeColor = GetValue(options, 'lineStyle.color', 0xffffff); this.defaultStrokeAlpha = GetValue(options, 'lineStyle.alpha', 1); this.lineStyle(this.defaultStrokeWidth, this.defaultStrokeColor, this.defaultStrokeAlpha); } if (GetValue(options, 'fillStyle', null)) { this.defaultFillColor = GetValue(options, 'fillStyle.color', 0xffffff); this.defaultFillAlpha = GetValue(options, 'fillStyle.alpha', 1); this.fillStyle(this.defaultFillColor, this.defaultFillAlpha); } return this; }, /** * Set the current line style. Used for all 'stroke' related functions. * * @method Phaser.GameObjects.Graphics#lineStyle * @since 3.0.0 * * @param {number} lineWidth - The stroke width. * @param {number} color - The stroke color. * @param {number} [alpha=1] - The stroke alpha. * * @return {this} This Game Object. */ lineStyle: function (lineWidth, color, alpha) { if (alpha === undefined) { alpha = 1; } this.commandBuffer.push( Commands.LINE_STYLE, lineWidth, color, alpha ); this._lineWidth = lineWidth; return this; }, /** * Set the current fill style. Used for all 'fill' related functions. * * @method Phaser.GameObjects.Graphics#fillStyle * @since 3.0.0 * * @param {number} color - The fill color. * @param {number} [alpha=1] - The fill alpha. * * @return {this} This Game Object. */ fillStyle: function (color, alpha) { if (alpha === undefined) { alpha = 1; } this.commandBuffer.push( Commands.FILL_STYLE, color, alpha ); return this; }, /** * Sets a gradient fill style. This is a WebGL only feature. * * The gradient color values represent the 4 corners of an untransformed rectangle. * The gradient is used to color all filled shapes and paths drawn after calling this method. * If you wish to turn a gradient off, call `fillStyle` and provide a new single fill color. * * When filling a triangle only the first 3 color values provided are used for the 3 points of a triangle. * * This feature is best used only on rectangles and triangles. All other shapes will give strange results. * * Note that for objects such as arcs or ellipses, or anything which is made out of triangles, each triangle used * will be filled with a gradient on its own. There is no ability to gradient fill a shape or path as a single * entity at this time. * * @method Phaser.GameObjects.Graphics#fillGradientStyle * @webglOnly * @since 3.12.0 * * @param {number} topLeft - The top left fill color. * @param {number} topRight - The top right fill color. * @param {number} bottomLeft - The bottom left fill color. * @param {number} bottomRight - The bottom right fill color. Not used when filling triangles. * @param {number} [alphaTopLeft=1] - The top left alpha value. If you give only this value, it's used for all corners. * @param {number} [alphaTopRight=1] - The top right alpha value. * @param {number} [alphaBottomLeft=1] - The bottom left alpha value. * @param {number} [alphaBottomRight=1] - The bottom right alpha value. * * @return {this} This Game Object. */ fillGradientStyle: function (topLeft, topRight, bottomLeft, bottomRight, alphaTopLeft, alphaTopRight, alphaBottomLeft, alphaBottomRight) { if (alphaTopLeft === undefined) { alphaTopLeft = 1; } if (alphaTopRight === undefined) { alphaTopRight = alphaTopLeft; } if (alphaBottomLeft === undefined) { alphaBottomLeft = alphaTopLeft; } if (alphaBottomRight === undefined) { alphaBottomRight = alphaTopLeft; } this.commandBuffer.push( Commands.GRADIENT_FILL_STYLE, alphaTopLeft, alphaTopRight, alphaBottomLeft, alphaBottomRight, topLeft, topRight, bottomLeft, bottomRight ); return this; }, /** * Sets a gradient line style. This is a WebGL only feature. * * The gradient color values represent the 4 corners of an untransformed rectangle. * The gradient is used to color all stroked shapes and paths drawn after calling this method. * If you wish to turn a gradient off, call `lineStyle` and provide a new single line color. * * This feature is best used only on single lines. All other shapes will give strange results. * * Note that for objects such as arcs or ellipses, or anything which is made out of triangles, each triangle used * will be filled with a gradient on its own. There is no ability to gradient stroke a shape or path as a single * entity at this time. * * @method Phaser.GameObjects.Graphics#lineGradientStyle * @webglOnly * @since 3.12.0 * * @param {number} lineWidth - The stroke width. * @param {number} topLeft - The tint being applied to the top-left of the Game Object. * @param {number} topRight - The tint being applied to the top-right of the Game Object. * @param {number} bottomLeft - The tint being applied to the bottom-left of the Game Object. * @param {number} bottomRight - The tint being applied to the bottom-right of the Game Object. * @param {number} [alpha=1] - The fill alpha. * * @return {this} This Game Object. */ lineGradientStyle: function (lineWidth, topLeft, topRight, bottomLeft, bottomRight, alpha) { if (alpha === undefined) { alpha = 1; } this.commandBuffer.push( Commands.GRADIENT_LINE_STYLE, lineWidth, alpha, topLeft, topRight, bottomLeft, bottomRight ); return this; }, /** * Start a new shape path. * * @method Phaser.GameObjects.Graphics#beginPath * @since 3.0.0 * * @return {this} This Game Object. */ beginPath: function () { this.commandBuffer.push( Commands.BEGIN_PATH ); return this; }, /** * Close the current path. * * @method Phaser.GameObjects.Graphics#closePath * @since 3.0.0 * * @return {this} This Game Object. */ closePath: function () { this.commandBuffer.push( Commands.CLOSE_PATH ); return this; }, /** * Fill the current path. * * @method Phaser.GameObjects.Graphics#fillPath * @since 3.0.0 * * @return {this} This Game Object. */ fillPath: function () { this.commandBuffer.push( Commands.FILL_PATH ); return this; }, /** * Fill the current path. * * This is an alias for `Graphics.fillPath` and does the same thing. * It was added to match the CanvasRenderingContext 2D API. * * @method Phaser.GameObjects.Graphics#fill * @since 3.16.0 * * @return {this} This Game Object. */ fill: function () { this.commandBuffer.push( Commands.FILL_PATH ); return this; }, /** * Stroke the current path. * * @method Phaser.GameObjects.Graphics#strokePath * @since 3.0.0 * * @return {this} This Game Object. */ strokePath: function () { this.commandBuffer.push( Commands.STROKE_PATH ); return this; }, /** * Stroke the current path. * * This is an alias for `Graphics.strokePath` and does the same thing. * It was added to match the CanvasRenderingContext 2D API. * * @method Phaser.GameObjects.Graphics#stroke * @since 3.16.0 * * @return {this} This Game Object. */ stroke: function () { this.commandBuffer.push( Commands.STROKE_PATH ); return this; }, /** * Fill the given circle. * * @method Phaser.GameObjects.Graphics#fillCircleShape * @since 3.0.0 * * @param {Phaser.Geom.Circle} circle - The circle to fill. * * @return {this} This Game Object. */ fillCircleShape: function (circle) { return this.fillCircle(circle.x, circle.y, circle.radius); }, /** * Stroke the given circle. * * @method Phaser.GameObjects.Graphics#strokeCircleShape * @since 3.0.0 * * @param {Phaser.Geom.Circle} circle - The circle to stroke. * * @return {this} This Game Object. */ strokeCircleShape: function (circle) { return this.strokeCircle(circle.x, circle.y, circle.radius); }, /** * Fill a circle with the given position and radius. * * @method Phaser.GameObjects.Graphics#fillCircle * @since 3.0.0 * * @param {number} x - The x coordinate of the center of the circle. * @param {number} y - The y coordinate of the center of the circle. * @param {number} radius - The radius of the circle. * * @return {this} This Game Object. */ fillCircle: function (x, y, radius) { this.beginPath(); this.arc(x, y, radius, 0, MATH_CONST.PI2); this.fillPath(); return this; }, /** * Stroke a circle with the given position and radius. * * @method Phaser.GameObjects.Graphics#strokeCircle * @since 3.0.0 * * @param {number} x - The x coordinate of the center of the circle. * @param {number} y - The y coordinate of the center of the circle. * @param {number} radius - The radius of the circle. * * @return {this} This Game Object. */ strokeCircle: function (x, y, radius) { this.beginPath(); this.arc(x, y, radius, 0, MATH_CONST.PI2); this.strokePath(); return this; }, /** * Fill the given rectangle. * * @method Phaser.GameObjects.Graphics#fillRectShape * @since 3.0.0 * * @param {Phaser.Geom.Rectangle} rect - The rectangle to fill. * * @return {this} This Game Object. */ fillRectShape: function (rect) { return this.fillRect(rect.x, rect.y, rect.width, rect.height); }, /** * Stroke the given rectangle. * * @method Phaser.GameObjects.Graphics#strokeRectShape * @since 3.0.0 * * @param {Phaser.Geom.Rectangle} rect - The rectangle to stroke. * * @return {this} This Game Object. */ strokeRectShape: function (rect) { return this.strokeRect(rect.x, rect.y, rect.width, rect.height); }, /** * Fill a rectangle with the given position and size. * * @method Phaser.GameObjects.Graphics#fillRect * @since 3.0.0 * * @param {number} x - The x coordinate of the top-left of the rectangle. * @param {number} y - The y coordinate of the top-left of the rectangle. * @param {number} width - The width of the rectangle. * @param {number} height - The height of the rectangle. * * @return {this} This Game Object. */ fillRect: function (x, y, width, height) { this.commandBuffer.push( Commands.FILL_RECT, x, y, width, height ); return this; }, /** * Stroke a rectangle with the given position and size. * * @method Phaser.GameObjects.Graphics#strokeRect * @since 3.0.0 * * @param {number} x - The x coordinate of the top-left of the rectangle. * @param {number} y - The y coordinate of the top-left of the rectangle. * @param {number} width - The width of the rectangle. * @param {number} height - The height of the rectangle. * * @return {this} This Game Object. */ strokeRect: function (x, y, width, height) { var lineWidthHalf = this._lineWidth / 2; var minx = x - lineWidthHalf; var maxx = x + lineWidthHalf; this.beginPath(); this.moveTo(x, y); this.lineTo(x, y + height); this.strokePath(); this.beginPath(); this.moveTo(x + width, y); this.lineTo(x + width, y + height); this.strokePath(); this.beginPath(); this.moveTo(minx, y); this.lineTo(maxx + width, y); this.strokePath(); this.beginPath(); this.moveTo(minx, y + height); this.lineTo(maxx + width, y + height); this.strokePath(); return this; }, /** * Fill a rounded rectangle with the given position, size and radius. * * @method Phaser.GameObjects.Graphics#fillRoundedRect * @since 3.11.0 * * @param {number} x - The x coordinate of the top-left of the rectangle. * @param {number} y - The y coordinate of the top-left of the rectangle. * @param {number} width - The width of the rectangle. * @param {number} height - The height of the rectangle. * @param {(Phaser.Types.GameObjects.Graphics.RoundedRectRadius|number)} [radius=20] - The corner radius; It can also be an object to specify different radius for corners. * * @return {this} This Game Object. */ fillRoundedRect: function (x, y, width, height, radius) { if (radius === undefined) { radius = 20; } var tl = radius; var tr = radius; var bl = radius; var br = radius; if (typeof radius !== 'number') { tl = GetFastValue(radius, 'tl', 20); tr = GetFastValue(radius, 'tr', 20); bl = GetFastValue(radius, 'bl', 20); br = GetFastValue(radius, 'br', 20); } var convexTL = (tl >= 0); var convexTR = (tr >= 0); var convexBL = (bl >= 0); var convexBR = (br >= 0); tl = Math.abs(tl); tr = Math.abs(tr); bl = Math.abs(bl); br = Math.abs(br); this.beginPath(); this.moveTo(x + tl, y); this.lineTo(x + width - tr, y); if (convexTR) { this.arc(x + width - tr, y + tr, tr, -MATH_CONST.TAU, 0); } else { this.arc(x + width, y, tr, Math.PI, MATH_CONST.TAU, true); } this.lineTo(x + width, y + height - br); if (convexBR) { this.arc(x + width - br, y + height - br, br, 0, MATH_CONST.TAU); } else { this.arc(x + width, y + height, br, -MATH_CONST.TAU, Math.PI, true); } this.lineTo(x + bl, y + height); if (convexBL) { this.arc(x + bl, y + height - bl, bl, MATH_CONST.TAU, Math.PI); } else { this.arc(x, y + height, bl, 0, -MATH_CONST.TAU, true); } this.lineTo(x, y + tl); if (convexTL) { this.arc(x + tl, y + tl, tl, -Math.PI, -MATH_CONST.TAU); } else { this.arc(x, y, tl, MATH_CONST.TAU, 0, true); } this.fillPath(); return this; }, /** * Stroke a rounded rectangle with the given position, size and radius. * * @method Phaser.GameObjects.Graphics#strokeRoundedRect * @since 3.11.0 * * @param {number} x - The x coordinate of the top-left of the rectangle. * @param {number} y - The y coordinate of the top-left of the rectangle. * @param {number} width - The width of the rectangle. * @param {number} height - The height of the rectangle. * @param {(Phaser.Types.GameObjects.Graphics.RoundedRectRadius|number)} [radius=20] - The corner radius; It can also be an object to specify different radii for corners. * * @return {this} This Game Object. */ strokeRoundedRect: function (x, y, width, height, radius) { if (radius === undefined) { radius = 20; } var tl = radius; var tr = radius; var bl = radius; var br = radius; var maxRadius = Math.min(width, height) / 2; if (typeof radius !== 'number') { tl = GetFastValue(radius, 'tl', 20); tr = GetFastValue(radius, 'tr', 20); bl = GetFastValue(radius, 'bl', 20); br = GetFastValue(radius, 'br', 20); } var convexTL = (tl >= 0); var convexTR = (tr >= 0); var convexBL = (bl >= 0); var convexBR = (br >= 0); tl = Math.min(Math.abs(tl), maxRadius); tr = Math.min(Math.abs(tr), maxRadius); bl = Math.min(Math.abs(bl), maxRadius); br = Math.min(Math.abs(br), maxRadius); this.beginPath(); this.moveTo(x + tl, y); this.lineTo(x + width - tr, y); this.moveTo(x + width - tr, y); if (convexTR) { this.arc(x + width - tr, y + tr, tr, -MATH_CONST.TAU, 0); } else { this.arc(x + width, y, tr, Math.PI, MATH_CONST.TAU, true); } this.lineTo(x + width, y + height - br); this.moveTo(x + width, y + height - br); if (convexBR) { this.arc(x + width - br, y + height - br, br, 0, MATH_CONST.TAU); } else { this.arc(x + width, y + height, br, -MATH_CONST.TAU, Math.PI, true); } this.lineTo(x + bl, y + height); this.moveTo(x + bl, y + height); if (convexBL) { this.arc(x + bl, y + height - bl, bl, MATH_CONST.TAU, Math.PI); } else { this.arc(x, y + height, bl, 0, -MATH_CONST.TAU, true); } this.lineTo(x, y + tl); this.moveTo(x, y + tl); if (convexTL) { this.arc(x + tl, y + tl, tl, -Math.PI, -MATH_CONST.TAU); } else { this.arc(x, y, tl, MATH_CONST.TAU, 0, true); } this.strokePath(); return this; }, /** * Fill the given point. * * Draws a square at the given position, 1 pixel in size by default. * * @method Phaser.GameObjects.Graphics#fillPointShape * @since 3.0.0 * * @param {(Phaser.Geom.Point|Phaser.Math.Vector2|object)} point - The point to fill. * @param {number} [size=1] - The size of the square to draw. * * @return {this} This Game Object. */ fillPointShape: function (point, size) { return this.fillPoint(point.x, point.y, size); }, /** * Fill a point at the given position. * * Draws a square at the given position, 1 pixel in size by default. * * @method Phaser.GameObjects.Graphics#fillPoint * @since 3.0.0 * * @param {number} x - The x coordinate of the point. * @param {number} y - The y coordinate of the point. * @param {number} [size=1] - The size of the square to draw. * * @return {this} This Game Object. */ fillPoint: function (x, y, size) { if (!size || size < 1) { size = 1; } else { x -= (size / 2); y -= (size / 2); } this.commandBuffer.push( Commands.FILL_RECT, x, y, size, size ); return this; }, /** * Fill the given triangle. * * @method Phaser.GameObjects.Graphics#fillTriangleShape * @since 3.0.0 * * @param {Phaser.Geom.Triangle} triangle - The triangle to fill. * * @return {this} This Game Object. */ fillTriangleShape: function (triangle) { return this.fillTriangle(triangle.x1, triangle.y1, triangle.x2, triangle.y2, triangle.x3, triangle.y3); }, /** * Stroke the given triangle. * * @method Phaser.GameObjects.Graphics#strokeTriangleShape * @since 3.0.0 * * @param {Phaser.Geom.Triangle} triangle - The triangle to stroke. * * @return {this} This Game Object. */ strokeTriangleShape: function (triangle) { return this.strokeTriangle(triangle.x1, triangle.y1, triangle.x2, triangle.y2, triangle.x3, triangle.y3); }, /** * Fill a triangle with the given points. * * @method Phaser.GameObjects.Graphics#fillTriangle * @since 3.0.0 * * @param {number} x0 - The x coordinate of the first point. * @param {number} y0 - The y coordinate of the first point. * @param {number} x1 - The x coordinate of the second point. * @param {number} y1 - The y coordinate of the second point. * @param {number} x2 - The x coordinate of the third point. * @param {number} y2 - The y coordinate of the third point. * * @return {this} This Game Object. */ fillTriangle: function (x0, y0, x1, y1, x2, y2) { this.commandBuffer.push( Commands.FILL_TRIANGLE, x0, y0, x1, y1, x2, y2 ); return this; }, /** * Stroke a triangle with the given points. * * @method Phaser.GameObjects.Graphics#strokeTriangle * @since 3.0.0 * * @param {number} x0 - The x coordinate of the first point. * @param {number} y0 - The y coordinate of the first point. * @param {number} x1 - The x coordinate of the second point. * @param {number} y1 - The y coordinate of the second point. * @param {number} x2 - The x coordinate of the third point. * @param {number} y2 - The y coordinate of the third point. * * @return {this} This Game Object. */ strokeTriangle: function (x0, y0, x1, y1, x2, y2) { this.commandBuffer.push( Commands.STROKE_TRIANGLE, x0, y0, x1, y1, x2, y2 ); return this; }, /** * Draw the given line. * * @method Phaser.GameObjects.Graphics#strokeLineShape * @since 3.0.0 * * @param {Phaser.Geom.Line} line - The line to stroke. * * @return {this} This Game Object. */ strokeLineShape: function (line) { return this.lineBetween(line.x1, line.y1, line.x2, line.y2); }, /** * Draw a line between the given points. * * @method Phaser.GameObjects.Graphics#lineBetween * @since 3.0.0 * * @param {number} x1 - The x coordinate of the start point of the line. * @param {number} y1 - The y coordinate of the start point of the line. * @param {number} x2 - The x coordinate of the end point of the line. * @param {number} y2 - The y coordinate of the end point of the line. * * @return {this} This Game Object. */ lineBetween: function (x1, y1, x2, y2) { this.beginPath(); this.moveTo(x1, y1); this.lineTo(x2, y2); this.strokePath(); return this; }, /** * Draw a line from the current drawing position to the given position. * * Moves the current drawing position to the given position. * * @method Phaser.GameObjects.Graphics#lineTo * @since 3.0.0 * * @param {number} x - The x coordinate to draw the line to. * @param {number} y - The y coordinate to draw the line to. * * @return {this} This Game Object. */ lineTo: function (x, y) { this.commandBuffer.push( Commands.LINE_TO, x, y ); return this; }, /** * Move the current drawing position to the given position. * * @method Phaser.GameObjects.Graphics#moveTo * @since 3.0.0 * * @param {number} x - The x coordinate to move to. * @param {number} y - The y coordinate to move to. * * @return {this} This Game Object. */ moveTo: function (x, y) { this.commandBuffer.push( Commands.MOVE_TO, x, y ); return this; }, /** * Stroke the shape represented by the given array of points. * * Pass `closeShape` to automatically close the shape by joining the last to the first point. * * Pass `closePath` to automatically close the path before it is stroked. * * @method Phaser.GameObjects.Graphics#strokePoints * @since 3.0.0 * * @param {(array|Phaser.Geom.Point[])} points - The points to stroke. * @param {boolean} [closeShape=false] - When `true`, the shape is closed by joining the last point to the first point. * @param {boolean} [closePath=false] - When `true`, the path is closed before being stroked. * @param {number} [endIndex] - The index of `points` to stop drawing at. Defaults to `points.length`. * * @return {this} This Game Object. */ strokePoints: function (points, closeShape, closePath, endIndex) { if (closeShape === undefined) { closeShape = false; } if (closePath === undefined) { closePath = false; } if (endIndex === undefined) { endIndex = points.length; } this.beginPath(); this.moveTo(points[0].x, points[0].y); for (var i = 1; i < endIndex; i++) { this.lineTo(points[i].x, points[i].y); } if (closeShape) { this.lineTo(points[0].x, points[0].y); } if (closePath) { this.closePath(); } this.strokePath(); return this; }, /** * Fill the shape represented by the given array of points. * * Pass `closeShape` to automatically close the shape by joining the last to the first point. * * Pass `closePath` to automatically close the path before it is filled. * * @method Phaser.GameObjects.Graphics#fillPoints * @since 3.0.0 * * @param {(array|Phaser.Geom.Point[])} points - The points to fill. * @param {boolean} [closeShape=false] - When `true`, the shape is closed by joining the last point to the first point. * @param {boolean} [closePath=false] - When `true`, the path is closed before being stroked. * @param {number} [endIndex] - The index of `points` to stop at. Defaults to `points.length`. * * @return {this} This Game Object. */ fillPoints: function (points, closeShape, closePath, endIndex) { if (closeShape === undefined) { closeShape = false; } if (closePath === undefined) { closePath = false; } if (endIndex === undefined) { endIndex = points.length; } this.beginPath(); this.moveTo(points[0].x, points[0].y); for (var i = 1; i < endIndex; i++) { this.lineTo(points[i].x, points[i].y); } if (closeShape) { this.lineTo(points[0].x, points[0].y); } if (closePath) { this.closePath(); } this.fillPath(); return this; }, /** * Stroke the given ellipse. * * @method Phaser.GameObjects.Graphics#strokeEllipseShape * @since 3.0.0 * * @param {Phaser.Geom.Ellipse} ellipse - The ellipse to stroke. * @param {number} [smoothness=32] - The number of points to draw the ellipse with. * * @return {this} This Game Object. */ strokeEllipseShape: function (ellipse, smoothness) { if (smoothness === undefined) { smoothness = 32; } var points = ellipse.getPoints(smoothness); return this.strokePoints(points, true); }, /** * Stroke an ellipse with the given position and size. * * @method Phaser.GameObjects.Graphics#strokeEllipse * @since 3.0.0 * * @param {number} x - The x coordinate of the center of the ellipse. * @param {number} y - The y coordinate of the center of the ellipse. * @param {number} width - The width of the ellipse. * @param {number} height - The height of the ellipse. * @param {number} [smoothness=32] - The number of points to draw the ellipse with. * * @return {this} This Game Object. */ strokeEllipse: function (x, y, width, height, smoothness) { if (smoothness === undefined) { smoothness = 32; } var ellipse = new Ellipse(x, y, width, height); var points = ellipse.getPoints(smoothness); return this.strokePoints(points, true); }, /** * Fill the given ellipse. * * @method Phaser.GameObjects.Graphics#fillEllipseShape * @since 3.0.0 * * @param {Phaser.Geom.Ellipse} ellipse - The ellipse to fill. * @param {number} [smoothness=32] - The number of points to draw the ellipse with. * * @return {this} This Game Object. */ fillEllipseShape: function (ellipse, smoothness) { if (smoothness === undefined) { smoothness = 32; } var points = ellipse.getPoints(smoothness); return this.fillPoints(points, true); }, /** * Fill an ellipse with the given position and size. * * @method Phaser.GameObjects.Graphics#fillEllipse * @since 3.0.0 * * @param {number} x - The x coordinate of the center of the ellipse. * @param {number} y - The y coordinate of the center of the ellipse. * @param {number} width - The width of the ellipse. * @param {number} height - The height of the ellipse. * @param {number} [smoothness=32] - The number of points to draw the ellipse with. * * @return {this} This Game Object. */ fillEllipse: function (x, y, width, height, smoothness) { if (smoothness === undefined) { smoothness = 32; } var ellipse = new Ellipse(x, y, width, height); var points = ellipse.getPoints(smoothness); return this.fillPoints(points, true); }, /** * Draw an arc. * * This method can be used to create circles, or parts of circles. * * Make sure you call `beginPath` before starting the arc unless you wish for the arc to automatically * close when filled or stroked. * * Use the optional `overshoot` argument increase the number of iterations that take place when * the arc is rendered in WebGL. This is useful if you're drawing an arc with an especially thick line, * as it will allow the arc to fully join-up. Try small values at first, i.e. 0.01. * * Call {@link Phaser.GameObjects.Graphics#fillPath} or {@link Phaser.GameObjects.Graphics#strokePath} after calling * this method to draw the arc. * * @method Phaser.GameObjects.Graphics#arc * @since 3.0.0 * * @param {number} x - The x coordinate of the center of the circle. * @param {number} y - The y coordinate of the center of the circle. * @param {number} radius - The radius of the circle. * @param {number} startAngle - The starting angle, in radians. * @param {number} endAngle - The ending angle, in radians. * @param {boolean} [anticlockwise=false] - Whether the drawing should be anticlockwise or clockwise. * @param {number} [overshoot=0] - This value allows you to increase the segment iterations in WebGL rendering. Useful if the arc has a thick stroke and needs to overshoot to join-up cleanly. Use small numbers such as 0.01 to start with and increase as needed. * * @return {this} This Game Object. */ arc: function (x, y, radius, startAngle, endAngle, anticlockwise, overshoot) { if (anticlockwise === undefined) { anticlockwise = false; } if (overshoot === undefined) { overshoot = 0; } this.commandBuffer.push( Commands.ARC, x, y, radius, startAngle, endAngle, anticlockwise, overshoot ); return this; }, /** * Creates a pie-chart slice shape centered at `x`, `y` with the given radius. * You must define the start and end angle of the slice. * * Setting the `anticlockwise` argument to `true` creates a shape similar to Pacman. * Setting it to `false` creates a shape like a slice of pie. * * This method will begin a new path and close the path at the end of it. * To display the actual slice you need to call either `strokePath` or `fillPath` after it. * * @method Phaser.GameObjects.Graphics#slice * @since 3.4.0 * * @param {number} x - The horizontal center of the slice. * @param {number} y - The vertical center of the slice. * @param {number} radius - The radius of the slice. * @param {number} startAngle - The start angle of the slice, given in radians. * @param {number} endAngle - The end angle of the slice, given in radians. * @param {boolean} [anticlockwise=false] - Whether the drawing should be anticlockwise or clockwise. * @param {number} [overshoot=0] - This value allows you to overshoot the endAngle by this amount. Useful if the arc has a thick stroke and needs to overshoot to join-up cleanly. * * @return {this} This Game Object. */ slice: function (x, y, radius, startAngle, endAngle, anticlockwise, overshoot) { if (anticlockwise === undefined) { anticlockwise = false; } if (overshoot === undefined) { overshoot = 0; } this.commandBuffer.push(Commands.BEGIN_PATH); this.commandBuffer.push(Commands.MOVE_TO, x, y); this.commandBuffer.push(Commands.ARC, x, y, radius, startAngle, endAngle, anticlockwise, overshoot); this.commandBuffer.push(Commands.CLOSE_PATH); return this; }, /** * Saves the state of the Graphics by pushing the current state onto a stack. * * The most recently saved state can then be restored with {@link Phaser.GameObjects.Graphics#restore}. * * @method Phaser.GameObjects.Graphics#save * @since 3.0.0 * * @return {this} This Game Object. */ save: function () { this.commandBuffer.push( Commands.SAVE ); return this; }, /** * Restores the most recently saved state of the Graphics by popping from the state stack. * * Use {@link Phaser.GameObjects.Graphics#save} to save the current state, and call this afterwards to restore that state. * * If there is no saved state, this command does nothing. * * @method Phaser.GameObjects.Graphics#restore * @since 3.0.0 * * @return {this} This Game Object. */ restore: function () { this.commandBuffer.push( Commands.RESTORE ); return this; }, /** * Inserts a translation command into this Graphics objects command buffer. * * All objects drawn _after_ calling this method will be translated * by the given amount. * * This does not change the position of the Graphics object itself, * only of the objects drawn by it after calling this method. * * @method Phaser.GameObjects.Graphics#translateCanvas * @since 3.0.0 * * @param {number} x - The horizontal translation to apply. * @param {number} y - The vertical translation to apply. * * @return {this} This Game Object. */ translateCanvas: function (x, y) { this.commandBuffer.push( Commands.TRANSLATE, x, y ); return this; }, /** * Inserts a scale command into this Graphics objects command buffer. * * All objects drawn _after_ calling this method will be scaled * by the given amount. * * This does not change the scale of the Graphics object itself, * only of the objects drawn by it after calling this method. * * @method Phaser.GameObjects.Graphics#scaleCanvas * @since 3.0.0 * * @param {number} x - The horizontal scale to apply. * @param {number} y - The vertical scale to apply. * * @return {this} This Game Object. */ scaleCanvas: function (x, y) { this.commandBuffer.push( Commands.SCALE, x, y ); return this; }, /** * Inserts a rotation command into this Graphics objects command buffer. * * All objects drawn _after_ calling this method will be rotated * by the given amount. * * This does not change the rotation of the Graphics object itself, * only of the objects drawn by it after calling this method. * * @method Phaser.GameObjects.Graphics#rotateCanvas * @since 3.0.0 * * @param {number} radians - The rotation angle, in radians. * * @return {this} This Game Object. */ rotateCanvas: function (radians) { this.commandBuffer.push( Commands.ROTATE, radians ); return this; }, /** * Clear the command buffer and reset the fill style and line style to their defaults. * * @method Phaser.GameObjects.Graphics#clear * @since 3.0.0 * * @return {this} This Game Object. */ clear: function () { this.commandBuffer.length = 0; if (this.defaultFillColor > -1) { this.fillStyle(this.defaultFillColor, this.defaultFillAlpha); } if (this.defaultStrokeColor > -1) { this.lineStyle(this.defaultStrokeWidth, this.defaultStrokeColor, this.defaultStrokeAlpha); } return this; }, /** * Generate a texture from this Graphics object. * * If `key` is a string it'll generate a new texture using it and add it into the * Texture Manager (assuming no key conflict happens). * * If `key` is a Canvas it will draw the texture to that canvas context. Note that it will NOT * automatically upload it to the GPU in WebGL mode. * * Please understand that the texture is created via the Canvas API of the browser, therefore some * Graphics features, such as `fillGradientStyle`, will not appear on the resulting texture, * as they're unsupported by the Canvas API. * * @method Phaser.GameObjects.Graphics#generateTexture * @since 3.0.0 * * @param {(string|HTMLCanvasElement)} key - The key to store the texture with in the Texture Manager, or a Canvas to draw to. * @param {number} [width] - The width of the graphics to generate. * @param {number} [height] - The height of the graphics to generate. * * @return {this} This Game Object. */ generateTexture: function (key, width, height) { var sys = this.scene.sys; var renderer = sys.game.renderer; if (width === undefined) { width = sys.scale.width; } if (height === undefined) { height = sys.scale.height; } Graphics.TargetCamera.setScene(this.scene); Graphics.TargetCamera.setViewport(0, 0, width, height); Graphics.TargetCamera.scrollX = this.x; Graphics.TargetCamera.scrollY = this.y; var texture; var ctx; var willRead = { willReadFrequently: true }; if (typeof key === 'string') { if (sys.textures.exists(key)) { // Key is a string, it DOES exist in the Texture Manager AND is a canvas, so draw to it texture = sys.textures.get(key); var src = texture.getSourceImage(); if (src instanceof HTMLCanvasElement) { ctx = src.getContext('2d', willRead); } } else { // Key is a string and doesn't exist in the Texture Manager, so generate and save it texture = sys.textures.createCanvas(key, width, height); ctx = texture.getSourceImage().getContext('2d', willRead); } } else if (key instanceof HTMLCanvasElement) { // Key is a Canvas, so draw to it ctx = key.getContext('2d', willRead); } if (ctx) { // var GraphicsCanvasRenderer = function (renderer, src, camera, parentMatrix, renderTargetCtx, allowClip) this.renderCanvas(renderer, this, Graphics.TargetCamera, null, ctx, false); if (texture) { texture.refresh(); } } return this; }, /** * Internal destroy handler, called as part of the destroy process. * * @method Phaser.GameObjects.Graphics#preDestroy * @protected * @since 3.9.0 */ preDestroy: function () { this.commandBuffer = []; } }); /** * A Camera used specifically by the Graphics system for rendering to textures. * * @name Phaser.GameObjects.Graphics.TargetCamera * @type {Phaser.Cameras.Scene2D.Camera} * @since 3.1.0 */ Graphics.TargetCamera = new BaseCamera