ArcadePhysics.Body.setCircle is a new method that allows you to define an Arcade Physics Body as being a circle instead of a rectangle. You can control the radius of the body and the offset from the parent sprite.

ArcadePhysics.World.separateCircle is a new method that handles all circular body collisions internally within Arcade Physics (thanks @VitaZheltyakov)

All of the Arcade Physics internal methods, such as `collideGroupVsSelf`, `collideSpriteVsSprite` and so on, have been updated to work with circular body shapes (thanks @VitaZheltyakov)

ArcadePhysics.Body.onWorldBounds is a new Signal that is dispatched whenever the Body collides with the world bounds, something that was previously difficult to detect. Due to the potentially high volume of signals this could create it is disabled by default. To use this feature set this property to a Phaser.Signal: `sprite.body.onWorldBounds = new Phaser.Signal()` and it will be called when a collision happens, passing one argument: the sprite on which it occurred.
This commit is contained in:
Richard Davey 2016-07-07 21:58:39 +01:00
parent 261155a683
commit 6ba3c9401f
4 changed files with 84 additions and 55 deletions

View file

@ -325,6 +325,10 @@ You can read all about the philosophy behind Lazer [here](http://phaser.io/news/
* Phaser.ArrayUtils.rotateRight is the opposite of ArrayUtils.rotate. It takes an array, removes the element from the end of the array, and inserts it at the start, shifting everything else 1 space in the process.
* Phaser.ArrayUtils.rotateLeft is the new name for Phaser.ArrayUtils.rotate. The old method is now deprecated (but still available in this release)
* Phaser.Color.toABGR converts RGBA components to a 32 bit integer in AABBGGRR format.
* ArcadePhysics.Body.setCircle is a new method that allows you to define an Arcade Physics Body as being a circle instead of a rectangle. You can control the radius of the body and the offset from the parent sprite.
* ArcadePhysics.World.separateCircle is a new method that handles all circular body collisions internally within Arcade Physics (thanks @VitaZheltyakov)
* All of the Arcade Physics internal methods, such as `collideGroupVsSelf`, `collideSpriteVsSprite` and so on, have been updated to work with circular body shapes (thanks @VitaZheltyakov)
* ArcadePhysics.Body.onWorldBounds is a new Signal that is dispatched whenever the Body collides with the world bounds, something that was previously difficult to detect. Due to the potentially high volume of signals this could create it is disabled by default. To use this feature set this property to a Phaser.Signal: `sprite.body.onWorldBounds = new Phaser.Signal()` and it will be called when a collision happens, passing one argument: the sprite on which it occurred.
### Updates

View file

@ -48,7 +48,7 @@ Phaser.Physics.Arcade.Body = function (sprite) {
* The radius of the circular collision shape this Body is using if Body.setCircle has been enabled.
* If you wish to change the radius then call `setCircle` again with the new value.
* If you wish to stop the Body using a circle then call `setCircle` with a radius of zero (or undefined).
* @property {float} radius
* @property {number} radius
* @default
* @readOnly
*/
@ -147,12 +147,12 @@ Phaser.Physics.Arcade.Body = function (sprite) {
* @property {Phaser.Point} newVelocity - The new velocity. Calculated during the Body.preUpdate and applied to its position.
* @readonly
*/
this.newVelocity = new Phaser.Point(0, 0);
this.newVelocity = new Phaser.Point();
/**
* @property {Phaser.Point} deltaMax - The Sprite position is updated based on the delta x/y values. You can set a cap on those (both +-) using deltaMax.
*/
this.deltaMax = new Phaser.Point(0, 0);
this.deltaMax = new Phaser.Point();
/**
* @property {Phaser.Point} acceleration - The acceleration is the rate of change of the velocity. Measured in pixels per second squared.
@ -173,7 +173,7 @@ Phaser.Physics.Arcade.Body = function (sprite) {
/**
* @property {Phaser.Point} gravity - A local gravity applied to this Body. If non-zero this over rides any world gravity, unless Body.allowGravity is set to false.
*/
this.gravity = new Phaser.Point(0, 0);
this.gravity = new Phaser.Point();
/**
* @property {Phaser.Point} bounce - The elasticity of the Body when colliding. bounce.x/y = 1 means full rebound, bounce.x/y = 0.5 means 50% rebound velocity.
@ -188,6 +188,16 @@ Phaser.Physics.Arcade.Body = function (sprite) {
*/
this.worldBounce = null;
/**
* A Signal that is dispatched when this Body collides with the world bounds.
* Due to the potentially high volume of signals this could create it is disabled by default.
* To use this feature set this property to a Phaser.Signal:
* `sprite.body.onWorldBounds = new Phaser.Signal()`
* and it will be called when a collision happens, passing one argument: the sprite on which it occurred.
* @property {Phaser.Signal} onWorldBounds
*/
this.onWorldBounds = null;
/**
* @property {Phaser.Point} maxVelocity - The maximum velocity in pixels per second sq. that the Body can reach.
* @default
@ -569,7 +579,10 @@ Phaser.Physics.Arcade.Body.prototype = {
if (this.collideWorldBounds)
{
this.checkWorldBounds();
if (this.checkWorldBounds() && this.onWorldBounds)
{
this.onWorldBounds.dispatch(this.sprite);
}
}
}
@ -740,6 +753,7 @@ Phaser.Physics.Arcade.Body.prototype = {
*
* @method Phaser.Physics.Arcade.Body#checkWorldBounds
* @protected
* @return {boolean} True if the Body collided with the world bounds, otherwise false.
*/
checkWorldBounds: function () {
@ -752,11 +766,12 @@ Phaser.Physics.Arcade.Body.prototype = {
if (this.isCircle)
{
var bodyBounds = {};
bodyBounds.x = this.center.x - this.radius;
bodyBounds.y = this.center.y - this.radius;
bodyBounds.right = this.center.x + this.radius;
bodyBounds.bottom = this.center.y + this.radius;
var bodyBounds = {
x: this.center.x - this.radius,
y: this.center.y - this.radius,
right: this.center.x + this.radius,
bottom: this.center.y + this.radius
};
if (bodyBounds.x < bounds.x && check.left)
{
@ -813,6 +828,8 @@ Phaser.Physics.Arcade.Body.prototype = {
}
}
return (this.blocked.up || this.blocked.down || this.blocked.left || this.blocked.right);
},
/**
@ -991,6 +1008,9 @@ Phaser.Physics.Arcade.Body.prototype = {
* 24 is the horizontal offset of the Body from the top-left of the Sprites texture, and 34
* is the vertical offset.
*
* Calling `setSize` on a Body that has already had `setCircle` will reset all of the Circle
* properties, making this Body rectangular again.
*
* @method Phaser.Physics.Arcade.Body#setSize
* @param {number} width - The width of the Body.
* @param {number} height - The height of the Body.
@ -999,11 +1019,6 @@ Phaser.Physics.Arcade.Body.prototype = {
*/
setSize: function (width, height, offsetX, offsetY) {
if (this.isCircle)
{
return;
}
if (offsetX === undefined) { offsetX = this.offset.x; }
if (offsetY === undefined) { offsetY = this.offset.y; }
@ -1017,6 +1032,9 @@ Phaser.Physics.Arcade.Body.prototype = {
this.center.setTo(this.position.x + this.halfWidth, this.position.y + this.halfHeight);
this.isCircle = false;
this.radius = 0;
},
/**
@ -1025,6 +1043,11 @@ Phaser.Physics.Arcade.Body.prototype = {
*
* You can also control the x and y offset, which is the position of the Body relative to the top-left of the Sprite.
*
* To change a Body back to being rectangular again call `Body.setSize`.
*
* Note: Circular collision only happens with other Arcade Physics bodies, it does not
* work against tile maps, where rectangular collision is the only method supported.
*
* @method Phaser.Physics.Arcade.Body#setCircle
* @param {number} [radius] - The radius of the Body in pixels. Pass a value of zero / undefined, to stop the Body using a circle for collision.
* @param {number} [offsetX] - The X offset of the Body from the Sprite position.
@ -1097,6 +1120,7 @@ Phaser.Physics.Arcade.Body.prototype = {
/**
* Returns the bounds of this physics body.
*
* Only used internally by the World collision methods.
*
* @method Phaser.Physics.Arcade.Body#getBounds
@ -1368,41 +1392,31 @@ Phaser.Physics.Arcade.Body.render = function (context, body, color, filled) {
color = color || 'rgba(0,255,0,0.4)';
context.fillStyle = color;
context.strokeStyle = color;
if (body.isCircle)
{
context.save();
context.setTransform(1, 0, 0, 1, 0, 0);
context.beginPath();
context.arc(body.center.x - body.game.camera.x, body.center.y - body.game.camera.y, body.radius, 0, 2 * Math.PI);
context.closePath();
if (filled)
{
context.fillStyle = color;
context.fill();
}
else
{
context.strokeStyle = color;
context.stroke();
}
// context.strokeStyle = '#ffff00';
// context.strokeRect(body.position.x - body.game.camera.x, body.position.y - body.game.camera.y, body.width, body.height);
context.restore();
}
else
{
if (filled)
{
context.fillStyle = color;
context.fillRect(body.position.x - body.game.camera.x, body.position.y - body.game.camera.y, body.width, body.height);
}
else
{
context.strokeStyle = color;
context.strokeRect(body.position.x - body.game.camera.x, body.position.y - body.game.camera.y, body.width, body.height);
}
}

View file

@ -710,14 +710,16 @@ Phaser.Physics.Arcade.prototype = {
for (var i = 0; i < group.hash.length; i++)
{
var object1 = group.hash[i];
// Skip duff entries - we can't check a non-existent sprite or one with no body
if (!group.hash[i] || !group.hash[i].exists || !group.hash[i].body)
if (!object1 || !object1.exists || !object1.body)
{
continue;
}
// Inject the Body bounds data into the bounds object
group.hash[i].body.getBounds(bounds);
bounds = object1.body.getBounds(bounds);
// Skip items either side of the sprite
if (this.sortDirection === Phaser.Physics.Arcade.LEFT_RIGHT)
@ -765,7 +767,7 @@ Phaser.Physics.Arcade.prototype = {
}
}
this.collideSpriteVsSprite(sprite, group.hash[i], collideCallback, processCallback, callbackContext, overlapOnly);
this.collideSpriteVsSprite(sprite, object1, collideCallback, processCallback, callbackContext, overlapOnly);
}
}
else
@ -827,8 +829,7 @@ Phaser.Physics.Arcade.prototype = {
}
// Inject the Body bounds data into the bounds1 object
object1.body.getBounds(bounds1);
// bounds1.ref = object1;
bounds1 = object1.body.getBounds(bounds1);
for (var j = i + 1; j < group.hash.length; j++)
{
@ -842,8 +843,7 @@ Phaser.Physics.Arcade.prototype = {
}
// Inject the Body bounds data into the bounds2 object
object2.body.getBounds(bounds2);
// bounds2.ref = object2;
bounds2 = object2.body.getBounds(bounds2);
// Skip items either side of the sprite
if (this.sortDirection === Phaser.Physics.Arcade.LEFT_RIGHT)
@ -967,23 +967,30 @@ Phaser.Physics.Arcade.prototype = {
// We define the behavior of bodies in a collision circle and rectangle
// If a collision occurs in the corner points of the rectangle, the body behave like circles
// Either body1 or body2 is a circle
if (body1.isCircle !== body2.isCircle)
{
var bodyRect = (body1.isCircle) ? body2 : body1;
var bodyCircle = (body1.isCircle) ? body1 : body2;
var rect = {
x: (body2.isCircle) ? body1.position.x : body2.position.x,
y: (body2.isCircle) ? body1.position.y : body2.position.y,
right: (body2.isCircle) ? body1.right : body2.right,
bottom: (body2.isCircle) ? body1.bottom : body2.bottom
x: bodyRect.x,
y: bodyRect.y,
right: bodyRect.right,
bottom: bodyRect.bottom
};
var circle = {
x: (body1.isCircle) ? (body1.position.x + body1.radius) : (body2.position.x + body2.radius),
y: (body1.isCircle) ? (body1.position.y + body1.radius) : (body2.position.y + body2.radius)
x: bodyCircle.x + bodyCircle.radius,
y: bodyCircle.y + bodyCircle.radius
};
if (circle.y < rect.y || circle.y > rect.bottom)
{
return this.separateCircle(body1, body2, overlapOnly);
if (circle.x < rect.x || circle.x > rect.right)
{
return this.separateCircle(body1, body2, overlapOnly);
}
}
}
@ -1175,13 +1182,13 @@ Phaser.Physics.Arcade.prototype = {
// Transform the velocity vector to the coordinate system oriented along the direction of impact.
// This is done to eliminate the vertical component of the velocity
var v1 = {
x : body1.velocity.x * Math.cos(angleCollision) + body1.velocity.y * Math.sin(angleCollision),
y : body1.velocity.x * Math.sin(angleCollision) - body1.velocity.y * Math.cos(angleCollision)
x: body1.velocity.x * Math.cos(angleCollision) + body1.velocity.y * Math.sin(angleCollision),
y: body1.velocity.x * Math.sin(angleCollision) - body1.velocity.y * Math.cos(angleCollision)
};
var v2 = {
x : body2.velocity.x * Math.cos(angleCollision) + body2.velocity.y * Math.sin(angleCollision),
y : body2.velocity.x * Math.sin(angleCollision) - body2.velocity.y * Math.cos(angleCollision)
x: body2.velocity.x * Math.cos(angleCollision) + body2.velocity.y * Math.sin(angleCollision),
y: body2.velocity.x * Math.sin(angleCollision) - body2.velocity.y * Math.cos(angleCollision)
};
// We expect the new velocity after impact
@ -1207,38 +1214,38 @@ Phaser.Physics.Arcade.prototype = {
if (Math.abs(angleCollision) < Math.PI / 2)
{
if (body1.velocity.x > 0 && !body1.immovable && body2.velocity.x > body1.velocity.x)
if ((body1.velocity.x > 0) && !body1.immovable && (body2.velocity.x > body1.velocity.x))
{
body1.velocity.x *= -1;
}
else if (body2.velocity.x < 0 && !body2.immovable && body1.velocity.x < body2.velocity.x)
else if ((body2.velocity.x < 0) && !body2.immovable && (body1.velocity.x < body2.velocity.x))
{
body2.velocity.x *= -1;
}
else if (body1.velocity.y > 0 && !body1.immovable && body2.velocity.y > body1.velocity.y)
else if ((body1.velocity.y > 0) && !body1.immovable && (body2.velocity.y > body1.velocity.y))
{
body1.velocity.y *= -1;
}
else if (body2.velocity.y < 0 && !body2.immovable && body1.velocity.y < body2.velocity.y)
else if ((body2.velocity.y < 0) && !body2.immovable && (body1.velocity.y < body2.velocity.y))
{
body2.velocity.y *= -1;
}
}
else if (Math.abs(angleCollision) > Math.PI / 2)
{
if (body1.velocity.x < 0 && !body1.immovable && body2.velocity.x < body1.velocity.x)
if ((body1.velocity.x < 0) && !body1.immovable && (body2.velocity.x < body1.velocity.x))
{
body1.velocity.x *= -1;
}
else if (body2.velocity.x > 0 && !body2.immovable && body1.velocity.x > body2.velocity.x)
else if ((body2.velocity.x > 0) && !body2.immovable && (body1.velocity.x > body2.velocity.x))
{
body2.velocity.x *= -1;
}
else if (body1.velocity.y < 0 && !body1.immovable && body2.velocity.y < body1.velocity.y)
else if ((body1.velocity.y < 0) && !body1.immovable && (body2.velocity.y < body1.velocity.y))
{
body1.velocity.y *= -1;
}
else if (body2.velocity.y > 0 && !body2.immovable && body1.velocity.x > body2.velocity.y)
else if ((body2.velocity.y > 0) && !body2.immovable && (body1.velocity.x > body2.velocity.y))
{
body2.velocity.y *= -1;
}

View file

@ -1,7 +1,7 @@
/// <reference path="pixi.d.ts" />
/// <reference path="p2.d.ts" />
// Type definitions for Phaser 2.5.1 - 2nd July 2016
// Type definitions for Phaser 2.6.0 - 7th July 2016
// Project: https://github.com/photonstorm/phaser
declare module "phaser" {
@ -3001,6 +3001,7 @@ declare module Phaser {
halfHeight: number;
height: number;
immovable: boolean;
isCircle: boolean;
isMoving: boolean;
mass: number;
maxAngular: number;
@ -3017,6 +3018,7 @@ declare module Phaser {
position: Phaser.Point;
preRotation: number;
prev: Phaser.Point;
radius: number;
right: number;
rotation: number;
skipQuadTree: boolean;
@ -3043,6 +3045,7 @@ declare module Phaser {
deltaAbsX(): number;
deltaAbsY(): number;
destroy(): void;
getBounds(obj: any): any;
hitTest(x: number, y: number): boolean;
moveFrom(duration: number, speed?: number, direction?: number): boolean;
moveTo(duration: number, distance: number, direction?: number): boolean;
@ -3053,6 +3056,7 @@ declare module Phaser {
render(context: any, body: Phaser.Physics.Arcade.Body, color?: string, filled?: boolean): void;
renderBodyInfo(debug: Phaser.Utils.Debug, body: Phaser.Physics.Arcade.Body): void;
reset(x: number, y: number): void;
setCircle(radius: number, offsetX?: number, offsetY?: number): void;
setSize(width: number, height: number, offsetX?: number, offsetY?: number): void;
updateBounds(): boolean;