P2 collision groups and callbacks working as expected, new example done.

This commit is contained in:
photonstorm 2014-03-13 09:14:12 +00:00
parent 94448d2497
commit 0584d3eadf
4 changed files with 267 additions and 64 deletions

View file

@ -0,0 +1,124 @@
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.image('panda', 'assets/sprites/spinObj_01.png');
game.load.image('sweet', 'assets/sprites/spinObj_06.png');
}
var ship;
var starfield;
var cursors;
function create() {
// Enable P2
game.physics.startSystem(Phaser.Physics.P2JS);
// Turn on impact events for the world, without this we get no collision callbacks
game.physics.p2.setImpactEvents(true);
// Create our collision groups. One for the player, one for the pandas
var playerCollisionGroup = game.physics.p2.createCollisionGroup();
var pandaCollisionGroup = game.physics.p2.createCollisionGroup();
// This part is vital if you want the objects with their own collision groups to still collide with the world bounds
// (which we do) - what this does is adjust the bounds to use its own collision group.
game.physics.p2.updateBoundsCollisionGroup();
starfield = game.add.tileSprite(0, 0, 800, 600, 'stars');
starfield.fixedToCamera = true;
var pandas = game.add.group();
pandas.enableBody = true;
pandas.physicsBodyType = Phaser.Physics.P2JS;
for (var i = 0; i < 4; i++)
{
var panda = pandas.create(game.world.randomX, game.world.randomY, 'panda');
panda.body.setRectangle(40, 40);
// Tell the panda to use the pandaCollisionGroup
panda.body.setCollisionGroup(pandaCollisionGroup);
// Pandas will collide against themselves and the player
// If you don't set this they'll not collide with anything.
// The first parameter is either an array or a single collision group.
panda.body.collides([pandaCollisionGroup, playerCollisionGroup]);
}
// Create our ship sprite
ship = game.add.sprite(200, 200, 'ship');
ship.scale.set(2);
ship.smoothed = false;
ship.animations.add('fly', [0,1,2,3,4,5], 10, true);
ship.play('fly');
game.physics.p2.enable(ship, false);
ship.body.setCircle(28);
// Set the ships collision group
ship.body.setCollisionGroup(playerCollisionGroup);
// The ship will collide with the pandas, and when it strikes one the hitPanda callback will fire, causing it to alpha out a bit
// When pandas collide with each other, nothing happens to them.
ship.body.collides(pandaCollisionGroup, hitPanda, this);
game.camera.follow(ship);
cursors = game.input.keyboard.createCursorKeys();
}
function hitPanda(body1, body2) {
// body1 is the space ship (as it's the body that owns the callback)
// body2 is the body it impacted with, in this case our panda
// As body2 is a Phaser.Physics.P2.Body object, you access its own (the sprite) via the sprite property:
body2.sprite.alpha -= 0.1;
}
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('Collide with the Pandas!', 32, 32);
}

View file

