diff --git a/examples/body3.php b/examples/body3.php index f6a39407..af9f62f4 100644 --- a/examples/body3.php +++ b/examples/body3.php @@ -26,27 +26,38 @@ function create() { game.world._stage.backgroundColorString = '#182d3b'; + // game.physics.gravity.y = 10; - bunny = game.add.sprite(200, 200, 'bunny'); - bunny.body.bounce.x = 0.8; + bunny = game.add.sprite(200, 100, 'bunny'); + bunny.body.bounce.y = 0.5; + // bunny.body.gravity.y = -10; + // bunny.body.setSize(20, 100); + // bunny.body.offset.setTo(25, -20); + // bunny.anchor.setTo(0.5, 0.5); - wall = game.add.sprite(700, 200, 'bunny'); + wall = game.add.sprite(200, 450, 'bunny'); wall.body.immovable = true; + wall.body.allowGravity = false; - bunny.body.velocity.x = 180; + // bunny.body.angularVelocity = 1; + bunny.body.velocity.x = 30; + // bunny.body.velocity.y = 120; + // bunny.body.velocity.y = 50; } function update() { - game.physics.separateX(bunny.body, wall.body); + // bunny.rotation += 0.01; + game.physics.separate(bunny.body, wall.body); } function render() { game.debug.renderSpriteCorners(bunny, true, true); - game.debug.renderRectangle(bunny.body.hitArea); + game.debug.renderRectangle(bunny.body.bounds); + game.debug.renderRectangle(wall.body.bounds); } diff --git a/src/physics/arcade/ArcadePhysics.js b/src/physics/arcade/ArcadePhysics.js index 77fcc2bc..40a1388d 100644 --- a/src/physics/arcade/ArcadePhysics.js +++ b/src/physics/arcade/ArcadePhysics.js @@ -4,7 +4,8 @@ Phaser.Physics.Arcade = function (game) { this.game = game; - this._length = 0; + this.gravity = new Phaser.Point; + this.bounds = new Phaser.Rectangle(0, 0, game.world.width, game.world.height); /** * @type {number} @@ -20,11 +21,11 @@ Phaser.Physics.Arcade = function (game) { this.FLOOR = this.DOWN; this.WALL = this.LEFT | this.RIGHT; this.ANY = this.LEFT | this.RIGHT | this.UP | this.DOWN; - // console.log('any', this.ANY); + this.OVERLAP_BIAS = 4; this.TILE_OVERLAP = false; - // avoid creating these every frame, per collision + // avoid gc spikes by caching these values for re-use this._obj1Bounds = new Phaser.Rectangle; this._obj2Bounds = new Phaser.Rectangle; this._overlap = 0; @@ -35,14 +36,6 @@ Phaser.Physics.Arcade = function (game) { this._obj2NewVelocity = 0; this._average = 0; - // this.angularDrag = 0; - // this.gravity = new Phaser.Point; - // this.drag = new Phaser.Point; - // this.bounce = new Phaser.Point; - // this.bounds = new Phaser.Rectangle(0, 0, game.world.width, game.world.height); - // this._distance = new Phaser.Point; - // this._tangent = new Phaser.Point; - }; Phaser.Physics.Arcade.prototype = { @@ -55,21 +48,21 @@ Phaser.Physics.Arcade.prototype = { updateMotion: function (body) { - this._velocityDelta = (this.computeVelocity(body.angularVelocity, body.gravity.x, body.angularAcceleration, body.angularDrag, body.maxAngular) - body.angularVelocity) / 2; - body.angularVelocity += this._velocityDelta; - body.sprite.rotation += body.angularVelocity * this.game.time.physicsElapsed; + // Rotation + this._velocityDelta = (this.computeVelocity(0, false, body.angularVelocity, body.angularAcceleration, body.angularDrag, body.maxAngular) - body.angularVelocity) / 2; body.angularVelocity += this._velocityDelta; + body.rotation += body.angularVelocity * this.game.time.physicsElapsed; - this._velocityDelta = (this.computeVelocity(body.velocity.x, body.gravity.x, body.acceleration.x, body.drag.x) - body.velocity.x) / 2; - body.velocity.x += this._velocityDelta; - this._delta = body.velocity.x * this.game.time.physicsElapsed; + // Horizontal + this._velocityDelta = (this.computeVelocity(1, body, body.velocity.x, body.acceleration.x, body.drag.x) - body.velocity.x) / 2; body.velocity.x += this._velocityDelta; + this._delta = body.velocity.x * this.game.time.physicsElapsed; body.x += this._delta; - this._velocityDelta = (this.computeVelocity(body.velocity.y, body.gravity.y, body.acceleration.y, body.drag.y) - body.velocity.y) / 2; + // Vertical + this._velocityDelta = (this.computeVelocity(2, body, body.velocity.y, body.acceleration.y, body.drag.y) - body.velocity.y) / 2; body.velocity.y += this._velocityDelta; this._delta = body.velocity.y * this.game.time.physicsElapsed; - body.velocity.y += this._velocityDelta; body.y += this._delta; }, @@ -84,16 +77,22 @@ Phaser.Physics.Arcade.prototype = { * * @return {number} The altered Velocity value. */ - computeVelocity: function (velocity, gravity, acceleration, drag, max) { + computeVelocity: function (axis, body, velocity, acceleration, drag, max) { - gravity = gravity || 0; - acceleration = acceleration || 0; - drag = drag || 0; max = max || 10000; + if (axis == 1 && body.allowGravity) + { + velocity += this.gravity.x + body.gravity.x; + } + else if (axis == 2 && body.allowGravity) + { + velocity += this.gravity.y + body.gravity.y; + } + if (acceleration !== 0) { - velocity += (acceleration + gravity) * this.game.time.physicsElapsed; + velocity += acceleration * this.game.time.physicsElapsed; } else if (drag !== 0) { @@ -111,11 +110,9 @@ Phaser.Physics.Arcade.prototype = { { velocity = 0; } - - velocity += gravity; } - if ((velocity != 0) && (max != 10000)) + if (velocity != 0) { if (velocity > max) { @@ -176,10 +173,7 @@ Phaser.Physics.Arcade.prototype = { */ separate: function (object1, object2) { - var separatedX = this.separateX(object1, object2); - var separatedY = this.separateY(object1, object2); - - return separatedX || separatedY; + return this.separateX(object1, object2) || this.separateY(object1, object2) }, @@ -207,7 +201,7 @@ Phaser.Physics.Arcade.prototype = { this._obj1Bounds.setTo(object1.x - ((object1.deltaX() > 0) ? object1.deltaX() : 0), object1.lastY, object1.width + ((object1.deltaX() > 0) ? object1.deltaX() : -object1.deltaX()), object1.height); this._obj2Bounds.setTo(object2.x - ((object2.deltaX() > 0) ? object2.deltaX() : 0), object2.lastY, object2.width + ((object2.deltaX() > 0) ? object2.deltaX() : -object2.deltaX()), object2.height); - if ((this._obj1Bounds.x + this._obj1Bounds.width > this._obj2Bounds.x) && (this._obj1Bounds.x < this._obj2Bounds.x + this._obj2Bounds.width) && (this._obj1Bounds.y + this._obj1Bounds.height > this._obj2Bounds.y) && (this._obj1Bounds.y < this._obj2Bounds.y + this._obj2Bounds.height)) + if ((this._obj1Bounds.right > this._obj2Bounds.x) && (this._obj1Bounds.x < this._obj2Bounds.right) && (this._obj1Bounds.bottom > this._obj2Bounds.y) && (this._obj1Bounds.y < this._obj2Bounds.bottom)) { this._maxOverlap = object1.deltaAbsX() + object2.deltaAbsX() + this.OVERLAP_BIAS; @@ -239,9 +233,7 @@ Phaser.Physics.Arcade.prototype = { object1.touching |= this.LEFT; object2.touching |= this.RIGHT; } - } - } } @@ -275,7 +267,6 @@ Phaser.Physics.Arcade.prototype = { object2.x += this._overlap; object2.velocity.x = this._obj1Velocity - this._obj2Velocity * object2.bounce.x; } - return true; } else @@ -294,100 +285,96 @@ Phaser.Physics.Arcade.prototype = { separateY: function (object1, object2) { // Can't separate two immovable objects - if (object1.immovable && object2.immovable) { + if (object1.immovable && object2.immovable) + { return false; } // First, get the two object deltas - var overlap = 0; - var obj1Delta = object1.y - object1.last.y; - var obj2Delta = object2.y - object2.last.y; + this._overlap = 0; - if (obj1Delta != obj2Delta) + if (object1.deltaY() != object2.deltaY()) { // Check if the Y hulls actually overlap - var obj1DeltaAbs = (obj1Delta > 0) ? obj1Delta : -obj1Delta; - var obj2DeltaAbs = (obj2Delta > 0) ? obj2Delta : -obj2Delta; - this._obj1Bounds = new Phaser.Rectangle(object1.x, object1.y - ((obj1Delta > 0) ? obj1Delta : 0), object1.width, object1.height + obj1DeltaAbs); - this._obj2Bounds = new Phaser.Rectangle(object2.x, object2.y - ((obj2Delta > 0) ? obj2Delta : 0), object2.width, object2.height + obj2DeltaAbs); + this._obj1Bounds.setTo(object1.x, object1.y - ((object1.deltaY() > 0) ? object1.deltaY() : 0), object1.width, object1.height + object1.deltaAbsY()); + this._obj2Bounds.setTo(object2.x, object2.y - ((object2.deltaY() > 0) ? object2.deltaY() : 0), object2.width, object2.height + object2.deltaAbsY()); - if ((this._obj1Bounds.x + this._obj1Bounds.width > this._obj2Bounds.x) && (this._obj1Bounds.x < this._obj2Bounds.x + this._obj2Bounds.width) && (this._obj1Bounds.y + this._obj1Bounds.height > this._obj2Bounds.y) && (this._obj1Bounds.y < this._obj2Bounds.y + this._obj2Bounds.height)) + if ((this._obj1Bounds.right > this._obj2Bounds.x) && (this._obj1Bounds.x < this._obj2Bounds.right) && (this._obj1Bounds.bottom > this._obj2Bounds.y) && (this._obj1Bounds.y < this._obj2Bounds.bottom)) { - var maxOverlap = obj1DeltaAbs + obj2DeltaAbs + Collision.OVERLAP_BIAS; + this._maxOverlap = object1.deltaAbsY() + object2.deltaAbsY() + this.OVERLAP_BIAS; // If they did overlap (and can), figure out by how much and flip the corresponding flags - if (obj1Delta > obj2Delta) + if (object1.deltaY() > object2.deltaY()) { - overlap = object1.y + object1.height - object2.y; + this._overlap = object1.y + object1.height - object2.y; - if ((overlap > maxOverlap) || !(object1.allowCollisions & Collision.DOWN) || !(object2.allowCollisions & Collision.UP)) + if ((this._overlap > this._maxOverlap) || !(object1.allowCollisions & this.DOWN) || !(object2.allowCollisions & this.UP)) { - overlap = 0; + this._overlap = 0; } else { - object1.touching |= Collision.DOWN; - object2.touching |= Collision.UP; + object1.touching |= this.DOWN; + object2.touching |= this.UP; } } - else if (obj1Delta < obj2Delta) + else if (object1.deltaY() < object2.deltaY()) { - overlap = object1.y - object2.height - object2.y; + this._overlap = object1.y - object2.height - object2.y; - if ((-overlap > maxOverlap) || !(object1.allowCollisions & Collision.UP) || !(object2.allowCollisions & Collision.DOWN)) + if ((-this._overlap > this._maxOverlap) || !(object1.allowCollisions & this.UP) || !(object2.allowCollisions & this.DOWN)) { - overlap = 0; + this._overlap = 0; } else { - object1.touching |= Collision.UP; - object2.touching |= Collision.DOWN; + object1.touching |= this.UP; + object2.touching |= this.DOWN; } } } } // Then adjust their positions and velocities accordingly (if there was any overlap) - if (overlap != 0) + if (this._overlap != 0) { this._obj1Velocity = object1.velocity.y; this._obj2Velocity = object2.velocity.y; if (!object1.immovable && !object2.immovable) { - overlap *= 0.5; - object1.y = object1.y - overlap; - object2.y += overlap; + this._overlap *= 0.5; + object1.y = object1.y - this._overlap; + object2.y += this._overlap; this._obj1NewVelocity = Math.sqrt((this._obj2Velocity * this._obj2Velocity * object2.mass) / object1.mass) * ((this._obj2Velocity > 0) ? 1 : -1); this._obj2NewVelocity = Math.sqrt((this._obj1Velocity * this._obj1Velocity * object1.mass) / object2.mass) * ((this._obj1Velocity > 0) ? 1 : -1); this._average = (this._obj1NewVelocity + this._obj2NewVelocity) * 0.5; this._obj1NewVelocity -= this._average; this._obj2NewVelocity -= this._average; - object1.velocity.y = this._average + this._obj1NewVelocity * object1.elasticity; - object2.velocity.y = this._average + this._obj2NewVelocity * object2.elasticity; + object1.velocity.y = this._average + this._obj1NewVelocity * object1.bounce.y; + object2.velocity.y = this._average + this._obj2NewVelocity * object2.bounce.y; } else if (!object1.immovable) { - object1.y = object1.y - overlap; - object1.velocity.y = this._obj2Velocity - this._obj1Velocity * object1.elasticity; + object1.y = object1.y - this._overlap; + object1.velocity.y = this._obj2Velocity - this._obj1Velocity * object1.bounce.y; // This is special case code that handles things like horizontal moving platforms you can ride - if (object2.active && object2.moves && (obj1Delta > obj2Delta)) + if (object2.active && object2.moves && (object1.deltaY() > object2.deltaY())) { - object1.x += object2.x - object2.last.x; + object1.x += object2.x - object2.lastX; } } else if (!object2.immovable) { - object2.y += overlap; - object2.velocity.y = this._obj1Velocity - this._obj2Velocity * object2.elasticity; + object2.y += this._overlap; + object2.velocity.y = this._obj1Velocity - this._obj2Velocity * object2.bounce.y; // This is special case code that handles things like horizontal moving platforms you can ride - if (object1.active && object1.moves && (obj1Delta < obj2Delta)) + if (object1.sprite.active && object1.moves && (object1.deltaY() < object2.deltaY())) { - object2.x += object1.x - object1.last.x; + object2.x += object1.x - object1.lastX; } } - return true; } else diff --git a/src/physics/arcade/Body.js b/src/physics/arcade/Body.js index 72b769b7..5dcfe577 100644 --- a/src/physics/arcade/Body.js +++ b/src/physics/arcade/Body.js @@ -3,11 +3,25 @@ Phaser.Physics.Arcade.Body = function (sprite) { this.sprite = sprite; this.game = sprite.game; - this.hitArea = new Phaser.Rectangle(sprite.x, sprite.y, sprite.currentFrame.sourceSizeW, sprite.currentFrame.sourceSizeH); this.offset = new Phaser.Point; - this.width = this.hitArea.width; - this.height = this.hitArea.height; + // the top-left of the Body + this.x = sprite.x; + this.y = sprite.y; + + // un-scaled original size + this.sourceWidth = sprite.currentFrame.sourceSizeW; + this.sourceHeight = sprite.currentFrame.sourceSizeH; + + // calculated (scaled) size + this.width = sprite.currentFrame.sourceSizeW; + this.height = sprite.currentFrame.sourceSizeH; + this.halfWidth = Math.floor(sprite.currentFrame.sourceSizeW / 2); + this.halfHeight = Math.floor(sprite.currentFrame.sourceSizeH / 2); + + this.bounds = new Phaser.Rectangle(sprite.x, sprite.y, this.width, this.height); + + // Scale value cache this._sx = sprite.scale.x; this._sy = sprite.scale.y; @@ -24,13 +38,26 @@ Phaser.Physics.Arcade.Body = function (sprite) { this.maxAngular = 1000; this.mass = 1; + // Handy consts + this.LEFT = 0x0001; + this.RIGHT = 0x0010; + this.UP = 0x0100; + this.DOWN = 0x1000; + this.NONE = 0; + this.CEILING = this.UP; + this.FLOOR = this.DOWN; + this.WALL = this.LEFT | this.RIGHT; + this.ANY = this.LEFT | this.RIGHT | this.UP | this.DOWN; + this.immovable = false; + this.moves = true; this.touching = 0; this.wasTouching = 0; - this.allowCollisions = 4369; + this.rotation = 0; + this.allowCollisions = this.ANY; + this.allowRotation = false; + this.allowGravity = true; - this.x = sprite.x; - this.y = sprite.y; this.lastX = sprite.x; this.lastY = sprite.y; @@ -38,23 +65,20 @@ Phaser.Physics.Arcade.Body = function (sprite) { Phaser.Physics.Arcade.Body.prototype = { - sprite: null, - game: null, - hitArea: null, - - updateBounds: function (x, y, scaleX, scaleY) { + updateBounds: function (centerX, centerY, scaleX, scaleY) { if (scaleX != this._sx || scaleY != this._sy) { - this.hitArea.width = this.width * scaleX; - this.hitArea.height = this.height * scaleY; + this.width = this.sourceWidth * scaleX; + this.height = this.sourceHeight * scaleY; + this.halfWidth = Math.floor(this.width / 2); + this.halfHeight = Math.floor(this.height / 2); + this.bounds.width = this.width; + this.bounds.height = this.height; this._sx = scaleX; this._sy = scaleY; } - this.hitArea.centerX = x; - this.hitArea.centerY = y; - }, update: function () { @@ -64,49 +88,62 @@ Phaser.Physics.Arcade.Body.prototype = { this.game.physics.updateMotion(this); - // delta force - _x and _y now contain the new positions, so work out the deltas - // separation and stuff happens here + this.bounds.x = this.x; + this.bounds.y = this.y; }, postUpdate: function () { - this.sprite.x = this.x; - this.sprite.y = this.y; + this.sprite.x = this.x - this.offset.x + (this.sprite.anchor.x * this.width); + this.sprite.y = this.y - this.offset.y + (this.sprite.anchor.y * this.height); + + if (this.allowRotation) + { + this.sprite.angle = this.rotation; + } }, - setSize: function (width, height) { + setSize: function (width, height, offsetX, offsetY) { - this.width = width; - this.height = height; - this.hitArea.width = this.width * scaleX; - this.hitArea.height = this.height * scaleY; + offsetX = offsetX || this.offset.x; + offsetY = offsetY || this.offset.y; + + this.sourceWidth = width; + this.sourceHeight = height; + this.width = this.sourceWidth * this._sx; + this.height = this.sourceHeight * this._sy; + this.halfWidth = Math.floor(this.width / 2); + this.halfHeight = Math.floor(this.height / 2); + this.bounds.width = this.width; + this.bounds.height = this.height; + this.offset.setTo(offsetX, offsetY); }, hullWidth: function () { - if (this.deltaX > 0) + if (this.deltaX() > 0) { - return this.hitArea.width + this.deltaX; + return this.width + this.deltaX(); } else { - return this.hitArea.width - this.deltaX; + return this.width - this.deltaX(); } }, hullHeight: function () { - if (this.deltaY > 0) + if (this.deltaY() > 0) { - return this.hitArea.height + this.deltaY; + return this.height + this.deltaY(); } else { - return this.hitArea.height - this.deltaY; + return this.height - this.deltaY(); } },