From af287cc2de2c2e563ffdcc8da8993ae840bf4616 Mon Sep 17 00:00:00 2001 From: Is Isilon Date: Sat, 28 Nov 2015 17:24:27 +0800 Subject: [PATCH] Added arcade-physics utility functions --- src/physics/World.js | 220 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 213 insertions(+), 7 deletions(-) diff --git a/src/physics/World.js b/src/physics/World.js index 518587a..5b99604 100644 --- a/src/physics/World.js +++ b/src/physics/World.js @@ -966,7 +966,7 @@ Phaser.Plugin.Isometric.Arcade.prototype = { /** * Find the distance between two display objects (like Sprites). * - * @method Phaser.Plugin.Isometric.Arcade#distanceBetween + * @method Phaser.Plugin.Isometric.Isometric.Arcade#distanceBetween * @param {any} source - The Display Object to test from. * @param {any} target - The Display Object to test to. * @return {number} The distance between the source and target objects. @@ -1001,29 +1001,235 @@ Phaser.Plugin.Isometric.Arcade.prototype = { }, - /** * Find the distance between a display object (like a Sprite) and the given x/y/z coordinates. * The calculation is made from the display objects x/y/z coordinate. This may be the top-left if its anchor hasn't been changed. * If you need to calculate from the center of a display object instead use the method distanceBetweenCenters() * * @method Phaser.Plugin.Isometric.Arcade#distanceToXYZ - * @param {any} displayObject - The Display Object to test from. + * @param {any} displayObjectBody - The Display Object to test from. * @param {number} x - The x coordinate to test to. * @param {number} y - The y coordinate to test to. * @param {number} z - The y coordinate to test to * @return {number} The distance between the object and the x/y coordinates. */ - distanceToXYZ: function (displayObject, x, y, z) { + distanceToXYZ: function (displayObjectBody, x, y, z) { - this._dx = displayObject.x - x; - this._dy = displayObject.y - y; - this._dz = displayObject.z - z; + this._dx = displayObjectBody.x - x; + this._dy = displayObjectBody.y - y; + this._dz = displayObjectBody.z - z; return Math.sqrt(this._dx * this._dx + this._dy * this._dy + this._dz * this._dz); + }, + + /** + * Find the distance between a display object (like a Sprite) and a Pointer. If no Pointer is given the Input.activePointer is used. + * The calculation is made from the display objects x/y coordinate. This may be the top-left if its anchor hasn't been changed. + * If you need to calculate from the center of a display object instead use the method distanceBetweenCenters() + * The distance to the Pointer is returned in isometric distance. + * + * @method Phaser.Physics.Arcade#distanceToPointer + * @param {any} displayObjectBody - The Display Object to test from. + * @param {Phaser.Pointer} [pointer] - The Phaser.Pointer to test to. If none is given then Input.activePointer is used. + * @return {number} The distance between the object and the Pointer. + */ + distanceToPointer: function (displayObjectBody, pointer) { + + pointer = pointer || this.game.input.activePointer; + var isoPointer = this.game.iso.unproject(pointer.position,undefined,displayObjectBody.z); + isoPointer.z = displayObjectBody.z; + var a = this.anglesToXYZ(displayObjectBody, isoPointer.x, isoPointer.y, isoPointer.z); + + return a.r; + + }, + + /** + * Find the angles in radians between a display object (like a IsoSprite) and the given x/y/z coordinate. + * + * @method Phaser.Physics.Isometric.Isometric.Arcade#anglesToXYZ + * @param {any} displayObjectBody - The Display Object to test from. + * @param {number} x - The x coordinate to get the angle to. + * @param {number} y - The y coordinate to get the angle to. + * @param {number} z - The z coordinate to get the angle to. + * @return {number} The angle in radians between displayObjectBody.x/y to Pointer.x/y + */ + anglesToXYZ: function (displayObjectBody, x, y, z) { + + // Spherical polar coordinates + var r = this.distanceToXYZ(displayObjectBody, x, y, z); + var theta = Math.atan2(y - displayObjectBody.y, x - displayObjectBody.x); + var phi = Math.acos((z - displayObjectBody.z)/ r); + + return {r:r,theta:theta,phi:phi}; + + }, + + /** + * Find the angle in radians between a display object (like a Sprite) and a Pointer, taking their x/y and center into account. + * This is not the visual angle but the angle in the isometric co-ordinate system. + * + * @method Phaser.Physics.Isometric.Arcade#angleToPointer + * @param {any} displayObjectBody - The Display Object to test from. + * @param {Phaser.Pointer} [pointer] - The Phaser.Pointer to test to. If none is given then Input.activePointer is used. + * @return {number} The (isometric) angle in radians between displayObjectBody.x/y to Pointer.x/y. + */ + angleToPointer: function(displayObjectBody, pointer) { + + pointer = pointer || this.game.input.activePointer; + var isoPointer = this.game.iso.unproject(pointer.position,undefined,displayObjectBody.z); + isoPointer.z = displayObjectBody.z; + var a = this.anglesToXYZ(displayObjectBody, isoPointer.x, isoPointer.y, isoPointer.z); + + return a.theta; + + }, + + /** + * Given the angle (in degrees) and speed calculate the velocity and return it as a Point object, or set it to the given point object. + * One way to use this is: velocityFromAngle(angle, 200, sprite.velocity) which will set the values directly to the sprites velocity and not create a new Point object. + * + * @method Phaser.Physics.Arcade#velocityFromAngle + * @param {number} theta - The angle in radians for x,y in the isometric co-ordinate system + * @param {number} [phi=Math.PI/2] - The angle in radians for z in the isometric co-ordinate system + * @param {number} [speed=60] - The speed it will move, in pixels per second sq. + * @param {Phaser.Point|object} [point] - The Point object in which the x and y properties will be set to the calculated velocity. + * @return {Phaser.Plugin.Isometric.Point3} - A Point where point.x contains the velocity x value and so on for y and z. + */ + velocityFromAngles: function (theta, phi, speed, point) { + + if (phi === undefined) { phi = Math.sin(Math.PI/2); } + if (speed === undefined) { speed = 60; } + point = point || new Phaser.Point(); + + return new Phaser.Plugin.Isometric.Point3( + Math.cos(theta) * Math.sin(phi) * speed, + Math.sin(theta) * Math.sin(phi) * speed, + Math.cos(phi) * speed + ); + + }, + + /** + * Sets the acceleration.x/y property on the display object so it will move towards the x/y coordinates at the given speed (in pixels per second sq.) + * You must give a maximum speed value, beyond which the display object won't go any faster. + * Note: The display object does not continuously track the target. If the target changes location during transit the display object will not modify its course. + * Note: The display object doesn't stop moving once it reaches the destination coordinates. + * + * @method Phaser.Physics.Isometric.Arcade#accelerateToXYZ + * @param {any} displayObject - The display object to move. + * @param {number} x - The x coordinate to accelerate towards. + * @param {number} y - The y coordinate to accelerate towards. + * @param {number} z - The z coordinate to accelerate towards. + * @param {number} [speed=60] - The speed it will accelerate in pixels per second. + * @param {number} [xSpeedMax=500] - The maximum x velocity the display object can reach. + * @param {number} [ySpeedMax=500] - The maximum y velocity the display object can reach. + * @param {number} [zSpeedMax=500] - The maximum z velocity the display object can reach. + * @return {number} The angle (in radians) that the object should be visually set to in order to match its new trajectory. + */ + accelerateToXYZ: function(displayObject, x, y, z, speed, xSpeedMax, ySpeedMax, zSpeedMax) { + + if (speed === undefined) { speed = 60; } + if (xSpeedMax === undefined) { xSpeedMax = 500; } + if (ySpeedMax === undefined) { ySpeedMax = 500; } + if (zSpeedMax === undefined) { zSpeedMax = 500; } + + var a = this.anglesToXYZ(displayObject.body, x, y,z); + var v = this.velocityFromAngles(a.theta,a.phi,speed); + + displayObject.body.acceleration.setTo(v.x,v.y,v.z); + displayObject.body.maxVelocity.setTo(xSpeedMax, ySpeedMax, zSpeedMax); + + return a.theta; + + }, + + /** + * Move the given display object towards the x/y coordinates at a steady velocity. + * If you specify a maxTime then it will adjust the speed (over-writing what you set) so it arrives at the destination in that number of seconds. + * Timings are approximate due to the way browser timers work. Allow for a variance of +- 50ms. + * Note: The display object does not continuously track the target. If the target changes location during transit the display object will not modify its course. + * Note: The display object doesn't stop moving once it reaches the destination coordinates. + * Note: Doesn't take into account acceleration, maxVelocity or drag (if you've set drag or acceleration too high this object may not move at all) + * + * @method Phaser.Physics.Isometric.Arcade#moveToXYZ + * @param {any} displayObject - The display object to move, must have an isoArcade body. + * @param {number} x - The x coordinate to move towards. + * @param {number} y - The y coordinate to move towards. + * @param {number} z - The z coordinate to move towards. + * @param {number} [speed=60] - The speed it will move, in pixels per second (default is 60 pixels/sec) + * @param {number} [maxTime=0] - Time given in milliseconds (1000 = 1 sec). If set the speed is adjusted so the object will arrive at destination in the given number of ms. + * @return {number} The angle (in radians) that the object should be visually set to in order to match its new velocity. + */ + moveToXYZ: function(displayObject, x, y, z, speed, maxTime) { + + if (typeof speed === 'undefined') { + speed = 60; + } + if (typeof maxTime === 'undefined') { + maxTime = 0; + } + + if (maxTime > 0) { + // We know how many pixels we need to move, but how fast? + speed = this.distanceToXYZ(displayObject.body, x, y ,z) / (maxTime / 1000); + } + var a = this.anglesToXYZ(displayObject.body, x, y,z); + var v = this.velocityFromAngles(a.theta,a.phi,speed); + + console.log(displayObject.id,v); + displayObject.body.velocity.copyFrom(v); + console.log(displayObject.id,displayObject.body.velocity); + + return a.theta; + + }, + + /** + * Move the given display object towards the destination object at a steady velocity. + * If you specify a maxTime then it will adjust the speed (overwriting what you set) so it arrives at the destination in that number of seconds. + * Timings are approximate due to the way browser timers work. Allow for a variance of +- 50ms. + * Note: The display object does not continuously track the target. If the target changes location during transit the display object will not modify its course. + * Note: The display object doesn't stop moving once it reaches the destination coordinates. + * Note: Doesn't take into account acceleration, maxVelocity or drag (if you've set drag or acceleration too high this object may not move at all) + * + * @method Phaser.Physics.Isometric.Arcade#moveToObject + * @param {any} displayObject - The display object to move. + * @param {any} destination - The display object to move towards. Can be any object but must have visible x/y/z properties. + * @param {number} [speed=60] - The speed it will move, in pixels per second (default is 60 pixels/sec) + * @param {number} [maxTime=0] - Time given in milliseconds (1000 = 1 sec). If set the speed is adjusted so the object will arrive at destination in the given number of ms. + * @return {number} The angle (in radians) that the object should be visually set to in order to match its new velocity. + */ + moveToObject: function (displayObject, destination, speed, maxTime) { + + return this.moveToXYZ(displayObject, destination.x, destination.y, destination.z, speed, maxTime); + + }, + + /** + * For isoArcade move the given display object towards the pointer at a steady velocity. If no pointer is given it will use Phaser.Input.activePointer. + * If you specify a maxTime then it will adjust the speed (over-writing what you set) so it arrives at the destination in that number of seconds. + * Timings are approximate due to the way browser timers work. Allow for a variance of +- 50ms. + * Note: The display object does not continuously track the target. If the target changes location during transit the display object will not modify its course. + * Note: The display object doesn't stop moving once it reaches the destination coordinates. + * + * @method Phaser.Physics.Isometric.Arcade#moveToPointer + * @param {any} displayObject - The display object to move. + * @param {number} [speed=60] - The speed it will move, in pixels per second (default is 60 pixels/sec) + * @param {Phaser.Pointer} [pointer] - The pointer to move towards. Defaults to Phaser.Input.activePointer. + * @param {number} [maxTime=0] - Time given in milliseconds (1000 = 1 sec). If set the speed is adjusted so the object will arrive at destination in the given number of ms. + * @return {number} The angle (in radians) that the object should be visually set to in order to match its new velocity. + */ + moveToPointer: function(displayObject, speed, pointer, maxTime) { + + pointer = pointer || this.game.input.activePointer; + var isoPointer = this.game.iso.unproject(pointer.position,undefined,displayObject.body.z); + isoPointer.z = displayObject.body.z; + return this.moveToXYZ(displayObject, isoPointer.x, isoPointer.y, isoPointer.z, speed, maxTime); } + }; Phaser.Physics.prototype.isoArcade = null;