Skip to content

Commit

Permalink
P2 postBroadphase example and handler done. Group.enableBodyDebug add…
Browse files Browse the repository at this point in the history
…ed. Sprites no longer remove bodies from the world if exists = false, instead they set safeDestroy to true, which removes the body on the next preUpdate, to avoid mid-step destruction issues.
  • Loading branch information
photonstorm committed Mar 13, 2014
1 parent 580ee6b commit 94448d2
Show file tree
Hide file tree
Showing 9 changed files with 234 additions and 46 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,8 @@ Updates:
* TileSprites now have a physics body property and call it in the pre and post updates. As with all physics bodies it's null by default.
* json is now the default tilemap format when not defined (thanks RyanDansie, #528)
* The Particle Emitter now remembers the frames given to it and resets it when a new particle is emitted.
* Game.focusLoss and focusGain methods and onBlur and onFocus Signals added, allowing for more fine-grained control over game pause vs. focus loss.
* Keyboard.removeKey method added (thanks qdrj, #550)


Bug Fixes:
Expand Down
17 changes: 10 additions & 7 deletions examples/p2 physics/impact events.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ function preload() {

game.load.image('stars', 'assets/misc/starfield.jpg');
game.load.spritesheet('ship', 'assets/sprites/humstar.png', 32, 32);
game.load.image('ball', 'assets/sprites/shinyball.png');
game.load.image('panda', 'assets/sprites/spinObj_01.png');
game.load.image('sweet', 'assets/sprites/spinObj_06.png');

}

Expand All @@ -25,12 +26,16 @@ function create() {

balls = game.add.group();
balls.enableBody = true;
// balls.enableBodyDebug = true;
balls.physicsBodyType = Phaser.Physics.P2JS;

for (var i = 0; i < 50; i++)
for (var i = 0; i < 10; i++)
{
var ball = balls.create(game.world.randomX, game.world.randomY, 'ball');
ball.body.setCircle(16);
var panda = balls.create(game.world.randomX, game.world.randomY, 'panda');
panda.body.setCircle(26);

var sweet = balls.create(game.world.randomX, game.world.randomY, 'sweet');
sweet.body.setCircle(28);
}

ship = game.add.sprite(200, 200, 'ship');
Expand All @@ -45,16 +50,14 @@ function create() {

game.camera.follow(ship);

ship.body.onBeginContact.add(hitBall, this);
// ship.body.onBeginContact.add(hitBall, this);

cursors = game.input.keyboard.createCursorKeys();

}

function hitBall(body) {

// body.sprite.kill();

}

function update() {
Expand Down
117 changes: 117 additions & 0 deletions examples/p2 physics/postbroadphase callback.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@

var game = new Phaser.Game(800, 600, Phaser.AUTO, 'phaser-example', { preload: preload, create: create, update: update, render: render });

function preload() {

game.load.image('stars', 'assets/misc/starfield.jpg');
game.load.spritesheet('ship', 'assets/sprites/humstar.png', 32, 32);
game.load.spritesheet('veggies', 'assets/sprites/fruitnveg64wh37.png', 64, 64);

}

var ship;
var starfield;
var cursors;

function create() {

game.world.setBounds(0, 0, 1600, 1200);

game.physics.startSystem(Phaser.Physics.P2JS);
game.physics.p2.defaultRestitution = 0.9;

starfield = game.add.tileSprite(0, 0, 800, 600, 'stars');
starfield.fixedToCamera = true;

veggies = game.add.group();
veggies.enableBody = true;
veggies.physicsBodyType = Phaser.Physics.P2JS;

var vegFrames = [ 1, 3, 4, 8 ];

for (var i = 0; i < 25; i++)
{
var veg = veggies.create(game.world.randomX, game.world.randomY, 'veggies', game.rnd.pick(vegFrames));
veg.body.setCircle(26);
}

ship = game.add.sprite(200, 200, 'ship');
ship.name = 'ship';
ship.scale.set(2);
ship.smoothed = false;
ship.animations.add('fly', [0,1,2,3,4,5], 10, true);
ship.play('fly');

// Create our physics body - a 28px radius circle. Set the 'false' parameter below to 'true' to enable debugging
game.physics.p2.enable(ship, false);
ship.body.setCircle(28);

game.camera.follow(ship);

game.physics.p2.setPostBroadphaseCallback(checkVeg, this);

cursors = game.input.keyboard.createCursorKeys();

}

function checkVeg(body1, body2) {

// To explain - the post broadphase event has collected together all potential collision pairs in the world
// It doesn't mean they WILL collide, just that they might do.

// This callback is sent each collision pair of bodies. It's up to you how you compare them.
// If you return true then the pair will carry on into the narrow phase, potentially colliding.
// If you return false they will be removed from the narrow phase check all together.

// In this simple example if one of the bodies is our space ship,
// and the other body is the green pepper sprite (frame ID 4) then we DON'T allow the collision to happen.
// Usually you would use a collision mask for something this simple, but it demonstates use.

if ((body1.sprite.name === 'ship' && body2.sprite.frame === 4) || (body2.sprite.name === 'ship' && body1.sprite.frame === 4))
{
return false;
}

return true;

}

function update() {

ship.body.setZeroVelocity();

if (cursors.left.isDown)
{
ship.body.moveLeft(200);
}
else if (cursors.right.isDown)
{
ship.body.moveRight(200);
}

if (cursors.up.isDown)
{
ship.body.moveUp(200);
}
else if (cursors.down.isDown)
{
ship.body.moveDown(200);
}

if (!game.camera.atLimit.x)
{
starfield.tilePosition.x += (ship.body.velocity.x * 16) * game.time.physicsElapsed;
}

if (!game.camera.atLimit.y)
{
starfield.tilePosition.y += (ship.body.velocity.y * 16) * game.time.physicsElapsed;
}

}

function render() {

game.debug.text('World bodies: ' + game.physics.p2.total, 32, 32);

}
9 changes: 8 additions & 1 deletion src/core/Group.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,17 @@ Phaser.Group = function (game, parent, name, addToStage) {
this.cameraOffset = new Phaser.Point();

/**
* @default
* @property {boolean} enableBody - If true all Sprites created with `Group.create` or `Group.createMulitple` will have a physics body created on them. Change the body type with `Group.physicsBodyType`.
*/
this.enableBody = false;

/**
* @property {boolean} enableBodyDebug - If true when a physics body is created (via Group.enableBody) it will create a physics debug object as well. Only works for P2 bodies.
* @default
*/
this.enableBodyDebug = false;

/**
* @property {number} physicsBodyType - If Group.enableBody is true this is the type of physics body that is created on new Sprites. Phaser.Physics.ARCADE, Phaser.Physics.P2, Phaser.Physics.NINJA, etc.
*/
Expand Down Expand Up @@ -297,7 +304,7 @@ Phaser.Group.prototype.create = function (x, y, key, frame, exists) {
}
else if (this.physicsBodyType === Phaser.Physics.P2JS && this.game.physics.p2)
{
this.game.physics.p2.enable(child);
this.game.physics.p2.enable(child, this.enableBodyDebug);
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/gameobjects/Sprite.js
Original file line number Diff line number Diff line change
Expand Up @@ -900,7 +900,7 @@ Object.defineProperty(Phaser.Sprite.prototype, "exists", {

if (this.body && this.body.type === Phaser.Physics.P2)
{
this.body.removeFromWorld();
this.body.safeRemove = true;
}

this.visible = false;
Expand Down
2 changes: 1 addition & 1 deletion src/gameobjects/TileSprite.js
Original file line number Diff line number Diff line change
Expand Up @@ -528,7 +528,7 @@ Object.defineProperty(Phaser.TileSprite.prototype, "exists", {

if (this.body && this.body.type === Phaser.Physics.P2)
{
this.body.removeFromWorld();
this.body.safeRemove = true;
}

this.visible = false;
Expand Down
3 changes: 1 addition & 2 deletions src/input/Keyboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,7 @@ Phaser.Keyboard.prototype = {

if (this._keys[keycode])
{

delete (this._keys[keycode]);
this._keys[keycode] = null;

this.removeKeyCapture(keycode);
}
Expand Down
42 changes: 36 additions & 6 deletions src/physics/p2/Body.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,21 +78,27 @@ Phaser.Physics.P2.Body = function (game, sprite, x, y, mass) {
* A Body can be set to collide against the World bounds automatically if this is set to true. Otherwise it will leave the World.
* Note that this only applies if your World has bounds! The response to the collision should be managed via CollisionMaterials.
* @property {boolean} collideWorldBounds - Should the Body collide with the World bounds?
* @default
*/
this.collideWorldBounds = true;

/**
* @property {Phaser.Signal} onImpact - Dispatched when the shape/s of this Body impact with another. The event will be sent 2 parameters, this Body and the impact Body.
* Dispatched when the shape/s of this Body impact with another. The event will be sent 2 parameters, this Body and the impact Body.
* @property {Phaser.Signal} onImpact
*/
this.onImpact = new Phaser.Signal();

/**
* @property {Phaser.Signal} onBeginContact - Dispatched when a first contact is created between two bodies. This event is fired before the step has been done.
* Dispatched when a first contact is created between shapes in two bodies. This event is fired during the step, so collision has already taken place.
* The event will be sent 4 parameters: The body it is in contact with, the shape from this body that caused the contact, the shape from the contact body and the contact equation data array.
* @property {Phaser.Signal} onBeginContact
*/
this.onBeginContact = new Phaser.Signal();

/**
* @property {Phaser.Signal} onEndContact - Dispatched when final contact occurs between two bodies. This event is fired before the step has been done.
* Dispatched when contact ends between shapes in two bodies. This event is fired during the step, so collision has already taken place.
* The event will be sent 3 parameters: The body it is in contact with, the shape from this body that caused the contact and the shape from the contact body.
* @property {Phaser.Signal} onEndContact
*/
this.onEndContact = new Phaser.Signal();

Expand All @@ -102,6 +108,11 @@ Phaser.Physics.P2.Body = function (game, sprite, x, y, mass) {
*/
this.collidesWith = [];

/**
* @property {boolean} safeRemove - To avoid deleting this body during a physics step, and causing all kinds of problems, set safeRemove to true to have it removed in the next preUpdate.
*/
this.safeRemove = false;

/**
* @property {array} _bodyCallbacks - Array of Body callbacks.
* @private
Expand Down Expand Up @@ -146,23 +157,35 @@ Phaser.Physics.P2.Body.prototype = {
/**
* Sets a callback to be fired any time this Body impacts with the given Body. The impact test is performed against body.id values.
* The callback will be sent 4 parameters: This body, the body that impacted, the Shape in this body and the shape in the impacting body.
* Note that the impact event happens after collision resolution, so it cannot be used to prevent a collision from happening.
* It also happens mid-step. So do not destroy a Body during this callback, instead set safeDestroy to true so it will be killed on the next preUpdate.
*
* @method Phaser.Physics.P2.Body#createBodyCallback
* @param {Phaser.Physics.P2.Body} body - The Body to send impact events for.
* @param {Phaser.Physics.P2.Body|p2.Body} body - The Body to send impact events for.
* @param {function} callback - The callback to fire on impact. Set to null to clear a previously set callback.
* @param {object} callbackContext - The context under which the callback will fire.
*/
createBodyCallback: function (body, callback, callbackContext) {

this._bodyCallbacks[body.data.id] = callback;
this._bodyCallbackContext[body.data.id] = callbackContext;
if (body instanceof Phaser.Physics.P2.Body)
{
this._bodyCallbacks[body.data.id] = callback;
this._bodyCallbackContext[body.data.id] = callbackContext;
}
else if (body instanceof p2.Body)
{
this._bodyCallbacks[body.id] = callback;
this._bodyCallbackContext[body.id] = callbackContext;
}

},

/**
* Sets a callback to be fired any time this Body impacts with the given Group. The impact test is performed against shape.collisionGroup values.
* The callback will be sent 4 parameters: This body, the body that impacted, the Shape in this body and the shape in the impacting body.
* This callback will only fire if this Body has been assigned a collision group.
* Note that the impact event happens after collision resolution, so it cannot be used to prevent a collision from happening.
* It also happens mid-step. So do not destroy a Body during this callback, instead set safeDestroy to true so it will be killed on the next preUpdate.
*
* @method Phaser.Physics.P2.Body#createGroupCallback
* @param {Phaser.Physics.CollisionGroup} group - The Group to send impact events for.
Expand Down Expand Up @@ -588,6 +611,13 @@ Phaser.Physics.P2.Body.prototype = {
* @protected
*/
preUpdate: function () {

if (this.safeRemove)
{
this.removeFromWorld();
this.safeRemove = false;
}

},

/**
Expand Down
Loading

0 comments on commit 94448d2

Please sign in to comment.