@ -16,27 +16,19 @@ 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;
balls = game.add.group();
balls.enableBody = true;
// balls.enableBodyDebug = true;
balls.physicsBodyType = Phaser.Physics.P2JS;
var panda = game.add.sprite(game.world.randomX, game.world.randomY, 'panda');
game.physics.p2.enable(panda, false);
panda.body.setRectangle(40, 40, 0, 0);
for (var i = 0; i < 10; i++)
{
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);
}
var sweet = game.add.sprite(game.world.randomX, game.world.randomY, 'sweet');
game.physics.p2.enable(sweet, false);
sweet.body.setRectangle(40, 40, 0, 0);
ship = game.add.sprite(200, 200, 'ship');
ship.scale.set(2);
@ -50,13 +42,22 @@ function create() {
game.camera.follow(ship);
// ship.body.onBeginContact.add(hitBall, this);
// Here we create a Body specific callback
ship.body.createBodyCallback(panda, hitPanda, this);
// And before this will happen, we need to turn on impact events for the world
game.physics.p2.setImpactEvents(true);
cursors = game.input.keyboard.createCursorKeys();
}
function hitBall(body) {
function hitPanda(body1, body2) {
// body1 is the space ship (as it's the body that owns the callback)
// body2 is the body it impacted with, in this case our panda
// As body2 is a Phaser.Physics.P2.Body object, you access its own (the sprite) via the sprite property:
body2.sprite.alpha -= 0.1;
}
@ -96,6 +97,6 @@ function update() {
function render() {
game.debug.text('World bodies: ' + game.physics.p2.total, 32, 32);
game.debug.text('Collide with the Panda!', 32, 32);
}

View file

@ -45,7 +45,7 @@ Phaser.Physics.P2.Body = function (game, sprite, x, y, mass) {
/**
* @property {number} type - The type of physics system this body belongs to.
*/
this.type = Phaser.Physics.P2;
this.type = Phaser.Physics.P2JS;
/**
* @property {Phaser.Point} offset - The offset of the Physics Body from the Sprite x/y position.
@ -114,28 +114,28 @@ Phaser.Physics.P2.Body = function (game, sprite, x, y, mass) {
this.safeRemove = false;
/**
* @property {array} _bodyCallbacks - Array of Body callbacks.
* @property {object} _bodyCallbacks - Array of Body callbacks.
* @private
*/
this._bodyCallbacks = [];
this._bodyCallbacks = {};
/**
* @property {array} _bodyCallbackContext - Array of Body callback contexts.
* @property {object} _bodyCallbackContext - Array of Body callback contexts.
* @private
*/
this._bodyCallbackContext = [];
this._bodyCallbackContext = {};
/**
* @property {array} _groupCallbacks - Array of Group callbacks.
* @property {object} _groupCallbacks - Array of Group callbacks.
* @private
*/
this._groupCallbacks = [];
this._groupCallbacks = {};
/**
* @property {array} _bodyCallbackContext - Array of Grouo callback contexts.
* @property {object} _bodyCallbackContext - Array of Grouo callback contexts.
* @private
*/
this._groupCallbackContext = [];
this._groupCallbackContext = {};
/**
* @property {Phaser.Physics.P2.BodyDebug} debugBody - Reference to the debug body.
@ -155,27 +155,41 @@ Phaser.Physics.P2.Body = function (game, sprite, x, y, mass) {
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.
* Sets a callback to be fired any time a shape in this Body impacts with a shape in 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|p2.Body} body - The Body to send impact events for.
* @param {Phaser.Sprite|Phaser.TileSprite|Phaser.Physics.P2.Body|p2.Body} object - The object 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) {
createBodyCallback: function (object, callback, callbackContext) {
if (body instanceof Phaser.Physics.P2.Body)
var id = -1;
if (object['id'])
{
this._bodyCallbacks[body.data.id] = callback;
this._bodyCallbackContext[body.data.id] = callbackContext;
id = object.id;
}
else if (body instanceof p2.Body)
else if (object['body'])
{
this._bodyCallbacks[body.id] = callback;
this._bodyCallbackContext[body.id] = callbackContext;
id = object.body.id;
}
if (id > -1)
{
if (callback === null)
{
delete (this._bodyCallbacks[id]);
delete (this._bodyCallbackContext[id]);
}
else
{
this._bodyCallbacks[id] = callback;
this._bodyCallbackContext[id] = callbackContext;
}
}
},
@ -194,8 +208,16 @@ Phaser.Physics.P2.Body.prototype = {
*/
createGroupCallback: function (group, callback, callbackContext) {
this._groupCallbacks[group.mask] = callback;
this._groupCallbackContext[group.mask] = callbackContext;
if (callback === null)
{
delete (this._groupCallbacks[group.mask]);
delete (this._groupCallbacksContext[group.mask]);
}
else
{
this._groupCallbacks[group.mask] = callback;
this._groupCallbackContext[group.mask] = callbackContext;
}
},
@ -228,7 +250,7 @@ Phaser.Physics.P2.Body.prototype = {
* This also resets the collisionMask.
*
* @method Phaser.Physics.P2.Body#setCollisionGroup
* @param {Phaser.Physics.CollisionGroup|array} group - The Collision Group that this Bodies shapes will use.
* @param {Phaser.Physics.CollisionGroup} group - The Collision Group that this Bodies shapes will use.
* @param {p2.Shape} [shape] - An optional Shape. If not provided the collision group will be added to all Shapes in this Body.
*/
setCollisionGroup: function (group, shape) {
@ -246,7 +268,7 @@ Phaser.Physics.P2.Body.prototype = {
else
{
shape.collisionGroup = group.mask;
shapes.collisionMask = mask;
shape.collisionMask = mask;
}
},
@ -902,14 +924,9 @@ Phaser.Physics.P2.Body.prototype = {
else if (Array.isArray(points[0]))
{
path = points[0].slice(0);
// for (var i = 0, len = points[0].length; i < len; i += 2)
// {
// path.push([points[0][i], points[0][i + 1]]);
// }
}
else if (typeof points[0] === 'number')
{
// console.log('addPolygon --- We\'ve a list of numbers');
// We've a list of numbers
for (var i = 0, len = points.length; i < len; i += 2)
{
@ -917,10 +934,6 @@ Phaser.Physics.P2.Body.prototype = {
}
}
// console.log('addPolygon PATH pre');
// console.log(path[1]);
// console.table(path);
// top and tail
var idx = path.length - 1;
@ -936,10 +949,6 @@ Phaser.Physics.P2.Body.prototype = {
path[p][1] = this.world.pxmi(path[p][1]);
}
// console.log('addPolygon PATH POST');
// console.log(path[1]);
// console.table(path);
result = this.data.fromPolygon(path, options);
this.shapeChanged();
@ -1565,6 +1574,21 @@ Object.defineProperty(Phaser.Physics.P2.Body.prototype, "y", {
});
/**
* @name Phaser.Physics.P2.Body#id
* @property {number} id - The Body ID. Each Body that has been added to the World has a unique ID.
* @readonly
*/
Object.defineProperty(Phaser.Physics.P2.Body.prototype, "id", {
get: function () {
return this.data.id;
}
});
/**
* @name Phaser.Physics.P2.Body#debug
* @property {boolean} debug - Enable or disable debug drawing of this body

View file

@ -148,8 +148,6 @@ Phaser.Physics.P2 = function (game, config) {
this.boundsCollidesWith = [];
// Group vs. Group callbacks
// By default we want everything colliding with everything
this.setBoundsToWorld(true, true, true, true, false);
@ -417,6 +415,7 @@ Phaser.Physics.P2.prototype = {
},
/**
* Sets the given material against the 4 bounds of this World.
*
@ -456,6 +455,35 @@ Phaser.Physics.P2.prototype = {
},
/**
* By default the World will be set to collide everything with everything. The bounds of the world is a Body with 4 shapes, one for each face.
* If you start to use your own collision groups then your objects will no longer collide with the bounds.
* To fix this you need to adjust the bounds to use its own collision group first BEFORE changing your Sprites collision group.
*
* @method Phaser.Physics.P2#updateBoundsCollisionGroup
* @param {boolean} [setCollisionGroup=true] - If true the Bounds will be set to use its own Collision Group.
*/
updateBoundsCollisionGroup: function (setCollisionGroup) {
if (typeof setCollisionGroup === 'undefined') { setCollisionGroup = true; }
for (var i = 0; i < 4; i++)
{
if (this._wallShapes[i])
{
if (setCollisionGroup)
{
this._wallShapes[i].collisionGroup = this.boundsCollisionGroup.mask;
}
else
{
this._wallShapes[i].collisionGroup = this.everythingCollisionGroup.mask;
}
}
}
},
/**
* Sets the bounds of the Physics world to match the given world pixel dimensions.
* You can optionally set which 'walls' to create: left, right, top or bottom.
@ -514,8 +542,6 @@ Phaser.Physics.P2.prototype = {
if (setCollisionGroup)
{
this._wallShapes[0].collisionGroup = this.boundsCollisionGroup.mask;
// this._wallShapes[0].collisionGroup = this.everythingCollisionGroup.mask;
// this._wallShapes[0].collisionMask = this.everythingCollisionGroup.mask;
}
this.bounds.addShape(this._wallShapes[0], [this.pxmi(-hw), 0], 1.5707963267948966 );
@ -528,8 +554,6 @@ Phaser.Physics.P2.prototype = {
if (setCollisionGroup)
{
this._wallShapes[1].collisionGroup = this.boundsCollisionGroup.mask;
// this._wallShapes[1].collisionGroup = this.everythingCollisionGroup.mask;
// this._wallShapes[1].collisionMask = this.everythingCollisionGroup.mask;
}
this.bounds.addShape(this._wallShapes[1], [this.pxmi(hw), 0], -1.5707963267948966 );
@ -542,8 +566,6 @@ Phaser.Physics.P2.prototype = {
if (setCollisionGroup)
{
this._wallShapes[2].collisionGroup = this.boundsCollisionGroup.mask;
// this._wallShapes[2].collisionGroup = this.everythingCollisionGroup.mask;
// this._wallShapes[2].collisionMask = this.everythingCollisionGroup.mask;
}
this.bounds.addShape(this._wallShapes[2], [0, this.pxmi(-hh)], -3.141592653589793 );
@ -556,8 +578,6 @@ Phaser.Physics.P2.prototype = {
if (setCollisionGroup)
{
this._wallShapes[3].collisionGroup = this.boundsCollisionGroup.mask;
// this._wallShapes[3].collisionGroup = this.everythingCollisionGroup.mask;
// this._wallShapes[3].collisionMask = this.everythingCollisionGroup.mask;
}
this.bounds.addShape(this._wallShapes[3], [0, this.pxmi(hh)] );
@ -937,12 +957,14 @@ Phaser.Physics.P2.prototype = {
},
/**
* Creates a Collision Group for the wall shapes.
* Creates a new Collision Group and optionally applies it to the given object.
* Collision Groups are handled using bitmasks, therefore you have a fixed limit you can create before you need to re-use older groups.
*
* @method Phaser.Physics.P2#createCollisionGroup
* @param {Phaser.Group|Phaser.Sprite} [object] - An optional Sprite or Group to apply the Collision Group to. If a Group is given it will be applied to all top-level children.
* @protected
*/
createCollisionGroup: function () {
createCollisionGroup: function (object) {
var bitmask = Math.pow(2, this._collisionGroupID);
@ -972,10 +994,42 @@ Phaser.Physics.P2.prototype = {
this.collisionGroups.push(group);
if (object)
{
this.setCollisionGroup(object, group);
}
return group;
},
/**
* Sets the given CollisionGroup to be the collision group for all shapes in this Body, unless a shape is specified.
* Note that this resets the collisionMask and any previously set groups. See Body.collides() for appending them.
*
* @method Phaser.Physics.P2y#setCollisionGroup
* @param {Phaser.Group|Phaser.Sprite} object - A Sprite or Group to apply the Collision Group to. If a Group is given it will be applied to all top-level children.
* @param {Phaser.Physics.CollisionGroup} group - The Collision Group that this Bodies shapes will use.
*/
setCollisionGroup: function (object, group) {
if (object instanceof Phaser.Group)
{
for (var i = 0; i < object.total; i++)
{
if (object.children[i]['body'] && object.children[i]['body'].type === Phaser.Physics.P2JS)
{
object.children[i].body.setCollisionGroup(group);
}
}
}
else
{
object.body.setCollisionGroup(group);
}
},
/**
* @method Phaser.Physics.P2.prototype.createBody
* @param {number} x - The x coordinate of Body.