n-way collision checks and onBeginContact and onEndContact done and working. Also fixed issue in TweenManager.removeAll.

This commit is contained in:
photonstorm 2014-02-05 02:39:03 +00:00
parent 4502ca1bf6
commit 84f0f00f49
10 changed files with 492 additions and 286 deletions

View file

@ -60,11 +60,10 @@ Significant API changes:
* The World level quadtree is no longer created, they are now built and ripped down each time you collide a Group, this helps collision accuracy.
* A SAT system has been integrated for Body collision and separation.
* Bodies are no longer added to a world quadtree, so have had all of their quadtree properties removed such as skipQuadtree, quadTreeIndex, etc.
* Body.drag has been removed. Please use the new Body.friction value instead (which is a number value, not a Point object)
* Body.drag has been removed. Please use the new Body.linearDamping value instead (which is a number value, not a Point object)
* Body.embedded and Body.wasTouching have been removed as they are no longer required.
* Body.customSeparateX/Y have been removed as you should now use Body.customSeparateCallback.
* Body.maxVelocity defaults have been removed from 10,000 to 2000.
* Body.friction is new and has a default value of 0.1 - you may need to set this to zero depending on the type of game you're making.
* Body.customSeparateCallback allows you to set your own callback when two Bodies need to separate rather than using the built-in method.
* Body.collideCallback allows you to set a callback that is fired whenever the Body is hit on any of its active faces.
* Body.allowCollision has been renamed to Body.checkCollision.
@ -102,13 +101,13 @@ New features:
* Added a new Project Template "Full Screen Mobile" which you can find in the resources folder. Contains html / css / layout needed for a deployed Phaser game.
* Body.speed - the current speed of the body.
* Body.angle - the current angle the Body is facing based on its velocity. This is not the same as the Sprite angle that may own the body.
* Body.friction - This now replaces Body.drag and provides for a much smoother friction experience.
* Body.linearDamping - This now replaces Body.drag and provides for a much smoother damping (friction) experience.
* Body.minBounceVelocity - If a Body has bounce set, this threshold controls if it should rebound or not. Use it to stop 'jittering' on bounds/tiles with super-low velocities.
* QuadTree.populate - you can pass it a Group and it'll automatically insert all of the children ready for inspection.
* Input.setMoveCallback allows you to set a callback that will be fired each time the activePointer receives a DOM move event.
* Math.distancePow(x1,y1,x2,y2,power) returns the distance between two coordinates at the given power.
* Physics.collideArray(obj, array) for when you want to collide an object against a number of sprites that aren't all in the same Group.
* Physics.overlapArray(obj, array) for when you want to overlap test an object against a number of sprites that aren't all in the same Group.
* Physics.collide now supports the 2nd parameter as an array, for when you want to collide an object against a number of sprites that aren't all in the same Group.
* Physics.overlap now supports the 2nd parameter as an array, for when you want to overlap test an object against a number of sprites that aren't all in the same Group.
* Math.reverseAngle - reverses an angle (in radians).
* Math.normalizeAngle - normalises an angle, now in radians only.
* Math.normalizeLatitude - Normalizes a latitude to the [-90,90] range.
@ -122,6 +121,7 @@ New features:
* Game.disableStep turns core update loop stepping off.
* Debug.renderPhysicsBody(body, color) is extremely useful for debugging the new physics bodies. Will draw the outline + points in the color given.
* Debug.renderBodyInfo(sprite, x, y, color) will display lots of Sprite body data.
* Sprite.events.onBeginContact will be fired when a Body makes contact with another Body. Once contact is over an onEndContact event will be dispatched.
New Examples:
@ -169,7 +169,7 @@ Updates:
* Tweens fire an onLoop event if they are set to repeat. onComplete is now only fired for the final repeat (or never if the repeat is infinite)
* Pointer used to un-pause a paused game every time it was clicked/touched (this avoided some rogue browser plugins). Now only happens if Stage.disableVisibilityChange is true.
* Input doesn't set the cursor to default if it's already set to none.
* You can now collide a group against itself, to have all children collide, and bodies won't check against themselves (thanks cocoademon)
* You can now collide a group against itself. This will check all children against each other, but not themselves (thanks cocoademon)
* RenderTexture.render / renderXY has a new parameter: renderHidden, a boolean which will allow you to render Sprites even if their visible is set to false.
* Added in prototype.constructor definitions to every class (thanks darkoverlordofdata)
* Group.destroy has a new parameter: destroyChildren (boolean) which will optionally call the destroy method of all Group children.
@ -190,6 +190,9 @@ Updates:
* Phaser.Math.minProperty and maxProperty added. Like Math.min/max but can be given a property an an array or list of objects to inspect.
* Added 'full' paramter to Body.reset, allowing you to control if motion or all data is reset or not.
* Exposed Group.pivot and Sprite.pivot to allow you to directly set the pivot points for rotation.
* Swapped to using the native and faster Array.isArray check.
* Added callback context parameter to Tween.onUpdateCallback(callback, context) to avoid having to bind or create anonymous functions.
* Updated TweenManager.removeAll so it flags all tweens as pendingDelete rather than nuking the array, to avoid tween callback array size errors (thanks DarkDev)
Bug Fixes:

View file

