Finished Plane documentation

This commit is contained in:
Richard Davey 2023-01-31 13:21:02 +00:00
parent c54cc2d6a1
commit dfc3ebc0ec
2 changed files with 464 additions and 40 deletions

View file

@ -13,7 +13,36 @@ var UUID = require('../../utils/string/UUID');
/**
* @classdesc
* Experimental
* A Plane Game Object.
*
* The Plane Game Object is a helper class that takes the Mesh Game Object and extends it,
* allowing for fast and easy creation of Planes. A Plane is a one-sided grid of cells,
* where you specify the number of cells in each dimension. The Plane can have a texture
* that is either repeated (tiled) across each cell, or applied to the full Plane.
*
* The Plane can then be manipulated in 3D space, with rotation across all 3 axis.
*
* This allows you to create effects not possible with regular Sprites, such as perspective
* distortion. You can also adjust the vertices on a per-vertex basis. Plane data becomes
* part of the WebGL batch, just like standard Sprites, so doesn't introduce any additional
* shader overhead. Because the Plane just generates vertices into the WebGL batch, like any
* other Sprite, you can use all of the common Game Object components on a Plane too,
* such as a custom pipeline, mask, blend mode or texture.
*
* You can use the `uvScroll` and `uvScale` methods to adjust the placement and scaling
* of the texture if this Plane is using a single texture, and not a frame from a texture
* atlas or sprite sheet.
*
* The Plane Game Object also has the Animation component, allowing you to play animations
* across the Plane just as you would with a Sprite.
*
* While a Plane cannot be enabled for input it does have the methods `hasFaceAt` and
* `getFaceAt` which can be used with Pointer coordinates to detect if they have clicked
* on Plane face, or not.
*
* Note that the Plane object is WebGL only and does not have a Canvas counterpart.
*
* The Plane origin is always 0.5 x 0.5 and cannot be changed.
*
* @class Plane
* @extends Phaser.GameObjects.Mesh
@ -21,11 +50,14 @@ var UUID = require('../../utils/string/UUID');
* @constructor
* @since 3.60.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] - The horizontal position of this Game Object in the world.
* @param {number} [y] - The vertical position of this Game Object in the world.
* @param {string|Phaser.Textures.Texture} [texture] - The key, or instance of the Texture this Game Object will use to render with, as stored in the Texture Manager.
* @param {string|number} [frame] - An optional frame from the Texture this Game Object is rendering with.
* @param {Phaser.Scene} scene - The Scene to which this Plane belongs. A Plane can only belong to one Scene at a time.
* @param {number} [x] - The horizontal position of this Plane in the world.
* @param {number} [y] - The vertical position of this Plane in the world.
* @param {string|Phaser.Textures.Texture} [texture] - The key, or instance of the Texture this Plane will use to render with, as stored in the Texture Manager.
* @param {string|number} [frame] - An optional frame from the Texture this Plane is rendering with.
* @param {number} [width=8] - The width of this Plane, in cells, not pixels.
* @param {number} [height=8] - The height of this Plane, in cells, not pixels.
* @param {boolean} [tile=false] - Is the texture tiled? I.e. repeated across each cell.
*/
var Plane = new Class({
@ -36,9 +68,6 @@ var Plane = new Class({
function Plane (scene, x, y, texture, frame, width, height, tile)
{
if (!texture) { texture = '__DEFAULT'; }
if (width === undefined) { width = 8; }
if (height === undefined) { height = 8; }
if (tile === undefined) { tile = false; }
Mesh.call(this, scene, x, y, texture, frame);
@ -51,15 +80,57 @@ var Plane = new Class({
* It is responsible for playing, loading, queuing animations for later playback,
* mixing between animations and setting the current animation frame to this Sprite.
*
* @name Phaser.GameObjects.Sprite#anims
* @name Phaser.GameObjects.Plane#anims
* @type {Phaser.Animations.AnimationState}
* @since 3.0.0
* @since 3.60.0
*/
this.anims = new AnimationState(this);
this._gridWidth = width;
this._gridHeight = height;
this._gridTile = tile;
/**
* The width of this Plane in cells, not pixels.
*
* This value is read-only. To adjust it, see the `setGridSize` method.
*
* @name Phaser.GameObjects.Plane#gridWidth
* @type {number}
* @readonly
* @since 3.60.0
*/
this.gridWidth;
/**
* The height of this Plane in cells, not pixels.
*
* This value is read-only. To adjust it, see the `setGridSize` method.
*
* @name Phaser.GameObjects.Plane#gridHeight
* @type {number}
* @readonly
* @since 3.60.0
*/
this.gridHeight;
/**
* Is the texture of this Plane tiled across all cells, or not?
*
* This value is read-only. To adjust it, see the `setGridSize` method.
*
* @name Phaser.GameObjects.Plane#isTiled
* @type {boolean}
* @readonly
* @since 3.60.0
*/
this.isTiled;
/**
* If this Plane has a checkboard texture, this is a reference to
* the WebGLTexture being used. Otherwise, it's null.
*
* @name Phaser.GameObjects.Plane#_checkerboard
* @type {?WebGLTexture}
* @private
* @since 3.60.0
*/
this._checkerboard = null;
this.hideCCW = false;
@ -70,22 +141,25 @@ var Plane = new Class({
},
/**
* Update this Sprite's animations.
* Modifies the layout of this Plane by adjusting the grid dimensions to the
* given width and height. The values are given in cells, not pixels.
*
* @method Phaser.GameObjects.Sprite#preUpdate
* @protected
* @since 3.0.0
* The `tile` parameter allows you to control if the texture is tiled, or
* applied across the entire Plane? A tiled texture will repeat with one
* iteration per cell. A non-tiled texture will be applied across the whole
* Plane.
*
* @param {number} time - The current timestamp.
* @param {number} delta - The delta time, in ms, elapsed since the last frame.
* Note that if this Plane is using a single texture, not from a texture atlas
* or sprite sheet, then you can use the `Plane.uvScale` method to have much
* more fine-grained control over the texture tiling.
*
* @method Phaser.GameObjects.Plane#preDestroy
* @since 3.60.0
*
* @param {number} [width=8] - The width of this Plane, in cells, not pixels.
* @param {number} [height=8] - The height of this Plane, in cells, not pixels.
* @param {boolean} [tile=false] - Is the texture tiled? I.e. repeated across each cell.
*/
preUpdate: function (time, delta)
{
Mesh.prototype.preUpdate.call(this, time, delta);
this.anims.update(time, delta);
},
setGridSize: function (width, height, tile)
{
if (width === undefined) { width = 8; }
@ -99,6 +173,10 @@ var Plane = new Class({
flipY = true;
}
this.gridWidth = width;
this.gridHeight = height;
this.isTiled = tile;
this.clear();
GenerateGridVerts({
@ -113,28 +191,40 @@ var Plane = new Class({
return this;
},
/**
* An internal method that resets the perspective projection for this Plane
* when it changes texture or frame, and also resets the cell UV coordinates,
* if required.
*
* @method Phaser.GameObjects.Plane#setSizeToFrame
* @since 3.60.0
*
* @param {boolean} [resetUV=true] - Reset all of the cell UV coordinates?
*/
setSizeToFrame: function (resetUV)
{
if (resetUV === undefined) { resetUV = true; }
this.setPerspective(this.width / this.frame.width, this.height / this.frame.height);
var frame = this.frame;
this.setPerspective(this.width / frame.width, this.height / frame.height);
if (this._checkerboard && this._checkerboard !== this.texture)
{
this.removeCheckerboard();
}
// Reset UV coordinates if frame has changed
if (!resetUV)
{
return;
}
var gridX = this._gridWidth;
var gridY = this._gridHeight;
// Reset UV coordinates if frame has changed
var gridX = this.gridWidth;
var gridY = this.gridHeight;
var verts = this.vertices;
var frame = this.frame;
var frameU0 = frame.u0;
var frameU1 = frame.u1;
@ -145,7 +235,7 @@ var Plane = new Class({
var y;
var i = 0;
if (this._gridTile)
if (this.isTiled)
{
// flipY
frameV0 = frame.v1;
@ -205,6 +295,19 @@ var Plane = new Class({
}
},
/**
* Sets the height of this Plane to match the given value, in pixels.
*
* This adjusts the `Plane.viewPosition.z` value to achieve this.
*
* If no `value` parameter is given, it will set the view height to match
* that of the current texture frame the Plane is using.
*
* @method Phaser.GameObjects.Plane#setViewHeight
* @since 3.60.0
*
* @param {number} [value] - The height, in pixels, to set this Plane view height to.
*/
setViewHeight: function (value)
{
if (value === undefined) { value = this.frame.height; }
@ -216,6 +319,27 @@ var Plane = new Class({
this.dirtyCache[10] = 1;
},
/**
* Creates a checkerboard style texture, based on the given colors and alpha
* values and applies it to this Plane, replacing any current texture it may
* have.
*
* The colors are used in an alternating pattern, like a chess board.
*
* Calling this method generates a brand new 16x16 pixel WebGLTexture internally
* and applies it to this Plane. While quite fast to do, you should still be
* mindful of calling this method either extensively, or in tight parts of
* your game.
*
* @method Phaser.GameObjects.Plane#createCheckerboard
* @since 3.60.0
*
* @param {number} [color1=0xffffff] - The odd cell color, specified as a hex value.
* @param {number} [color2=0x0000ff] - The even cell color, specified as a hex value.
* @param {number} [alpha1=255] - The odd cell alpha value, specified as a number between 0 and 255.
* @param {number} [alpha2=255] - The even cell alpha value, specified as a number between 0 and 255.
* @param {number} [height=128] - The view height of the Plane after creation, in pixels.
*/
createCheckerboard: function (color1, color2, alpha1, alpha2, height)
{
if (color1 === undefined) { color1 = 0xffffff; }
@ -281,6 +405,13 @@ var Plane = new Class({
return this;
},
/**
* If this Plane has a Checkerboard Texture, this method will destroy it
* and reset the internal flag for it.
*
* @method Phaser.GameObjects.Plane#removeCheckerboard
* @since 3.60.0
*/
removeCheckerboard: function ()
{
if (this._checkerboard)
@ -291,11 +422,301 @@ var Plane = new Class({
}
},
/**
* Start playing the given animation on this Plane.
*
* Animations in Phaser can either belong to the global Animation Manager, or specifically to this Plane.
*
* The benefit of a global animation is that multiple Game Objects can all play the same animation, without
* having to duplicate the data. You can just create it once and then play it on any animating Game Object.
*
* The following code shows how to create a global repeating animation. The animation will be created
* from all of the frames within the sprite sheet that was loaded with the key 'muybridge':
*
* ```javascript
* var config = {
* key: 'run',
* frames: 'muybridge',
* frameRate: 15,
* repeat: -1
* };
*
* // This code should be run from within a Scene:
* this.anims.create(config);
* ```
*
* However, if you wish to create an animation that is unique to this Plane, and this Plane alone,
* you can call the `Animation.create` method instead. It accepts the exact same parameters as when
* creating a global animation, however the resulting data is kept locally in this Plane.
*
* With the animation created, either globally or locally, you can now play it on this Plane:
*
* ```javascript
* const plane = this.add.plane(...);
* plane.play('run');
* ```
*
* Alternatively, if you wish to run it at a different frame rate for example, you can pass a config
* object instead:
*
* ```javascript
* const plane = this.add.plane(...);
* plane.play({ key: 'run', frameRate: 24 });
* ```
*
* When playing an animation on a Plane it will first check to see if it can find a matching key
* locally within the Plane. If it can, it will play the local animation. If not, it will then
* search the global Animation Manager and look for it there.
*
* If you need a Plane to be able to play both local and global animations, make sure they don't
* have conflicting keys.
*
* See the documentation for the `PlayAnimationConfig` config object for more details about this.
*
* Also, see the documentation in the Animation Manager for further details on creating animations.
*
* @method Phaser.GameObjects.Plane#play
* @fires Phaser.Animations.Events#ANIMATION_START
* @since 3.60.0
*
* @param {(string|Phaser.Animations.Animation|Phaser.Types.Animations.PlayAnimationConfig)} key - The string-based key of the animation to play, or an Animation instance, or a `PlayAnimationConfig` object.
* @param {boolean} [ignoreIfPlaying=false] - If an animation is already playing then ignore this call.
*
* @return {this} This Game Object.
*/
play: function (key, ignoreIfPlaying)
{
return this.anims.play(key, ignoreIfPlaying);
},
/**
* Start playing the given animation on this Plane, in reverse.
*
* Animations in Phaser can either belong to the global Animation Manager, or specifically to a Game Object.
*
* The benefit of a global animation is that multiple Game Objects can all play the same animation, without
* having to duplicate the data. You can just create it once and then play it on any animating Game Object.
*
* The following code shows how to create a global repeating animation. The animation will be created
* from all of the frames within the sprite sheet that was loaded with the key 'muybridge':
*
* ```javascript
* var config = {
* key: 'run',
* frames: 'muybridge',
* frameRate: 15,
* repeat: -1
* };
*
* // This code should be run from within a Scene:
* this.anims.create(config);
* ```
*
* However, if you wish to create an animation that is unique to this Game Object, and this Game Object alone,
* you can call the `Animation.create` method instead. It accepts the exact same parameters as when
* creating a global animation, however the resulting data is kept locally in this Game Object.
*
* With the animation created, either globally or locally, you can now play it on this Game Object:
*
* ```javascript
* const plane = this.add.plane(...);
* plane.playReverse('run');
* ```
*
* Alternatively, if you wish to run it at a different frame rate, for example, you can pass a config
* object instead:
*
* ```javascript
* const plane = this.add.plane(...);
* plane.playReverse({ key: 'run', frameRate: 24 });
* ```
*
* When playing an animation on a Game Object it will first check to see if it can find a matching key
* locally within the Game Object. If it can, it will play the local animation. If not, it will then
* search the global Animation Manager and look for it there.
*
* If you need a Game Object to be able to play both local and global animations, make sure they don't
* have conflicting keys.
*
* See the documentation for the `PlayAnimationConfig` config object for more details about this.
*
* Also, see the documentation in the Animation Manager for further details on creating animations.
*
* @method Phaser.GameObjects.Plane#playReverse
* @fires Phaser.Animations.Events#ANIMATION_START
* @since 3.60.0
*
* @param {(string|Phaser.Animations.Animation|Phaser.Types.Animations.PlayAnimationConfig)} key - The string-based key of the animation to play, or an Animation instance, or a `PlayAnimationConfig` object.
* @param {boolean} [ignoreIfPlaying=false] - If an animation is already playing then ignore this call.
*
* @return {this} This Game Object.
*/
playReverse: function (key, ignoreIfPlaying)
{
return this.anims.playReverse(key, ignoreIfPlaying);
},
/**
* Waits for the specified delay, in milliseconds, then starts playback of the given animation.
*
* If the animation _also_ has a delay value set in its config, it will be **added** to the delay given here.
*
* If an animation is already running and a new animation is given to this method, it will wait for
* the given delay before starting the new animation.
*
* If no animation is currently running, the given one begins after the delay.
*
* When playing an animation on a Game Object it will first check to see if it can find a matching key
* locally within the Game Object. If it can, it will play the local animation. If not, it will then
* search the global Animation Manager and look for it there.
*
* @method Phaser.GameObjects.Plane#playAfterDelay
* @fires Phaser.Animations.Events#ANIMATION_START
* @since 3.60.0
*
* @param {(string|Phaser.Animations.Animation|Phaser.Types.Animations.PlayAnimationConfig)} key - The string-based key of the animation to play, or an Animation instance, or a `PlayAnimationConfig` object.
* @param {number} delay - The delay, in milliseconds, to wait before starting the animation playing.
*
* @return {this} This Game Object.
*/
playAfterDelay: function (key, delay)
{
return this.anims.playAfterDelay(key, delay);
},
/**
* Waits for the current animation to complete the `repeatCount` number of repeat cycles, then starts playback
* of the given animation.
*
* You can use this to ensure there are no harsh jumps between two sets of animations, i.e. going from an
* idle animation to a walking animation, by making them blend smoothly into each other.
*
* If no animation is currently running, the given one will start immediately.
*
* When playing an animation on a Game Object it will first check to see if it can find a matching key
* locally within the Game Object. If it can, it will play the local animation. If not, it will then
* search the global Animation Manager and look for it there.
*
* @method Phaser.GameObjects.Plane#playAfterRepeat
* @fires Phaser.Animations.Events#ANIMATION_START
* @since 3.60.0
*
* @param {(string|Phaser.Animations.Animation|Phaser.Types.Animations.PlayAnimationConfig)} key - The string-based key of the animation to play, or an Animation instance, or a `PlayAnimationConfig` object.
* @param {number} [repeatCount=1] - How many times should the animation repeat before the next one starts?
*
* @return {this} This Game Object.
*/
playAfterRepeat: function (key, repeatCount)
{
return this.anims.playAfterRepeat(key, repeatCount);
},
/**
* Immediately stops the current animation from playing and dispatches the `ANIMATION_STOP` events.
*
* If no animation is playing, no event will be dispatched.
*
* If there is another animation queued (via the `chain` method) then it will start playing immediately.
*
* @method Phaser.GameObjects.Plane#stop
* @fires Phaser.Animations.Events#ANIMATION_STOP
* @since 3.60.0
*
* @return {this} This Game Object.
*/
stop: function ()
{
return this.anims.stop();
},
/**
* Stops the current animation from playing after the specified time delay, given in milliseconds.
*
* It then dispatches the `ANIMATION_STOP` event.
*
* If no animation is running, no events will be dispatched.
*
* If there is another animation in the queue (set via the `chain` method) then it will start playing,
* when the current one stops.
*
* @method Phaser.GameObjects.Plane#stopAfterDelay
* @fires Phaser.Animations.Events#ANIMATION_STOP
* @since 3.60.0
*
* @param {number} delay - The number of milliseconds to wait before stopping this animation.
*
* @return {this} This Game Object.
*/
stopAfterDelay: function (delay)
{
return this.anims.stopAfterDelay(delay);
},
/**
* Stops the current animation from playing after the given number of repeats.
*
* It then dispatches the `ANIMATION_STOP` event.
*
* If no animation is running, no events will be dispatched.
*
* If there is another animation in the queue (set via the `chain` method) then it will start playing,
* when the current one stops.
*
* @method Phaser.GameObjects.Plane#stopAfterRepeat
* @fires Phaser.Animations.Events#ANIMATION_STOP
* @since 3.60.0
*
* @param {number} [repeatCount=1] - How many times should the animation repeat before stopping?
*
* @return {this} This Game Object.
*/
stopAfterRepeat: function (repeatCount)
{
return this.anims.stopAfterRepeat(repeatCount);
},
/**
* Stops the current animation from playing when it next sets the given frame.
* If this frame doesn't exist within the animation it will not stop it from playing.
*
* It then dispatches the `ANIMATION_STOP` event.
*
* If no animation is running, no events will be dispatched.
*
* If there is another animation in the queue (set via the `chain` method) then it will start playing,
* when the current one stops.
*
* @method Phaser.GameObjects.Plane#stopOnFrame
* @fires Phaser.Animations.Events#ANIMATION_STOP
* @since 3.60.0
*
* @param {Phaser.Animations.AnimationFrame} frame - The frame to check before stopping this animation.
*
* @return {this} This Game Object.
*/
stopOnFrame: function (frame)
{
return this.anims.stopOnFrame(frame);
},
/**
* Runs the preUpdate for this Plane, which will check its Animation State,
* if one is playing, and refresh view / model matrices, if updated.
*
* @method Phaser.GameObjects.Plane#preUpdate
* @protected
* @since 3.60.0
*
* @param {number} time - The current timestamp.
* @param {number} delta - The delta time, in ms, elapsed since the last frame.
*/
preUpdate: function (time, delta)
{
Mesh.prototype.preUpdate.call(this, time, delta);
this.anims.update(time, delta);
},
/**
* Handles the pre-destroy step for the Plane, which removes the vertices and debug callbacks.
*

View file

@ -8,19 +8,22 @@ var Plane = require('./Plane');
var GameObjectFactory = require('../GameObjectFactory');
/**
* Creates a new Image Game Object and adds it to the Scene.
* Creates a new Plane Game Object and adds it to the Scene.
*
* Note: This method will only be available if the Image Game Object has been built into Phaser.
* Note: This method will only be available if the Plane Game Object has been built into Phaser.
*
* @method Phaser.GameObjects.GameObjectFactory#plane
* @since 3.60.0
*
* @param {number} [x] - The horizontal position of this Game Object in the world.
* @param {number} [y] - The vertical position of this Game Object in the world.
* @param {string|Phaser.Textures.Texture} [texture] - The key, or instance of the Texture this Game Object will use to render with, as stored in the Texture Manager.
* @param {string|number} [frame] - An optional frame from the Texture this Game Object is rendering with.
* @param {number} [x] - The horizontal position of this Plane in the world.
* @param {number} [y] - The vertical position of this Plane in the world.
* @param {string|Phaser.Textures.Texture} [texture] - The key, or instance of the Texture this Plane will use to render with, as stored in the Texture Manager.
* @param {string|number} [frame] - An optional frame from the Texture this Plane is rendering with.
* @param {number} [width=8] - The width of this Plane, in cells, not pixels.
* @param {number} [height=8] - The height of this Plane, in cells, not pixels.
* @param {boolean} [tile=false] - Is the texture tiled? I.e. repeated across each cell.
*
* @return {Phaser.GameObjects.Image} The Game Object that was created.
* @return {Phaser.GameObjects.Plane} The Plane Game Object that was created.
*/
GameObjectFactory.register('plane', function (x, y, texture, frame, width, height, tile)
{