Skip to content

Commit 50e126f

Browse files
committed
* Body has two new properties: left and top. These are the same as Body.x and Body.y but allow you to pass the Body to geometry level functions such as Circle.contains.
* Body.setCircle allows you to define a Body as using a circle to collide with instead of a rectangle. You can set the radius of the collision circle and an offset. * Body.render now renders both circle and rectangle body shapes to the Debug canvas. * World.intersects has been updated to support both circle and rectangle body shapes, and supports quick-paths for circle vs. circle and rect vs. rect checks. * World.circleBodyIntersects is a new method that checks for intersection between a Body that has been defined as a circle, and a normal rectangle based Body. This is used internally by World.intersects, but exposed for direct calls as well.
1 parent b162ca4 commit 50e126f

3 files changed

Lines changed: 194 additions & 45 deletions

File tree

README.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,15 @@ You can read all about the philosophy behind Lazer [here](http://phaser.io/news/
331331
* SoundManager.muteOnPause is a new boolean that allows you to control if the Sound system gets muted automatically when a Phaser game pauses, such as when it loses focus. You may need to set this to `false` if you wish to control the audio system from outside of your Phaser game, i.e. from DOM buttons or similar (#2382)
332332
* You can now pass a TilemapLayer as a Texture to a TileSprite. A limitation of this is that if you pass it to a TileSprite it will make a fill pattern from the TilemapLayer at that instant it's passed, and it won't keep track of the layer in future should it update (thanks @jdnichollsc #1989)
333333

334+
### New Arcade Physics Features
335+
336+
* Body has two new properties: `left` and `top`. These are the same as `Body.x` and `Body.y` but allow you to pass the Body to geometry level functions such as Circle.contains.
337+
* Body.setCircle allows you to define a Body as using a circle to collide with instead of a rectangle. You can set the radius of the collision circle and an offset.
338+
* Body.render now renders both circle and rectangle body shapes to the Debug canvas.
339+
* World.intersects has been updated to support both circle and rectangle body shapes, and supports quick-paths for circle vs. circle and rect vs. rect checks.
340+
* World.circleBodyIntersects is a new method that checks for intersection between a Body that has been defined as a circle, and a normal rectangle based Body. This is used internally by World.intersects, but exposed for direct calls as well.
341+
342+
334343
### Updates
335344

336345
* TypeScript definitions fixes and updates (thanks @clark-stevenson)
@@ -352,7 +361,7 @@ You can read all about the philosophy behind Lazer [here](http://phaser.io/news/
352361
* Video now uses MediaStreamTrack.stop() instead of MediaStream.stop() where possible, as the later is now deprecated in some browsers (thanks @stoneman1 #2371)
353362
* The Physics Manager will now throw a console warning if you try to enable a physics body using an unknown physics engine type (thanks @jakewilson #2415)
354363
* The Tileset class will tell you the name of the tileset image throwing the uneven size error (thanks @jakewilson #2415)
355-
* Emitter.start when used with a false `explode` parameter would cummulatively add particles to the current total. With quantity 10 the first call would emit 10 particles, the next 20, and so on. Calls to start will now reset the quantity each time. This is a behaviour change from earlier versions, so if you relied on the old way please account for it in your code (thanks @BdR76 #2187)
364+
* Emitter.start when used with a false `explode` parameter would cummulatively add particles to the current total. With quantity 10 the first call would emit 10 particles, the next 20, and so on. Calls to start will now reset the quantity each time. This is a behavior change from earlier versions, so if you relied on the old way please account for it in your code (thanks @BdR76 #2187)
356365

357366
### Bug Fixes
358367

src/physics/arcade/Body.js

Lines changed: 123 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,22 @@ Phaser.Physics.Arcade.Body = function (sprite) {
3636
this.enable = true;
3737

3838
/**
39-
* @property {boolean} isCircle - Testing.
39+
* If `true` this Body is using circular collision detection. If `false` it is using rectangular.
40+
* Use `Body.setCircle` to control the collision shape this Body uses.
41+
* @property {boolean} isCircle
4042
* @default
43+
* @readOnly
4144
*/
4245
this.isCircle = false;
4346

47+
/**
48+
* The radius of the circular collision shape this Body is using if Body.setCircle has been enabled.
49+
* If you wish to change the radius then call `setCircle` again with the new value.
50+
* If you wish to stop the Body using a circle then call `setCircle` with a radius of zero (or undefined).
51+
* @property {float} radius
52+
* @default
53+
* @readOnly
54+
*/
4455
this.radius = 0;
4556

4657
/**
@@ -576,26 +587,6 @@ Phaser.Physics.Arcade.Body.prototype = {
576587

577588
},
578589

579-
/**
580-
* Destroys this Body.
581-
*
582-
* First it calls Group.removeFromHash if the Game Object this Body belongs to is part of a Group.
583-
* Then it nulls the Game Objects body reference, and nulls this Body.sprite reference.
584-
*
585-
* @method Phaser.Physics.Arcade.Body#destroy
586-
*/
587-
destroy: function () {
588-
589-
if (this.sprite.parent && this.sprite.parent instanceof Phaser.Group)
590-
{
591-
this.sprite.parent.removeFromHash(this.sprite);
592-
}
593-
594-
this.sprite.body = null;
595-
this.sprite = null;
596-
597-
},
598-
599590
/**
600591
* Internal method.
601592
*
@@ -641,6 +632,9 @@ Phaser.Physics.Arcade.Body.prototype = {
641632
* So it could be smaller or larger than the parent Sprite. You can also control the x and y offset, which
642633
* is the position of the Body relative to the top-left of the Sprite.
643634
*
635+
* Calling `setSize` will have no effect if you have previously used `Body.setCircle`. To change a collision
636+
* circle use `setCircle` instead.
637+
*
644638
* @method Phaser.Physics.Arcade.Body#setSize
645639
* @param {number} width - The width of the Body.
646640
* @param {number} height - The height of the Body.
@@ -649,6 +643,11 @@ Phaser.Physics.Arcade.Body.prototype = {
649643
*/
650644
setSize: function (width, height, offsetX, offsetY) {
651645

646+
if (this.isCircle)
647+
{
648+
return;
649+
}
650+
652651
if (offsetX === undefined) { offsetX = this.offset.x; }
653652
if (offsetY === undefined) { offsetY = this.offset.y; }
654653

@@ -664,6 +663,47 @@ Phaser.Physics.Arcade.Body.prototype = {
664663

665664
},
666665

666+
/**
667+
* Sets this Body as using a circle, of the given radius, for all collision detection instead of a rectangle.
668+
* The radius is given in pixels and is the distance from the center of the circle to the edge.
669+
*
670+
* You can also control the x and y offset, which is the position of the Body relative to the top-left of the Sprite.
671+
*
672+
* @method Phaser.Physics.Arcade.Body#setCircle
673+
* @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.
674+
* @param {number} [offsetX] - The X offset of the Body from the Sprite position.
675+
* @param {number} [offsetY] - The Y offset of the Body from the Sprite position.
676+
*/
677+
setCircle: function (radius, offsetX, offsetY) {
678+
679+
if (offsetX === undefined) { offsetX = this.offset.x; }
680+
if (offsetY === undefined) { offsetY = this.offset.y; }
681+
682+
if (radius > 0)
683+
{
684+
this.isCircle = true;
685+
this.radius = radius;
686+
687+
this.sourceWidth = radius * 2;
688+
this.sourceHeight = radius * 2;
689+
690+
this.width = this.sourceWidth * this._sx;
691+
this.height = this.sourceHeight * this._sy;
692+
693+
this.halfWidth = Math.floor(this.width / 2);
694+
this.halfHeight = Math.floor(this.height / 2);
695+
696+
this.offset.setTo(offsetX, offsetY);
697+
698+
this.center.setTo(this.position.x + this.halfWidth, this.position.y + this.halfHeight);
699+
}
700+
else
701+
{
702+
this.isCircle = false;
703+
}
704+
705+
},
706+
667707
/**
668708
* Resets all Body values (velocity, acceleration, rotation, etc)
669709
*
@@ -804,20 +844,39 @@ Phaser.Physics.Arcade.Body.prototype = {
804844

805845
return this.rotation - this.preRotation;
806846

847+
},
848+
849+
/**
850+
* Destroys this Body.
851+
*
852+
* First it calls Group.removeFromHash if the Game Object this Body belongs to is part of a Group.
853+
* Then it nulls the Game Objects body reference, and nulls this Body.sprite reference.
854+
*
855+
* @method Phaser.Physics.Arcade.Body#destroy
856+
*/
857+
destroy: function () {
858+
859+
if (this.sprite.parent && this.sprite.parent instanceof Phaser.Group)
860+
{
861+
this.sprite.parent.removeFromHash(this.sprite);
862+
}
863+
864+
this.sprite.body = null;
865+
this.sprite = null;
866+
807867
}
808868

809869
};
810870

811871
/**
812-
* @name Phaser.Physics.Arcade.Body#bottom
813-
* @property {number} bottom - The bottom value of this Body (same as Body.y + Body.height)
814-
* @readonly
872+
* @name Phaser.Physics.Arcade.Body#left
873+
* @property {number} left - The x position of the Body. The same as `Body.x`.
815874
*/
816-
Object.defineProperty(Phaser.Physics.Arcade.Body.prototype, "bottom", {
875+
Object.defineProperty(Phaser.Physics.Arcade.Body.prototype, "left", {
817876

818877
get: function () {
819878

820-
return this.position.y + this.height;
879+
return this.position.x;
821880

822881
}
823882

@@ -838,6 +897,35 @@ Object.defineProperty(Phaser.Physics.Arcade.Body.prototype, "right", {
838897

839898
});
840899

900+
/**
901+
* @name Phaser.Physics.Arcade.Body#top
902+
* @property {number} top - The y position of the Body. The same as `Body.y`.
903+
*/
904+
Object.defineProperty(Phaser.Physics.Arcade.Body.prototype, "top", {
905+
906+
get: function () {
907+
908+
return this.position.y;
909+
910+
}
911+
912+
});
913+
914+
/**
915+
* @name Phaser.Physics.Arcade.Body#bottom
916+
* @property {number} bottom - The bottom value of this Body (same as Body.y + Body.height)
917+
* @readonly
918+
*/
919+
Object.defineProperty(Phaser.Physics.Arcade.Body.prototype, "bottom", {
920+
921+
get: function () {
922+
923+
return this.position.y + this.height;
924+
925+
}
926+
927+
});
928+
841929
/**
842930
* @name Phaser.Physics.Arcade.Body#x
843931
* @property {number} x - The x position.
@@ -892,20 +980,27 @@ Phaser.Physics.Arcade.Body.render = function (context, body, color, filled) {
892980

893981
color = color || 'rgba(0,255,0,0.4)';
894982

895-
if (this.isCircle)
983+
if (body.isCircle)
896984
{
985+
context.save();
986+
context.setTransform(1, 0, 0, 1, 0, 0);
987+
897988
context.beginPath();
898-
context.arc(body.position.x - body.game.camera.x, body.position.y - body.game.camera.y, body.radius, 0, 6.283185307179586, false);
989+
context.arc(body.center.x - body.game.camera.x, body.center.y - body.game.camera.y, body.radius, 0, 2 * Math.PI);
899990
context.closePath();
900991

901992
if (filled)
902993
{
903994
context.fillStyle = color;
995+
context.fill();
904996
}
905997
else
906998
{
907999
context.strokeStyle = color;
1000+
context.stroke();
9081001
}
1002+
1003+
context.restore();
9091004
}
9101005
else
9111006
{

src/physics/arcade/World.js

Lines changed: 61 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -977,33 +977,78 @@ Phaser.Physics.Arcade.prototype = {
977977
* Check for intersection against two bodies.
978978
*
979979
* @method Phaser.Physics.Arcade#intersects
980-
* @param {Phaser.Physics.Arcade.Body} body1 - The Body object to check.
981-
* @param {Phaser.Physics.Arcade.Body} body2 - The Body object to check.
980+
* @param {Phaser.Physics.Arcade.Body} body1 - The first Body object to check.
981+
* @param {Phaser.Physics.Arcade.Body} body2 - The second Body object to check.
982982
* @return {boolean} True if they intersect, otherwise false.
983983
*/
984984
intersects: function (body1, body2) {
985985

986-
if (body1.right <= body2.position.x)
986+
if (body1.isCircle)
987987
{
988-
return false;
988+
if (body2.isCircle)
989+
{
990+
// Circle vs. Circle
991+
return Phaser.Math.distance(body1.center.x, body1.center.y, body2.center.x, body2.center.y) <= (body1.radius + body2.radius);
992+
}
993+
else
994+
{
995+
// Circle vs. Rect
996+
return this.circleBodyIntersects(body1, body2);
997+
}
989998
}
990-
991-
if (body1.bottom <= body2.position.y)
999+
else
9921000
{
993-
return false;
994-
}
1001+
if (body2.isCircle)
1002+
{
1003+
// Rect vs. Circle
1004+
return this.circleBodyIntersects(body2, body1);
1005+
}
1006+
else
1007+
{
1008+
// Rect vs. Rect
1009+
if (body1.right <= body2.position.x)
1010+
{
1011+
return false;
1012+
}
9951013

996-
if (body1.position.x >= body2.right)
997-
{
998-
return false;
999-
}
1014+
if (body1.bottom <= body2.position.y)
1015+
{
1016+
return false;
1017+
}
10001018

1001-
if (body1.position.y >= body2.bottom)
1002-
{
1003-
return false;
1019+
if (body1.position.x >= body2.right)
1020+
{
1021+
return false;
1022+
}
1023+
1024+
if (body1.position.y >= body2.bottom)
1025+
{
1026+
return false;
1027+
}
1028+
1029+
return true;
1030+
}
10041031
}
10051032

1006-
return true;
1033+
},
1034+
1035+
/**
1036+
* Checks to see if a circular Body intersects with a Rectangular Body.
1037+
*
1038+
* @method Phaser.Physics.Arcade#circleBodyIntersects
1039+
* @param {Phaser.Physics.Arcade.Body} circle - The Body with `isCircle` set.
1040+
* @param {Phaser.Physics.Arcade.Body} body - The Body with `isCircle` not set (i.e. uses Rectangle shape)
1041+
* @return {boolean} Returns true if the bodies intersect, otherwise false.
1042+
*/
1043+
circleBodyIntersects: function (circle, body) {
1044+
1045+
var x = Phaser.Math.clamp(circle.center.x, body.left, body.right);
1046+
var y = Phaser.Math.clamp(circle.center.y, body.top, body.bottom);
1047+
1048+
var dx = (circle.center.x - x) * (circle.center.x - x);
1049+
var dy = (circle.center.y - y) * (circle.center.y - y);
1050+
1051+
return (dx + dy) <= (circle.radius * circle.radius);
10071052

10081053
},
10091054

0 commit comments

Comments
 (0)