@ -4,17 +4,121 @@ var game = new Phaser.Game(800, 600, Phaser.CANVAS, 'phaser-example', { preload:
function preload() {
game.load.spritesheet('gameboy', 'assets/sprites/gameboy_seize_color_40x60.png', 40, 60);
game.load.image('atari', 'assets/sprites/atari130xe.png');
}
var sprite;
var sprite2;
var sprite3;
var reverse = false;
function onBeginContact(a, b) {
console.log('Begin Contact between', a.name, 'and', b.name);
}
function onEndContact(a, b) {
console.log('End Contact between', a.name, 'and', b.name);
}
function create() {
game.stage.backgroundColor = '#124184';
test5();
test8();
}
function test8() {
// A up into B
sprite = game.add.sprite(300, 200, 'atari');
sprite.name = 'atari';
sprite.body.collideWorldBounds = true;
sprite.body.bounce.setTo(1, 1);
sprite.body.checkCollision.up = false;
sprite.body.checkCollision.down = false;
sprite2 = game.add.sprite(350, 400, 'gameboy', 2);
sprite2.name = 'gameboy';
sprite2.body.collideWorldBounds = true;
sprite2.body.bounce.setTo(1, 1);
sprite2.events.onBeginContact.add(onBeginContact, this);
sprite2.events.onEndContact.add(onEndContact, this);
reverse = true;
game.input.onDown.add(launch8, this);
}
function launch8() {
sprite2.body.velocity.x = -100;
sprite2.body.velocity.y = -200;
}
function test7() {
// A down into B
sprite = game.add.sprite(300, 400, 'atari');
sprite.name = 'atari';
sprite.body.collideWorldBounds = true;
sprite.body.bounce.setTo(1, 1);
sprite.body.checkCollision.up = false;
sprite2 = game.add.sprite(350, 100, 'gameboy', 2);
sprite2.name = 'gameboy';
sprite2.body.collideWorldBounds = true;
sprite2.body.bounce.setTo(1, 1);
// reverse = true;
game.input.onDown.add(launch7, this);
}
function launch7() {
sprite2.body.velocity.y = 100;
}
function test6() {
// Offset Down Collision false
sprite = game.add.sprite(100, 300, 'atari');
sprite.name = 'atari';
sprite.body.collideWorldBounds = true;
sprite.body.bounce.setTo(1, 1);
sprite.body.checkCollision.left = false;
sprite.body.checkCollision.right = false;
sprite2 = game.add.sprite(500, 330, 'gameboy', 2);
// sprite2 = game.add.sprite(500, 530, 'gameboy', 2);
sprite2.name = 'gameboy';
sprite2.body.collideWorldBounds = true;
sprite2.body.bounce.setTo(1, 1);
sprite3 = game.add.sprite(400, 100, 'gameboy', 0);
sprite3.name = 'gameboy2';
sprite3.body.collideWorldBounds = true;
sprite3.body.bounce.setTo(1, 1);
game.input.onDown.add(launch6, this);
}
function launch6() {
sprite.body.velocity.x = 100;
sprite2.body.velocity.x = -100;
sprite3.body.velocity.y = 100;
}
@ -22,10 +126,10 @@ function test5() {
// Offset Down Collision false
sprite = game.add.sprite(438, 400, 'gameboy', 0);
sprite = game.add.sprite(360, 400, 'gameboy', 0);
sprite.name = 'red';
sprite.body.collideWorldBounds = true;
sprite.body.bounce.setTo(0.9, 0.9);
// sprite.body.bounce.setTo(0.9, 0.9);
sprite.body.checkCollision.up = false;
sprite2 = game.add.sprite(400, 200, 'gameboy', 2);
@ -33,7 +137,7 @@ function test5() {
sprite2.body.collideWorldBounds = true;
// sprite2.body.checkCollision.down = false;
// sprite2.body.mass = 1;
// sprite2.body.bounce.setTo(1, 1);
sprite2.body.bounce.setTo(1, 1);
// sprite2.body.friction = 0;
game.input.onDown.add(launch5, this);
@ -180,7 +284,19 @@ function launch1() {
function update() {
game.physics.collide(sprite, sprite2);
if (reverse)
{
game.physics.collide(sprite2, sprite);
}
else
{
game.physics.collide(sprite, sprite2);
}
if (sprite3)
{
game.physics.collide(sprite, sprite3);
}
}
@ -201,4 +317,9 @@ function render() {
game.debug.renderPhysicsBody(sprite2.body);
}
if (sprite3)
{
game.debug.renderPhysicsBody(sprite3.body);
}
}

View file

@ -49,8 +49,7 @@ Phaser.Group = function (game, parent, name, useStage) {
{
if (parent instanceof Phaser.Group)
{
parent._container.addChild(this._container);
parent._container.updateTransform();
parent.add(this);
}
else
{

View file

@ -24,6 +24,7 @@
Phaser.Events = function (sprite) {
this.parent = sprite;
this.onAddedToGroup = new Phaser.Signal();
this.onRemovedFromGroup = new Phaser.Signal();
this.onKilled = new Phaser.Signal();
@ -41,6 +42,9 @@ Phaser.Events = function (sprite) {
this.onAnimationComplete = null;
this.onAnimationLoop = null;
this.onBeginContact = null;
this.onEndContact = null;
};
Phaser.Events.prototype = {

View file

@ -934,6 +934,11 @@ Phaser.Sprite.prototype.destroy = function() {
this.animations.destroy();
}
if (this.body)
{
this.body.destroy();
}
this.alive = false;
this.exists = false;
this.visible = false;

View file

@ -397,12 +397,6 @@ Phaser.Physics.Arcade.prototype = {
// pos = pos + dt*(vel + temp/2)
// vel = vel + temp
if (body.sprite.debug)
{
console.log('updateMotion: acx', body.acceleration.x, 'acy', body.acceleration.y, 'gravx', this._gravityX, 'gravy', this._gravityY, 'elapsed', this.game.time.physicsElapsed);
// console.log('updateMotion: rotation', body.rotation, 'vd', this._velocityDelta, 'drag', this._drag, 'acceleration', body.angularAcceleration);
}
this._p.setTo((body.acceleration.x + this._gravityX) * this.game.time.physicsElapsed, (body.acceleration.y + this._gravityY) * this.game.time.physicsElapsed);
return this._p;
@ -413,10 +407,11 @@ Phaser.Physics.Arcade.prototype = {
* Checks for overlaps between two game objects. The objects can be Sprites, Groups or Emitters.
* You can perform Sprite vs. Sprite, Sprite vs. Group and Group vs. Group overlap checks.
* Unlike collide the objects are NOT automatically separated or have any physics applied, they merely test for overlap results.
* The second parameter can be an array of objects, of differing types.
*
* @method Phaser.Physics.Arcade#overlap
* @param {Phaser.Sprite|Phaser.Group|Phaser.Particles.Emitter} object1 - The first object to check. Can be an instance of Phaser.Sprite, Phaser.Group or Phaser.Particles.Emitter.
* @param {Phaser.Sprite|Phaser.Group|Phaser.Particles.Emitter} object2 - The second object to check. Can be an instance of Phaser.Sprite, Phaser.Group or Phaser.Particles.Emitter.
* @param {Phaser.Sprite|Phaser.Group|Phaser.Particles.Emitter|array} object2 - The second object or array of objects to check. Can be Phaser.Sprite, Phaser.Group or Phaser.Particles.Emitter.
* @param {function} [overlapCallback=null] - An optional callback function that is called if the objects overlap. The two objects will be passed to this function in the same order in which you specified them.
* @param {function} [processCallback=null] - A callback function that lets you perform additional checks against the two objects if they overlap. If this is set then overlapCallback will only be called if processCallback returns true.
* @param {object} [callbackContext] - The context in which to run the callbacks.
@ -431,39 +426,16 @@ Phaser.Physics.Arcade.prototype = {
this._result = false;
this._total = 0;
this.collideHandler(object1, object2, overlapCallback, processCallback, callbackContext, true);
return (this._total > 0);
},
/**
* Checks for overlaps between a game object and a array of game objects. You can perform Sprite vs. Sprite, Sprite vs. Group, Group vs. Group, Sprite vs. Tilemap Layer or Group vs. Tilemap Layer checks.
* Unlike collide the objects are NOT automatically separated or have any physics applied, they merely test for overlap results.
* An optional processCallback can be provided. If given this function will be called when two sprites are found to be overlapping,
* giving you the chance to perform additional checks. If the function returns true then the overlap takes place. If it returns false it is skipped.
* The collideCallback is an optional function that is only called if two sprites collide. If a processCallback has been set then it needs to return true for overlapCallback to be called.
*
* @method Phaser.Physics.Arcade#overlapArray
* @param {Phaser.Sprite|Phaser.Group|Phaser.Particles.Emitter|Phaser.Tilemap} object1 - The first object to check. Can be an instance of Phaser.Sprite, Phaser.Group, Phaser.Particles.Emitter, or Phaser.Tilemap.
* @param {<Phaser.Sprite>array|<Phaser.Group>array|<Phaser.Particles.Emitter>array|<Phaser.Tilemap>array} objectArray - An array of objects to check. Can contain instances of Phaser.Sprite, Phaser.Group, Phaser.Particles.Emitter or Phaser.Tilemap.
* @param {function} [overlapCallback=null] - An optional callback function that is called if the objects overlap. The two objects will be passed to this function in the same order in which you specified them.
* @param {function} [processCallback=null] - A callback function that lets you perform additional checks against the two objects if they overlap. If this is set then collision will only happen if processCallback returns true. The two objects will be passed to this function in the same order in which you specified them.
* @param {object} [callbackContext] - The context in which to run the callbacks.
* @returns {boolean} True if a collision occured otherwise false.
*/
overlapArray: function (object1, objectArray, overlapCallback, processCallback, callbackContext) {
overlapCallback = overlapCallback || null;
processCallback = processCallback || null;
callbackContext = callbackContext || overlapCallback;
this._result = false;
this._total = 0;
for (var i = 0, len = objectArray.length; i < len; i++)
if (Array.isArray(object2))
{
this.collideHandler(object1, objectArray[i], overlapCallback, processCallback, callbackContext, true);
for (var i = 0, len = object2.length; i < len; i++)
{
this.collideHandler(object1, object2[i], overlapCallback, processCallback, callbackContext, true);
}
}
else
{
this.collideHandler(object1, object2, overlapCallback, processCallback, callbackContext, true);
}
return (this._total > 0);
@ -472,7 +444,7 @@ Phaser.Physics.Arcade.prototype = {
/**
* Checks for collision between two game objects. You can perform Sprite vs. Sprite, Sprite vs. Group, Group vs. Group, Sprite vs. Tilemap Layer or Group vs. Tilemap Layer collisions.
* If you'd like to collide an array of objects see Phaser.Physics.Arcade#collideArray.
* The second parameter can be an array of objects, of differing types.
* The objects are also automatically separated. If you don't require separation then use ArcadePhysics.overlap instead.
* An optional processCallback can be provided. If given this function will be called when two sprites are found to be colliding. It is called before any separation takes place,
* giving you the chance to perform additional checks. If the function returns true then the collision and separation is carried out. If it returns false it is skipped.
@ -480,7 +452,7 @@ Phaser.Physics.Arcade.prototype = {
*
* @method Phaser.Physics.Arcade#collide
* @param {Phaser.Sprite|Phaser.Group|Phaser.Particles.Emitter|Phaser.Tilemap} object1 - The first object to check. Can be an instance of Phaser.Sprite, Phaser.Group, Phaser.Particles.Emitter, or Phaser.Tilemap.
* @param {Phaser.Sprite|Phaser.Group|Phaser.Particles.Emitter|Phaser.Tilemap} object2 - The second object to check. Can be an instance of Phaser.Sprite, Phaser.Group, Phaser.Particles.Emitter or Phaser.Tilemap.
* @param {Phaser.Sprite|Phaser.Group|Phaser.Particles.Emitter|Phaser.Tilemap|array} object2 - The second object or array of objects to check. Can be Phaser.Sprite, Phaser.Group, Phaser.Particles.Emitter or Phaser.Tilemap.
* @param {function} [collideCallback=null] - An optional callback function that is called if the objects collide. The two objects will be passed to this function in the same order in which you specified them.
* @param {function} [processCallback=null] - A callback function that lets you perform additional checks against the two objects if they overlap. If this is set then collision will only happen if processCallback returns true. The two objects will be passed to this function in the same order in which you specified them.
* @param {object} [callbackContext] - The context in which to run the callbacks.
@ -495,39 +467,16 @@ Phaser.Physics.Arcade.prototype = {
this._result = false;
this._total = 0;
this.collideHandler(object1, object2, collideCallback, processCallback, callbackContext, false);
return (this._total > 0);
},
/**
* Checks for collision between a game object and an array of game objects. You can perform Sprite vs. Sprite, Sprite vs. Group, Group vs. Group, Sprite vs. Tilemap Layer or Group vs. Tilemap Layer collisions.
* The objects are also automatically separated. If you don't require separation then use ArcadePhysics.overlap instead.
* An optional processCallback can be provided. If given this function will be called when two sprites are found to be colliding. It is called before any separation takes place,
* giving you the chance to perform additional checks. If the function returns true then the collision and separation is carried out. If it returns false it is skipped.
* The collideCallback is an optional function that is only called if two sprites collide. If a processCallback has been set then it needs to return true for collideCallback to be called.
*
* @method Phaser.Physics.Arcade#collideArray
* @param {Phaser.Sprite|Phaser.Group|Phaser.Particles.Emitter|Phaser.Tilemap} object1 - The first object to check. Can be an instance of Phaser.Sprite, Phaser.Group, Phaser.Particles.Emitter, or Phaser.Tilemap.
* @param {<Phaser.Sprite>array|<Phaser.Group>array|<Phaser.Particles.Emitter>array|<Phaser.Tilemap>array} objectArray - An array of objects to check. Can contain instances of Phaser.Sprite, Phaser.Group, Phaser.Particles.Emitter or Phaser.Tilemap.
* @param {function} [collideCallback=null] - An optional callback function that is called if the objects collide. The two objects will be passed to this function in the same order in which you specified them.
* @param {function} [processCallback=null] - A callback function that lets you perform additional checks against the two objects if they overlap. If this is set then collision will only happen if processCallback returns true. The two objects will be passed to this function in the same order in which you specified them.
* @param {object} [callbackContext] - The context in which to run the callbacks.
* @returns {boolean} True if a collision occured otherwise false.
*/
collideArray: function (object1, objectArray, collideCallback, processCallback, callbackContext) {
collideCallback = collideCallback || null;
processCallback = processCallback || null;
callbackContext = callbackContext || collideCallback;
this._result = false;
this._total = 0;
for (var i = 0, len = objectArray.length; i < len; i++)
if (Array.isArray(object2))
{
this.collideHandler(object1, objectArray[i], collideCallback, processCallback, callbackContext, false);
for (var i = 0, len = object2.length; i < len; i++)
{
this.collideHandler(object1, object2[i], collideCallback, processCallback, callbackContext, false);
}
}
else
{
this.collideHandler(object1, object2, collideCallback, processCallback, callbackContext, false);
}
return (this._total > 0);
@ -745,8 +694,6 @@ Phaser.Physics.Arcade.prototype = {
*/
collideSpriteVsTilemapLayer: function (sprite, tilemapLayer, collideCallback, processCallback, callbackContext) {
// console.log('collideSpriteVsTilemapLayer x:', sprite.body.x, 'y:', sprite.body.y, 'body left:', sprite.body.left, 'right:', sprite.body.right);
this._mapData = tilemapLayer.getTiles(sprite.body.left, sprite.body.top, sprite.body.width, sprite.body.height, true);
if (this._mapData.length === 0)
@ -871,12 +818,19 @@ Phaser.Physics.Arcade.prototype = {
*/
intersects: function (a, b) {
var result = false;
if (a.width <= 0 || a.height <= 0 || b.width <= 0 || b.height <= 0)
{
return false;
result = false;
}
return !(a.right < b.left || a.bottom < b.top || a.left > b.right || a.top > b.bottom);
result = !(a.right < b.left || a.bottom < b.top || a.left > b.right || a.top > b.bottom);
if (!result && a.inContact(b))
{
a.removeContact(b);
}
},

View file

@ -271,6 +271,24 @@ Phaser.Physics.Arcade.Body = function (sprite) {
*/
this.height = 0;
/**
* @property {array<Phaser.Physics.Arcade.Body>} contacts - Used to store references to bodies this Body is in contact with.
* @protected
*/
this.contacts = [];
/**
* @property {number} overlapX - Mostly used internally to store the overlap values from Tile seperation.
* @protected
*/
this.overlapX = 0;
/**
* @property {number} overlapY - Mostly used internally to store the overlap values from Tile seperation.
* @protected
*/
this.overlapY = 0;
/**
* @property {Phaser.Point} _temp - Internal cache var.
* @private
@ -307,129 +325,34 @@ Phaser.Physics.Arcade.Body = function (sprite) {
*/
this._distances = [0, 0, 0, 0];
this.overlapX = 0;
this.overlapY = 0;
// Velocity cache
/**
* @property {number} _vx - Internal cache var.
* @private
*/
this._vx = 0;
/**
* @property {number} _vy - Internal cache var.
* @private
*/
this._vy = 0;
// Set-up the default shape
this.setRectangle(sprite.width, sprite.height, 0, 0);
// Set-up contact events
this.sprite.events.onBeginContact = new Phaser.Signal();
this.sprite.events.onEndContact = new Phaser.Signal();
};
Phaser.Physics.Arcade.Body.prototype = {
/**
* Sets this Body to use a circle of the given radius for all collision.
* The Circle will be centered on the center of the Sprite by default, but can be adjusted via the Body.offset property and the setCircle x/y parameters.
*
* @method Phaser.Physics.Arcade#setCircle
* @param {number} radius - The radius of this circle (in pixels)
* @param {number} [offsetX=0] - The x amount the circle will be offset from the Sprites center.
* @param {number} [offsetY=0] - The y amount the circle will be offset from the Sprites center.
*/
setCircle: function (radius, offsetX, offsetY) {
if (typeof offsetX === 'undefined') { offsetX = this.sprite._cache.halfWidth; }
if (typeof offsetY === 'undefined') { offsetY = this.sprite._cache.halfHeight; }
this.type = Phaser.Physics.Arcade.CIRCLE;
this.shape = new SAT.Circle(new SAT.Vector(this.sprite.x, this.sprite.y), radius);
this.polygon = null;
this.offset.setTo(offsetX, offsetY);
},
/**
* Sets this Body to use a rectangle for all collision.
* If you don't specify any parameters it will be sized to match the parent Sprites current width and height (including scale factor) and centered on the sprite.
*
* @method Phaser.Physics.Arcade#setRectangle
* @param {number} [width] - The width of the rectangle. If not specified it will default to the width of the parent Sprite.
* @param {number} [height] - The height of the rectangle. If not specified it will default to the height of the parent Sprite.
* @param {number} [translateX] - The x amount the rectangle will be translated from the Sprites center.
* @param {number} [translateY] - The y amount the rectangle will be translated from the Sprites center.
*/
setRectangle: function (width, height, translateX, translateY) {
if (typeof width === 'undefined') { width = this.sprite.width; }
if (typeof height === 'undefined') { height = this.sprite.height; }
if (typeof translateX === 'undefined') { translateX = -this.sprite._cache.halfWidth; }
if (typeof translateY === 'undefined') { translateY = -this.sprite._cache.halfHeight; }
this.type = Phaser.Physics.Arcade.RECT;
this.shape = new SAT.Box(new SAT.Vector(this.sprite.world.x, this.sprite.world.y), width, height);
this.polygon = this.shape.toPolygon();
this.polygon.translate(translateX, translateY);
this.offset.setTo(0, 0);
},
/**
* Sets this Body to use a convex polygon for collision.
* The points are specified in a counter-clockwise direction and must create a convex polygon.
* Use Body.translate and/or Body.offset to re-position the polygon from the Sprite origin.
*
* @method Phaser.Physics.Arcade#setPolygon
* @param {array<SAT.Vector>|Array<Number>|SAT.Vector...|Number...} points - This can be an array of Vectors that form the polygon,
* a flat array of numbers that will be interpreted as [x,y, x,y, ...], or the arguments passed can be
* all the points of the polygon e.g. `setPolygon(new SAT.Vector(), new SAT.Vector(), ...)`, or the
* arguments passed can be flat x,y values e.g. `setPolygon(x,y, x,y, x,y, ...)` where `x` and `y` are Numbers.
*/
setPolygon: function (points) {
this.type = Phaser.Physics.Arcade.POLYGON;
this.shape = null;
if (!(points instanceof Array))
{
points = Array.prototype.slice.call(arguments);
}
if (typeof points[0] === 'number')
{
var p = [];
for (var i = 0, len = points.length; i < len; i += 2)
{
p.push(new SAT.Vector(points[i], points[i + 1]));
}
points = p;
}
this.polygon = new SAT.Polygon(new SAT.Vector(this.sprite.center.x, this.sprite.center.y), points);
this.offset.setTo(0, 0);
},
/**
* Used for translating rectangle and polygon bodies from the Sprite parent. Doesn't apply to Circles.
* See also the Body.offset property.
*
* @method Phaser.Physics.Arcade#translate
* @param {number} x - The x amount the polygon or rectangle will be translated by from the Sprite.
* @param {number} y - The y amount the polygon or rectangle will be translated by from the Sprite.
*/
translate: function (x, y) {
if (this.polygon)
{
this.polygon.translate(x, y);
}
},
/**
* Internal method.
* Internal method that updates the Body scale in relation to the parent Sprite.
*
* @method Phaser.Physics.Arcade#updateScale
* @private
* @protected
*/
updateScale: function () {
@ -448,7 +371,7 @@ Phaser.Physics.Arcade.Body.prototype = {
},
/**
* Internal method.
* Internal method that updates the Body position in relation to the parent Sprite.
*
* @method Phaser.Physics.Arcade#preUpdate
* @protected
@ -530,7 +453,7 @@ if (this.sprite.debug)
},
/**
* Internal method that checks and potentially resets the blocke status.
* Internal method that checks and potentially resets the blocked status flags.
*
* @method Phaser.Physics.Arcade#checkBlocked
* @protected
@ -753,7 +676,7 @@ if (this.sprite.debug)
},
/**
* Adds the given Vector from this Body.
* Adds the given Vector to this Body.
*
* @method Phaser.Physics.Arcade#add
* @protected
@ -905,40 +828,110 @@ if (this.sprite.debug)
*/
overlap: function (body, response) {
var result = false;
if ((this.type === Phaser.Physics.Arcade.RECT || this.type === Phaser.Physics.Arcade.POLYGON) && (body.type === Phaser.Physics.Arcade.RECT || body.type === Phaser.Physics.Arcade.POLYGON))
{
return SAT.testPolygonPolygon(this.polygon, body.polygon, response);
result = SAT.testPolygonPolygon(this.polygon, body.polygon, response);
}
else if (this.type === Phaser.Physics.Arcade.CIRCLE && body.type === Phaser.Physics.Arcade.CIRCLE)
{
return SAT.testCircleCircle(this.shape, body.shape, response);
result = SAT.testCircleCircle(this.shape, body.shape, response);
}
else if ((this.type === Phaser.Physics.Arcade.RECT || this.type === Phaser.Physics.Arcade.POLYGON) && body.type === Phaser.Physics.Arcade.CIRCLE)
{
return SAT.testPolygonCircle(this.polygon, body.shape, response);
result = SAT.testPolygonCircle(this.polygon, body.shape, response);
}
else if (this.type === Phaser.Physics.Arcade.CIRCLE && (body.type === Phaser.Physics.Arcade.RECT || body.type === Phaser.Physics.Arcade.POLYGON))
{
return SAT.testCirclePolygon(this.shape, body.polygon, response);
result = SAT.testCirclePolygon(this.shape, body.polygon, response);
}
if (!result)
{
this.removeContact(body);
}
return result;
},
/**
* Checks if this Body is already in contact with the given Body.
*
* @method Phaser.Physics.Arcade#inContact
* @param {Phaser.Physics.Arcade.Body} body - The Body to be checked.
* @return {boolean} True if the given Body is already in contact with this Body.
*/
inContact: function (body) {
return (this.contacts.indexOf(body) != -1);
},
/**
* Adds the given Body to the contact list of this Body. Also adds this Body to the contact list of the given Body.
*
* @method Phaser.Physics.Arcade#addContact
* @param {Phaser.Physics.Arcade.Body} body - The Body to be added.
* @return {boolean} True if the given Body was added to this contact list, false if already on it.
*/
addContact: function (body) {
if (this.inContact(body))
{
return false;
}
this.contacts.push(body);
this.sprite.events.onBeginContact.dispatch(this.sprite, body.sprite, this, body);
body.addContact(this);
return true;
},
/**
* Removes the given Body from the contact list of this Body. Also removes this Body from the contact list of the given Body.
*
* @method Phaser.Physics.Arcade#removeContact
* @param {Phaser.Physics.Arcade.Body} body - The Body to be removed.
* @return {boolean} True if the given Body was removed from this contact list, false if wasn't on it.
*/
removeContact: function (body) {
if (!this.inContact(body))
{
return false;
}
this.contacts.splice(this.contacts.indexOf(body), 1);
this.sprite.events.onEndContact.dispatch(this.sprite, body.sprite, this, body);
body.removeContact(this);
return true;
},
/**
* This separates this Body from the given Body unless a customSeparateCallback is set.
* It assumes they have already been overlap checked and the resulting overlap is stored in overlapX and overlapY.
* It assumes they have already been overlap checked and the resulting overlap is stored in the SAT response.
*
* @method Phaser.Physics.Arcade#separate
* @protected
* @param {Phaser.Physics.Arcade.Body} body - The Body to be separated from this one.
* @param {SAT.Response} response - SAT Response handler.
* @return {boolean}
* @return {boolean} True if the bodies were separated, false if not (for example checkCollide allows them to pass through)
*/
separate: function (body, response) {
if (this.customSeparateCallback)
if (this.inContact(body))
{
return this.customSeparateCallback.call(this.customSeparateContext, this, response);
return false;
}
this._distances[0] = body.right - this.x; // Distance of B to face on left side of A
@ -946,42 +939,65 @@ if (this.sprite.debug)
this._distances[2] = body.bottom - this.y; // Distance of B to face on bottom side of A
this._distances[3] = this.bottom - body.y; // Distance of B to face on top side of A
console.log(this.sprite.name, 'collided with', body.sprite.name, response.overlapN, this._distances);
// If we've zero distance then check for side-slicing
if (response.overlapN.x && (this._distances[0] === 0 || this._distances[1] === 0))
{
response.overlapN.x = false;
response.overlapN.y = true;
}
else if (response.overlapN.y && (this._distances[2] === 0 || this._distances[3] === 0))
{
response.overlapN.x = true;
response.overlapN.y = false;
}
if (this.customSeparateCallback)
{
return this.customSeparateCallback.call(this.customSeparateContext, this, response, this._distances);
}
var hasSeparated = false;
if (response.overlapN.x)
{
// Which is smaller? Left or Right?
if (this._distances[0] < this._distances[1])
if (this._distances[0] < this._distances[1] && (body.checkCollision.right || this.checkCollision.left))
{
console.log(this.sprite.name, 'collided on the LEFT with', body.sprite.name, response);
this.hitLeft(body, response);
// console.log(this.sprite.name, 'collided on the LEFT with', body.sprite.name, response);
hasSeparated = this.hitLeft(body, response);
}
else if (this._distances[1] < this._distances[0])
else if (this._distances[1] < this._distances[0] && (body.checkCollision.left || this.checkCollision.right))
{
console.log(this.sprite.name, 'collided on the RIGHT with', body.sprite.name, response);
this.hitRight(body, response);
// console.log(this.sprite.name, 'collided on the RIGHT with', body.sprite.name, response);
hasSeparated = this.hitRight(body, response);
}
}
else if (response.overlapN.y)
{
// Which is smaller? Top or Bottom?
if (this._distances[2] < this._distances[3])
if (this._distances[2] < this._distances[3] && (body.checkCollision.down || this.checkCollision.up))
{
console.log(this.sprite.name, 'collided on the TOP with', body.sprite.name, response);
this.hitTop(body, response);
// console.log(this.sprite.name, 'collided on the TOP with', body.sprite.name, response);
hasSeparated = this.hitTop(body, response);
}
else if (this._distances[3] < this._distances[2])
else if (this._distances[3] < this._distances[2] && (body.checkCollision.up || this.checkCollision.down))
{
console.log(this.sprite.name, 'collided on the BOTTOM with', body.sprite.name, response);
this.hitBottom(body, response);
// console.log(this.sprite.name, 'collided on the BOTTOM with', body.sprite.name, response);
hasSeparated = this.hitBottom(body, response);
}
}
this.game.physics.checkBounds(this);
this.game.physics.checkBounds(body);
if (hasSeparated)
{
this.game.physics.checkBounds(this);
this.game.physics.checkBounds(body);
}
else
{
this.addContact(body);
}
return true;
return hasSeparated;
},
@ -999,17 +1015,6 @@ if (this.sprite.debug)
*/
hitLeft: function (body, response) {
// We know that Body is overlapping with This on the left hand side (deltaX < 0 = moving left, > 0 = moving right)
if (body.speed > 0 && (body.deltaX() <= 0 || (body.deltaX() > 0 && !this.checkCollision.left)))
{
return;
}
if (this.speed > 0 && (this.deltaX() >= 0 || (this.deltaX() < 0 && !body.checkCollision.right)))
{
return;
}
if (this.collideCallback && !this.collideCallback.call(this.collideCallbackContext, Phaser.LEFT, this, body, response))
{
return;
@ -1052,17 +1057,6 @@ if (this.sprite.debug)
*/
hitRight: function (body, response) {
// We know that Body is overlapping with This on the right hand side (deltaX < 0 = moving left, > 0 = moving right)
if (body.speed > 0 && (body.deltaX() >= 0 || (body.deltaX() < 0 && !this.checkCollision.right)))
{
return;
}
if (this.speed > 0 && (this.deltaX() <= 0 || (this.deltaX() > 0 && !body.checkCollision.left)))
{
return;
}
if (this.collideCallback && !this.collideCallback.call(this.collideCallbackContext, Phaser.RIGHT, this, body))
{
return;
@ -1105,20 +1099,9 @@ if (this.sprite.debug)
*/
hitTop: function (body, response) {
// We know that Body is overlapping with This on the bottom side (deltaY < 0 = moving up, > 0 = moving down)
if (body.speed > 0 && (body.deltaY() <= 0 || (body.deltaY() > 0 && !this.checkCollision.up)))
{
return;
}
if (this.speed > 0 && (this.deltaY() >= 0 || (this.deltaY() < 0 && !body.checkCollision.down)))
{
return;
}
if (this.collideCallback && !this.collideCallback.call(this.collideCallbackContext, Phaser.UP, this, body))
{
return;
return false;
}
if (!this.moves || this.immovable || this.blocked.down || this.touching.down)
@ -1142,6 +1125,8 @@ if (this.sprite.debug)
this.touching.up = true;
body.touching.down = true;
return true;
},
/**
@ -1158,20 +1143,9 @@ if (this.sprite.debug)
*/
hitBottom: function (body, response) {
// We know that Body is overlapping with This on the bottom side (deltaY < 0 = moving up, > 0 = moving down)
if (body.speed > 0 && (body.deltaY() >= 0 || (body.deltaY() < 0 && !this.checkCollision.down)))
{
return;
}
if (this.speed > 0 && (this.deltaY() <= 0 || (this.deltaY() > 0 && !body.checkCollision.up)))
{
return;
}
if (this.collideCallback && !this.collideCallback.call(this.collideCallbackContext, Phaser.DOWN, this, body))
{
return;
return false;
}
if (!this.moves || this.immovable || this.blocked.up || this.touching.up)
@ -1195,6 +1169,8 @@ if (this.sprite.debug)
this.touching.down = true;
body.touching.up = true;
return true;
},
/**
@ -1361,6 +1337,131 @@ if (this.sprite.debug)
this.preY = this.y;
this.updateBounds();
this.contacts.length = 0;
},
/**
* Destroys this Body and all references it holds to other objects.
*
* @method Phaser.Physics.Arcade#destroy
*/
destroy: function () {
this.sprite = null;
this.collideCallback = null;
this.collideCallbackContext = null;
this.customSeparateCallback = null;
this.customSeparateContext = null;
this.contacts.length = 0;
},
/**
* Sets this Body to use a circle of the given radius for all collision.
* The Circle will be centered on the center of the Sprite by default, but can be adjusted via the Body.offset property and the setCircle x/y parameters.
*
* @method Phaser.Physics.Arcade#setCircle
* @param {number} radius - The radius of this circle (in pixels)
* @param {number} [offsetX=0] - The x amount the circle will be offset from the Sprites center.
* @param {number} [offsetY=0] - The y amount the circle will be offset from the Sprites center.
*/
setCircle: function (radius, offsetX, offsetY) {
if (typeof offsetX === 'undefined') { offsetX = this.sprite._cache.halfWidth; }
if (typeof offsetY === 'undefined') { offsetY = this.sprite._cache.halfHeight; }
this.type = Phaser.Physics.Arcade.CIRCLE;
this.shape = new SAT.Circle(new SAT.Vector(this.sprite.x, this.sprite.y), radius);
this.polygon = null;
this.offset.setTo(offsetX, offsetY);
},
/**
* Sets this Body to use a rectangle for all collision.
* If you don't specify any parameters it will be sized to match the parent Sprites current width and height (including scale factor) and centered on the sprite.
*
* @method Phaser.Physics.Arcade#setRectangle
* @param {number} [width] - The width of the rectangle. If not specified it will default to the width of the parent Sprite.
* @param {number} [height] - The height of the rectangle. If not specified it will default to the height of the parent Sprite.
* @param {number} [translateX] - The x amount the rectangle will be translated from the Sprites center.
* @param {number} [translateY] - The y amount the rectangle will be translated from the Sprites center.
*/
setRectangle: function (width, height, translateX, translateY) {
if (typeof width === 'undefined') { width = this.sprite.width; }
if (typeof height === 'undefined') { height = this.sprite.height; }
if (typeof translateX === 'undefined') { translateX = -this.sprite._cache.halfWidth; }
if (typeof translateY === 'undefined') { translateY = -this.sprite._cache.halfHeight; }
this.type = Phaser.Physics.Arcade.RECT;
this.shape = new SAT.Box(new SAT.Vector(this.sprite.world.x, this.sprite.world.y), width, height);
this.polygon = this.shape.toPolygon();
this.polygon.translate(translateX, translateY);
this.offset.setTo(0, 0);
},
/**
* Sets this Body to use a convex polygon for collision.
* The points are specified in a counter-clockwise direction and must create a convex polygon.
* Use Body.translate and/or Body.offset to re-position the polygon from the Sprite origin.
*
* @method Phaser.Physics.Arcade#setPolygon
* @param {array<SAT.Vector>|Array<Number>|SAT.Vector...|Number...} points - This can be an array of Vectors that form the polygon,
* a flat array of numbers that will be interpreted as [x,y, x,y, ...], or the arguments passed can be
* all the points of the polygon e.g. `setPolygon(new SAT.Vector(), new SAT.Vector(), ...)`, or the
* arguments passed can be flat x,y values e.g. `setPolygon(x,y, x,y, x,y, ...)` where `x` and `y` are Numbers.
*/
setPolygon: function (points) {
this.type = Phaser.Physics.Arcade.POLYGON;
this.shape = null;
if (!Array.isArray(points))
{
points = Array.prototype.slice.call(arguments);
}
if (typeof points[0] === 'number')
{
var p = [];
for (var i = 0, len = points.length; i < len; i += 2)
{
p.push(new SAT.Vector(points[i], points[i + 1]));
}
points = p;
}
this.polygon = new SAT.Polygon(new SAT.Vector(this.sprite.center.x, this.sprite.center.y), points);
this.offset.setTo(0, 0);
},
/**
* Used for translating rectangle and polygon bodies from the Sprite parent. Doesn't apply to Circles.
* See also the Body.offset property.
*
* @method Phaser.Physics.Arcade#translate
* @param {number} x - The x amount the polygon or rectangle will be translated by from the Sprite.
* @param {number} y - The y amount the polygon or rectangle will be translated by from the Sprite.
*/
translate: function (x, y) {
if (this.polygon)
{
this.polygon.translate(x, y);
}
},
/**

View file

@ -125,6 +125,13 @@ Phaser.Tween = function (object, game) {
*/
this._onUpdateCallback = null;
/**
* @property {object} _onUpdateCallbackContext - The context in which to call the onUpdate callback.
* @private
* @default null
*/
this._onUpdateCallbackContext = null;
/**
* @property {number} _pausedTime - Private pause timer.
* @private
@ -293,6 +300,8 @@ Phaser.Tween.prototype = {
this.isRunning = false;
this._onUpdateCallback = null;
this._manager.remove(this);
return this;
@ -411,9 +420,11 @@ Phaser.Tween.prototype = {
* @param {function} callback - The callback to invoke each time this tween is updated.
* @return {Phaser.Tween} Itself.
*/
onUpdateCallback: function (callback) {
onUpdateCallback: function (callback, callbackContext) {
this._onUpdateCallback = callback;
this._onUpdateCallbackContext = callbackContext;
return this;
},
@ -506,7 +517,7 @@ Phaser.Tween.prototype = {
if (this._onUpdateCallback !== null)
{
this._onUpdateCallback.call(this._object, value);
this._onUpdateCallback.call(this._onUpdateCallbackContext, this, value);
}
if (elapsed == 1)

View file

@ -28,13 +28,13 @@ Phaser.TweenManager = function (game) {
this.game = game;
/**
* @property {array} _tweens - Description.
* @property {array<Phaser.Tween>} _tweens - All of the currently running tweens.
* @private
*/
this._tweens = [];
/**
* @property {array} _add - Description.
* @property {array<Phaser.Tween>} _add - All of the tweens queued to be added in the next update.
* @private
*/
this._add = [];
@ -46,13 +46,6 @@ Phaser.TweenManager = function (game) {
Phaser.TweenManager.prototype = {
/**
* Version number of this library.
* @property {string} REVISION
* @default
*/
REVISION: '11dev',
/**
* Get all the tween objects in an array.
* @method Phaser.TweenManager#getAll
@ -65,12 +58,17 @@ Phaser.TweenManager.prototype = {
},
/**
* Remove all tween objects.
* Remove all tweens running and in the queue. Doesn't call any of the tween onComplete events.
* @method Phaser.TweenManager#removeAll
*/
removeAll: function () {
this._tweens = [];
for (var i = 0; i < this._tweens.length; i++)
{
this._tweens[i].pendingDelete = true;
}
this._add = [];
},

View file

@ -265,3 +265,13 @@ if (typeof Function.prototype.bind != 'function') {
};
})();
}
/**
* A polyfill for Array.isArray
*/
if (!Array.isArray) {
Array.isArray = function (arg) {
return Object.prototype.toString.call(arg) == '[object Array]';
};
}