From 5968dd053b11f59bf9447d520e71bf11d0547a33 Mon Sep 17 00:00:00 2001 From: photonstorm Date: Wed, 19 Feb 2014 01:51:14 +0000 Subject: [PATCH] CollisionGroup and collision masks working. Need to refine a little, but all the essentials are there. --- Gruntfile.js | 1 + build/config.php | 1 + examples/wip/cannon.js | 53 ++++++++++++++++++++++-- examples/wip/platforms.js | 49 ++++++++++++++-------- src/math/Math.js | 4 +- src/physics/Body.js | 77 ++++++++++++++++++++++++++++++++++- src/physics/CollisionGroup.js | 21 ++++++++++ src/physics/World.js | 55 +++++++++++++++++++++++++ 8 files changed, 235 insertions(+), 26 deletions(-) create mode 100644 src/physics/CollisionGroup.js diff --git a/Gruntfile.js b/Gruntfile.js index 58006c80..42d62b78 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -140,6 +140,7 @@ module.exports = function (grunt) { 'src/physics/Spring.js', 'src/physics/Material.js', 'src/physics/ContactMaterial.js', + 'src/physics/CollisionGroup.js', 'src/particles/Particles.js', 'src/particles/arcade/ArcadeParticles.js', diff --git a/build/config.php b/build/config.php index 3940453f..fb00fa88 100644 --- a/build/config.php +++ b/build/config.php @@ -186,6 +186,7 @@ + diff --git a/examples/wip/cannon.js b/examples/wip/cannon.js index ce9069de..0ed68d25 100644 --- a/examples/wip/cannon.js +++ b/examples/wip/cannon.js @@ -6,6 +6,7 @@ function preload() { // game.load.image('arrow', 'assets/sprites/arrow.png'); game.load.image('arrow', 'assets/sprites/thrust_ship2.png'); game.load.image('chunk', 'assets/sprites/chunk.png'); + game.load.image('box', 'assets/sprites/block.png'); game.load.spritesheet('bullets', 'assets/sprites/balls.png', 17, 17); } @@ -16,6 +17,13 @@ var angle = 0; var fireRate = 100; var nextFire = 0; var cursors; +// var PLAYER; +// var BULLET; +// var WORLD; +var boxes; +var playerGroup; +var bulletGroup; +var boxGroup; function create() { @@ -29,9 +37,38 @@ function create() { bullets.createMultiple(250, 'bullets', 0, false); cannon = game.add.sprite(50, 500, 'arrow'); + cannon.name = 'ship'; cannon.physicsEnabled = true; - cannon.body.kinematic = true; - cannon.body.mass = 4; + + playerGroup = game.physics.createCollisionGroup(); + bulletGroup = game.physics.createCollisionGroup(); + boxGroup = game.physics.createCollisionGroup(); + + // cannon.body.data.shapes[0].collisionGroup = PLAYER.mask; + // cannon.body.data.shapes[0].collisionMask = game.physics.WORLD.mask; + + cannon.body.setCollisionGroup(playerGroup); + cannon.body.collides(boxGroup); + + + boxes = game.add.group(); + + for (var i = 0; i < 10; i++) + { + var box = boxes.create(game.rnd.integerInRange(100, 700), game.rnd.integerInRange(100, 500), 'box'); + box.name = 'box' + i; + box.scale.set(0.5); + // box.scale.set(game.rnd.realInRange(0.2, 0.7)); + box.physicsEnabled = true; + box.body.setCollisionGroup(boxGroup); + box.body.collides(playerGroup); + box.body.collides(bulletGroup); + // box.body.mass = 10; + // box.body.setMaterial(boxMaterial); + // box.body.fixedRotation = true; + } + + cursors = game.input.keyboard.createCursorKeys(); } @@ -58,6 +95,12 @@ function fire() { bullet.body.velocity.x = magnitude * Math.cos(angle); bullet.body.velocity.y = magnitude * Math.sin(angle); + + bullet.body.setCollisionGroup(bulletGroup); + bullet.body.collides(boxGroup); + + // bullet.body.data.shapes[0].collisionGroup = BULLET; + // bullet.body.data.shapes[0].collisionMask = WORLD | BULLET; } } @@ -80,11 +123,13 @@ function update() { if (cursors.up.isDown) { - cannon.body.moveForward(200); + // cannon.body.moveForward(200); + cannon.body.thrust(200); } else if (cursors.down.isDown) { - cannon.body.moveBackward(200); + // cannon.body.moveBackward(200); + cannon.body.reverse(200); } var dx = game.input.activePointer.worldX - cannon.x; diff --git a/examples/wip/platforms.js b/examples/wip/platforms.js index 528e2ce6..b4e792d3 100644 --- a/examples/wip/platforms.js +++ b/examples/wip/platforms.js @@ -14,8 +14,8 @@ var facing = 'left'; var jumpTimer = 0; var cursors; var jumpButton; -var box1; -var box2; + +var boxes; function create() { @@ -39,40 +39,53 @@ function create() { // game.physics.setBoundsToWorld(); - game.physics.gravity.y = 20; + // game.physics.gravity.y = 9.78; + game.physics.setBoundsToWorld(true, true, false, true); + + game.physics.world.gravity[1] = -20; game.physics.friction = 0.5; + game.physics.world.solver.stiffness = 1e20; + game.physics.world.solver.relaxation = 3; // Materials var groundMaterial = game.physics.createMaterial('ground'); var characterMaterial = game.physics.createMaterial('character'); var boxMaterial = game.physics.createMaterial('box'); - player = game.add.sprite(32, 320, 'dude'); + player = game.add.sprite(100, -400, 'dude'); player.physicsEnabled = true; player.body.fixedRotation = true; player.body.setMaterial(characterMaterial); + player.body.mass = 1; + player.body.damping = 0.5; player.animations.add('left', [0, 1, 2, 3], 10, true); player.animations.add('turn', [4], 20, true); player.animations.add('right', [5, 6, 7, 8], 10, true); - box1 = game.add.sprite(200, 300, 'box'); - box1.physicsEnabled = true; - // box1.body.fixedRotation = true; - box1.body.setMaterial(boxMaterial); + boxes = game.add.group(); - box2 = game.add.sprite(400, 300, 'box'); - box2.physicsEnabled = true; - // box2.body.fixedRotation = true; - box2.body.setMaterial(boxMaterial); + for (var i = 0; i < 50; i++) + { + var box = boxes.create(game.rnd.integerInRange(200, 700), game.rnd.integerInRange(-200, 400), 'box'); + // box.scale.set(0.5); + box.scale.set(game.rnd.realInRange(0.2, 0.7)); + box.physicsEnabled = true; + box.body.mass = 10; + box.body.setMaterial(boxMaterial); + box.body.fixedRotation = true; + } // Set the material along the ground - game.physics.setWorldMaterial(groundMaterial, false, false, false, true); + game.physics.setWorldMaterial(groundMaterial); var groundCharacterCM = game.physics.createContactMaterial(groundMaterial, characterMaterial, { friction: 0.0 }); // no friction between character and ground var boxCharacterCM = game.physics.createContactMaterial(boxMaterial, characterMaterial, { friction: 0.0 }); // No friction between character and boxes var boxGroundCM = game.physics.createContactMaterial(boxMaterial, groundMaterial, { friction: 0.6 }); // Between boxes and ground + console.log(groundCharacterCM); + console.log(boxGroundCM); + // game.camera.follow(player); cursors = game.input.keyboard.createCursorKeys(); @@ -82,11 +95,9 @@ function create() { function update() { - player.body.velocity.x = 0; - if (cursors.left.isDown) { - player.body.moveLeft(150); + player.body.moveLeft(200); if (facing != 'left') { @@ -96,7 +107,7 @@ function update() { } else if (cursors.right.isDown) { - player.body.moveRight(150); + player.body.moveRight(200); if (facing != 'right') { @@ -106,6 +117,8 @@ function update() { } else { + player.body.velocity.x = 0; + if (facing != 'idle') { player.animations.stop(); @@ -150,7 +163,7 @@ function render () { // if (player.debug) // { - // game.debug.renderPhysicsBody(player.body); + game.debug.renderPhysicsBody(player.body); // game.debug.renderBodyInfo(player, 16, 24); // } diff --git a/src/math/Math.js b/src/math/Math.js index fd1dfd89..a1876cb6 100644 --- a/src/math/Math.js +++ b/src/math/Math.js @@ -1292,7 +1292,7 @@ Phaser.Math = { }, /** - * Convert p2 physics value to pixel scale. + * Convert p2 physics value (meters) to pixel scale. * * @method Phaser.Math#p2px * @param {number} v - The value to convert. @@ -1303,7 +1303,7 @@ Phaser.Math = { }, /** - * Convert pixel value to p2 physics scale. + * Convert pixel value to p2 physics scale (meters). * * @method Phaser.Math#px2p * @param {number} v - The value to convert. diff --git a/src/physics/Body.js b/src/physics/Body.js index 8fc62f6c..9e1faf87 100644 --- a/src/physics/Body.js +++ b/src/physics/Body.js @@ -63,6 +63,19 @@ Phaser.Physics.Body = function (game, sprite, x, y, mass) { */ this.gravity = new Phaser.Point(); + /** + * 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? + */ + this.collideWorldBounds = true; + + /** + * @property {array} collidesWith - Array of CollisionGroups that this Bodies shapes collide with. + * @private + */ + this.collidesWith = []; + // this.onAdded = new Phaser.Signal(); // this.onRemoved = new Phaser.Signal(); @@ -77,6 +90,66 @@ Phaser.Physics.Body = function (game, sprite, x, y, mass) { Phaser.Physics.Body.prototype = { + setCollisionGroup: function (group, shape) { + + if (typeof shape === 'undefined') + { + for (var i = this.data.shapes.length - 1; i >= 0; i--) + { + this.data.shapes[i].collisionGroup = group.mask; + } + } + else + { + shape.collisionGroup = group.mask; + } + + }, + + /** + * Adds the given CollisionGroup to the list of groups that this body will collide with and updates the collision mask. + * + * @method Phaser.Physics.Body#collides + * @param {Phaser.Physics.CollisionGroup} group - The Collision Group that this Bodies shapes will collide with. + * @param {p2.Shape} [shape] - An optional Shape. If not provided the collision mask will be added to all Shapes in this Body. + */ + collides: function (group, shape) { + + // TODO: group can be an array + + if (this.collidesWith.indexOf(group) === -1) + { + this.collidesWith.push(group); + } + + var mask = 0; + + if (this.collideWorldBounds) + { + mask = this.game.physics.boundsCollisionGroup.mask; + } + + for (var i = 0; i < this.collidesWith.length; i++) + { + mask = mask | this.collidesWith[i].mask; + } + + if (typeof shape === 'undefined') + { + for (var i = this.data.shapes.length - 1; i >= 0; i--) + { + this.data.shapes[i].collisionMask = mask; + } + } + else + { + shape.collisionMask = mask; + } + + console.log('collides', this.sprite.name, group, mask); + + }, + /** * Moves the shape offsets so their center of mass becomes the body center of mass. * @@ -819,7 +892,7 @@ Phaser.Physics.Body.prototype = { }, /** - * Convert p2 physics value to pixel scale. + * Convert p2 physics value (meters) to pixel scale. * * @method Phaser.Math#p2px * @param {number} v - The value to convert. @@ -832,7 +905,7 @@ Phaser.Physics.Body.prototype = { }, /** - * Convert pixel value to p2 physics scale. + * Convert pixel value to p2 physics scale (meters). * * @method Phaser.Math#px2p * @param {number} v - The value to convert. diff --git a/src/physics/CollisionGroup.js b/src/physics/CollisionGroup.js new file mode 100644 index 00000000..5f27ea57 --- /dev/null +++ b/src/physics/CollisionGroup.js @@ -0,0 +1,21 @@ +/** +* @author Richard Davey +* @copyright 2014 Photon Storm Ltd. +* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License} +*/ + +/** +* Collision Group +* +* @class Phaser.Physics.CollisionGroup +* @classdesc Physics Collision Group Constructor +* @constructor +*/ +Phaser.Physics.CollisionGroup = function (bitmask) { + + /** + * @property {number} mask - The CollisionGroup bitmask. + */ + this.mask = bitmask; + +} diff --git a/src/physics/World.js b/src/physics/World.js index 18dfdd9b..bd712490 100644 --- a/src/physics/World.js +++ b/src/physics/World.js @@ -132,6 +132,20 @@ Phaser.Physics.World = function (game) { this.world.on("beginContact", this.beginContactHandler, this); this.world.on("endContact", this.endContactHandler, this); + /** + * @property {array} collisionGroups - Internal var. + */ + this.collisionGroups = []; + + /** + * @property {number} _collisionGroupID - Internal var. + * @private + */ + this._collisionGroupID = 2; + + this.boundsCollisionGroup = new Phaser.Physics.CollisionGroup(2); + this.boundsCollidesWith = []; + this.setBoundsToWorld(true, true, true, true); }; @@ -310,24 +324,28 @@ Phaser.Physics.World.prototype = { if (left) { this._wallShapes[0] = new p2.Plane(); + this._wallShapes[0].collisionGroup = this.boundsCollisionGroup.mask; this.bounds.addShape(this._wallShapes[0], [this.game.math.px2p(-hw), 0], 1.5707963267948966 ); } if (right) { this._wallShapes[1] = new p2.Plane(); + this._wallShapes[1].collisionGroup = this.boundsCollisionGroup.mask; this.bounds.addShape(this._wallShapes[1], [this.game.math.px2p(hw), 0], -1.5707963267948966 ); } if (top) { this._wallShapes[2] = new p2.Plane(); + this._wallShapes[2].collisionGroup = this.boundsCollisionGroup.mask; this.bounds.addShape(this._wallShapes[2], [0, this.game.math.px2p(-hh)], -3.141592653589793 ); } if (bottom) { this._wallShapes[3] = new p2.Plane(); + this._wallShapes[3].collisionGroup = this.boundsCollisionGroup.mask; this.bounds.addShape(this._wallShapes[3], [0, this.game.math.px2p(hh)] ); } @@ -662,6 +680,43 @@ Phaser.Physics.World.prototype = { }, + createCollisionGroup: function () { + + var bitmask = Math.pow(2, this._collisionGroupID); + + // Add it to the bounds mask automatically (they won't collide with it unless it's added back by the other group) + // this.boundsCollisionGroup.mask = this.boundsCollisionGroup.mask | bitmask; + + if (this._wallShapes[0]) + { + this._wallShapes[0].collisionMask = this._wallShapes[0].collisionMask | bitmask; + } + + if (this._wallShapes[1]) + { + this._wallShapes[1].collisionMask = this._wallShapes[1].collisionMask | bitmask; + } + + if (this._wallShapes[2]) + { + this._wallShapes[2].collisionMask = this._wallShapes[2].collisionMask | bitmask; + } + + if (this._wallShapes[3]) + { + this._wallShapes[3].collisionMask = this._wallShapes[3].collisionMask | bitmask; + } + + this._collisionGroupID++; + + var group = new Phaser.Physics.CollisionGroup(bitmask); + + this.collisionGroups.push(group); + + return group; + + }, + /** * @method Phaser.Physics.World.prototype.createBody * @param {number} x - The x coordinate of Body.