From 3e53c0671d9ac672b054145e6bf1938be643bca9 Mon Sep 17 00:00:00 2001 From: Richard Davey Date: Thu, 30 May 2013 23:03:56 +0100 Subject: [PATCH] About to start re-doing collideShapes --- Phaser/Phaser.csproj | 4 + Phaser/Statics.ts | 54 ++++ Phaser/components/sprite/Physics.ts | 20 +- Phaser/core/Circle.ts | 1 - Phaser/physics/AABB.ts | 21 +- Phaser/physics/Circle.ts | 143 +++++++++ Phaser/physics/PhysicsManager.ts | 325 +++++++++++++++----- Phaser/utils/RectangleUtils.ts | 11 - Tests/Tests.csproj | 8 + Tests/phaser.js | 451 +++++++++++++++++++++------- Tests/physics/aabb vs aabb 1.js | 47 +++ Tests/physics/aabb vs aabb 1.ts | 77 +++++ Tests/physics/circle 1.js | 38 +++ Tests/physics/circle 1.ts | 65 ++++ build/phaser.d.ts | 83 ++++- build/phaser.js | 451 +++++++++++++++++++++------- 16 files changed, 1477 insertions(+), 322 deletions(-) create mode 100644 Phaser/physics/Circle.ts create mode 100644 Tests/physics/aabb vs aabb 1.js create mode 100644 Tests/physics/aabb vs aabb 1.ts create mode 100644 Tests/physics/circle 1.js create mode 100644 Tests/physics/circle 1.ts diff --git a/Phaser/Phaser.csproj b/Phaser/Phaser.csproj index 60c9de63..e3c83e30 100644 --- a/Phaser/Phaser.csproj +++ b/Phaser/Phaser.csproj @@ -116,6 +116,10 @@ AABB.ts + + + Circle.ts + IPhysicsShape.ts diff --git a/Phaser/Statics.ts b/Phaser/Statics.ts index 472a1d3c..23bc4afb 100644 --- a/Phaser/Statics.ts +++ b/Phaser/Statics.ts @@ -24,6 +24,60 @@ module Phaser { static GEOM_LINE: number = 3; static GEOM_POLYGON: number = 4; + /** + * Flag used to allow GameObjects to collide on their left side + * @type {number} + */ + static LEFT: number = 0x0001; + + /** + * Flag used to allow GameObjects to collide on their right side + * @type {number} + */ + static RIGHT: number = 0x0010; + + /** + * Flag used to allow GameObjects to collide on their top side + * @type {number} + */ + static UP: number = 0x0100; + + /** + * Flag used to allow GameObjects to collide on their bottom side + * @type {number} + */ + static DOWN: number = 0x1000; + + /** + * Flag used with GameObjects to disable collision + * @type {number} + */ + static NONE: number = 0; + + /** + * Flag used to allow GameObjects to collide with a ceiling + * @type {number} + */ + static CEILING: number = Types.UP; + + /** + * Flag used to allow GameObjects to collide with a floor + * @type {number} + */ + static FLOOR: number = Types.DOWN; + + /** + * Flag used to allow GameObjects to collide with a wall (same as LEFT+RIGHT) + * @type {number} + */ + static WALL: number = Types.LEFT | Types.RIGHT; + + /** + * Flag used to allow GameObjects to collide on any face + * @type {number} + */ + static ANY: number = Types.LEFT | Types.RIGHT | Types.UP | Types.DOWN; + } } diff --git a/Phaser/components/sprite/Physics.ts b/Phaser/components/sprite/Physics.ts index 7379d0ff..cce6c279 100644 --- a/Phaser/components/sprite/Physics.ts +++ b/Phaser/components/sprite/Physics.ts @@ -2,6 +2,8 @@ /// /// /// +/// +/// /** * Phaser - Components - Physics @@ -23,6 +25,7 @@ module Phaser.Components { this.velocity = new Vec2; this.acceleration = new Vec2; + this.touching = Phaser.Types.NONE; this.shape = this.game.world.physics.add(new Phaser.Physics.AABB(this.game, this._sprite, this._sprite.x, this._sprite.y, this._sprite.width, this._sprite.height)); @@ -37,7 +40,7 @@ module Phaser.Components { * Whether this object will be moved by impacts with other objects or not. * @type {boolean} */ - public immovable: bool; + public immovable: bool = false; /** * Set this to false if you want to skip the automatic movement stuff @@ -52,12 +55,22 @@ module Phaser.Components { public velocity: Vec2; public acceleration: Vec2; + public touching: number; + + public setCircle(diameter: number) { + + this.game.world.physics.remove(this.shape); + this.shape = this.game.world.physics.add(new Phaser.Physics.Circle(this.game, this._sprite, this._sprite.x, this._sprite.y, diameter)); + this._sprite.physics.shape.physics = this; + + } + /** * Internal function for updating the position and speed of this object. */ public update() { - if (this.moves) + if (this.moves && this.shape) { this._sprite.x = (this.shape.position.x - this.shape.bounds.halfWidth) - this.shape.offset.x; this._sprite.y = (this.shape.position.y - this.shape.bounds.halfHeight) - this.shape.offset.y; @@ -74,7 +87,8 @@ module Phaser.Components { this._sprite.texture.context.fillStyle = color; this._sprite.texture.context.fillText('Sprite: (' + this._sprite.frameBounds.width + ' x ' + this._sprite.frameBounds.height + ')', x, y); - this._sprite.texture.context.fillText('x: ' + this._sprite.frameBounds.x.toFixed(1) + ' y: ' + this._sprite.frameBounds.y.toFixed(1) + ' rotation: ' + this._sprite.rotation.toFixed(1), x, y + 14); + //this._sprite.texture.context.fillText('x: ' + this._sprite.frameBounds.x.toFixed(1) + ' y: ' + this._sprite.frameBounds.y.toFixed(1) + ' rotation: ' + this._sprite.rotation.toFixed(1), x, y + 14); + this._sprite.texture.context.fillText('x: ' + this.shape.bounds.x.toFixed(1) + ' y: ' + this.shape.bounds.y.toFixed(1) + ' rotation: ' + this._sprite.rotation.toFixed(1), x, y + 14); this._sprite.texture.context.fillText('vx: ' + this.velocity.x.toFixed(1) + ' vy: ' + this.velocity.y.toFixed(1), x, y + 28); this._sprite.texture.context.fillText('ax: ' + this.acceleration.x.toFixed(1) + ' ay: ' + this.acceleration.y.toFixed(1), x, y + 42); diff --git a/Phaser/core/Circle.ts b/Phaser/core/Circle.ts index e2468462..a5d98a34 100644 --- a/Phaser/core/Circle.ts +++ b/Phaser/core/Circle.ts @@ -27,7 +27,6 @@ module Phaser { private _diameter: number = 0; private _radius: number = 0; - //private _pos: Vec2; /** * The x coordinate of the center of the circle diff --git a/Phaser/physics/AABB.ts b/Phaser/physics/AABB.ts index ad5ec9ce..a508cbd8 100644 --- a/Phaser/physics/AABB.ts +++ b/Phaser/physics/AABB.ts @@ -53,6 +53,9 @@ module Phaser.Physics { public preUpdate() { this.oldPosition.copyFrom(this.position); + + this.bounds.x = this.position.x - this.bounds.halfWidth; + this.bounds.y = this.position.y - this.bounds.halfHeight; if (this.sprite) { @@ -71,8 +74,8 @@ module Phaser.Physics { public update() { - this.bounds.x = this.position.x; - this.bounds.y = this.position.y; + //this.bounds.x = this.position.x; + //this.bounds.y = this.position.y; } @@ -85,6 +88,12 @@ module Phaser.Physics { public render(context:CanvasRenderingContext2D) { + //context.beginPath(); + //context.strokeStyle = 'rgb(255,255,0)'; + //context.strokeRect(this.bounds.x, this.bounds.y, this.bounds.width, this.bounds.height); + //context.stroke(); + //context.closePath(); + context.beginPath(); context.strokeStyle = 'rgb(0,255,0)'; context.strokeRect(this.position.x - this.bounds.halfWidth, this.position.y - this.bounds.halfHeight, this.bounds.width, this.bounds.height); @@ -95,7 +104,7 @@ module Phaser.Physics { context.fillStyle = 'rgb(0,255,0)'; context.fillRect(this.position.x, this.position.y, 2, 2); - if (this.oH == 1) + if (this.physics.touching == Phaser.Types.LEFT) { context.beginPath(); context.strokeStyle = 'rgb(255,0,0)'; @@ -104,7 +113,7 @@ module Phaser.Physics { context.stroke(); context.closePath(); } - else if (this.oH == -1) + else if (this.physics.touching == Phaser.Types.RIGHT) { context.beginPath(); context.strokeStyle = 'rgb(255,0,0)'; @@ -114,7 +123,7 @@ module Phaser.Physics { context.closePath(); } - if (this.oV == 1) + if (this.physics.touching == Phaser.Types.UP) { context.beginPath(); context.strokeStyle = 'rgb(255,0,0)'; @@ -123,7 +132,7 @@ module Phaser.Physics { context.stroke(); context.closePath(); } - else if (this.oV == -1) + else if (this.physics.touching == Phaser.Types.DOWN) { context.beginPath(); context.strokeStyle = 'rgb(255,0,0)'; diff --git a/Phaser/physics/Circle.ts b/Phaser/physics/Circle.ts new file mode 100644 index 00000000..11fc6f44 --- /dev/null +++ b/Phaser/physics/Circle.ts @@ -0,0 +1,143 @@ +/// +/// +/// +/// +/// + +/** +* Phaser - Physics - Circle +*/ + +module Phaser.Physics { + + export class Circle implements IPhysicsShape { + + constructor(game: Game, sprite: Sprite, x: number, y: number, diameter: number) { + + this.game = game; + this.world = game.world.physics; + + if (sprite !== null) + { + this.sprite = sprite; + this.scale = Vec2Utils.clone(this.sprite.scale); + } + else + { + this.sprite = null; + this.physics = null; + this.scale = new Vec2(1, 1); + } + + this.radius = diameter / 2; + this.bounds = new Rectangle(x + Math.round(diameter / 2), y + Math.round(diameter / 2), diameter, diameter); + this.position = new Vec2(x + this.bounds.halfWidth, y + this.bounds.halfHeight); + this.oldPosition = new Vec2(x + this.bounds.halfWidth, y + this.bounds.halfHeight); + this.offset = new Vec2(0, 0); + + } + + public game: Game; + public world: PhysicsManager; + public sprite: Sprite; + public physics: Phaser.Components.Physics; + + public position: Vec2; + public oldPosition: Vec2; + public offset: Vec2; + public scale: Vec2; + public bounds: Rectangle; + + public radius: number; + public oH: number; + public oV: number; + + public preUpdate() { + + this.oldPosition.copyFrom(this.position); + + if (this.sprite) + { + this.position.setTo((this.sprite.x + this.bounds.halfWidth) + this.offset.x, (this.sprite.y + this.bounds.halfHeight) + this.offset.y); + + // Update scale / dimensions + if (Vec2Utils.equals(this.scale, this.sprite.scale) == false) + { + this.scale.copyFrom(this.sprite.scale); + // needs to be radius based (+ square) + //this.bounds.width = this.sprite.width; + //this.bounds.height = this.sprite.height; + } + } + + } + + public update() { + + this.bounds.x = this.position.x; + this.bounds.y = this.position.y; + + } + + public setSize(width: number, height: number) { + + this.bounds.width = width; + this.bounds.height = height; + + } + + public render(context:CanvasRenderingContext2D) { + + context.beginPath(); + context.strokeStyle = 'rgb(0,255,0)'; + context.arc(this.position.x, this.position.y, this.radius, 0, Math.PI * 2); + context.stroke(); + context.closePath(); + + // center point + context.fillStyle = 'rgb(0,255,0)'; + context.fillRect(this.position.x, this.position.y, 2, 2); + + if (this.oH == 1) + { + context.beginPath(); + context.strokeStyle = 'rgb(255,0,0)'; + context.moveTo(this.position.x - this.bounds.halfWidth, this.position.y - this.bounds.halfHeight); + context.lineTo(this.position.x - this.bounds.halfWidth, this.position.y + this.bounds.halfHeight); + context.stroke(); + context.closePath(); + } + else if (this.oH == -1) + { + context.beginPath(); + context.strokeStyle = 'rgb(255,0,0)'; + context.moveTo(this.position.x + this.bounds.halfWidth, this.position.y - this.bounds.halfHeight); + context.lineTo(this.position.x + this.bounds.halfWidth, this.position.y + this.bounds.halfHeight); + context.stroke(); + context.closePath(); + } + + if (this.oV == 1) + { + context.beginPath(); + context.strokeStyle = 'rgb(255,0,0)'; + context.moveTo(this.position.x - this.bounds.halfWidth, this.position.y - this.bounds.halfHeight); + context.lineTo(this.position.x + this.bounds.halfWidth, this.position.y - this.bounds.halfHeight); + context.stroke(); + context.closePath(); + } + else if (this.oV == -1) + { + context.beginPath(); + context.strokeStyle = 'rgb(255,0,0)'; + context.moveTo(this.position.x - this.bounds.halfWidth, this.position.y + this.bounds.halfHeight); + context.lineTo(this.position.x + this.bounds.halfWidth, this.position.y + this.bounds.halfHeight); + context.stroke(); + context.closePath(); + } + + } + + } + +} \ No newline at end of file diff --git a/Phaser/physics/PhysicsManager.ts b/Phaser/physics/PhysicsManager.ts index a9e476f2..10c010e9 100644 --- a/Phaser/physics/PhysicsManager.ts +++ b/Phaser/physics/PhysicsManager.ts @@ -1,5 +1,6 @@ /// /// +/// /** * Phaser - PhysicsManager @@ -60,15 +61,44 @@ module Phaser.Physics { } + public remove(shape: IPhysicsShape) { + + this._length = this._objects.length; + + for (var i = 0; i < this._length; i++) + { + if (this._objects[i] === shape) + { + this._objects[i] = null; + } + } + + } + public update() { this._length = this._objects.length; for (var i = 0; i < this._length; i++) { - this._objects[i].preUpdate(); - this.updateMotion(this._objects[i]); - this.collideWorld(this._objects[i]); + if (this._objects[i]) + { + this._objects[i].preUpdate(); + this.updateMotion(this._objects[i]); + this.collideWorld(this._objects[i]); + + if (this._objects[i].physics.immovable == false) + { + for (var x = 0; x < this._length; x++) + { + if (this._objects[x] !== this._objects[i]) + { + this.collideShapes(this._objects[i], this._objects[x]); + } + } + } + + } } } @@ -78,14 +108,17 @@ module Phaser.Physics { // iterate through the objects here, updating and colliding for (var i = 0; i < this._length; i++) { - this._objects[i].render(this.game.stage.context); + if (this._objects[i]) + { + this._objects[i].render(this.game.stage.context); + } } } - private updateMotion(obj: IPhysicsShape) { + private updateMotion(shape: IPhysicsShape) { - if (obj.physics.moves == false) + if (shape.physics.moves == false) { return; } @@ -97,17 +130,17 @@ module Phaser.Physics { this.angularVelocity += velocityDelta; */ - this._velocityDelta = (this.computeVelocity(obj.physics.velocity.x, obj.physics.gravity.x, obj.physics.acceleration.x, obj.physics.drag.x) - obj.physics.velocity.x) / 2; - obj.physics.velocity.x += this._velocityDelta; - this._delta = obj.physics.velocity.x * this.game.time.elapsed; - obj.physics.velocity.x += this._velocityDelta; - obj.position.x += this._delta; + this._velocityDelta = (this.computeVelocity(shape.physics.velocity.x, shape.physics.gravity.x, shape.physics.acceleration.x, shape.physics.drag.x) - shape.physics.velocity.x) / 2; + shape.physics.velocity.x += this._velocityDelta; + this._delta = shape.physics.velocity.x * this.game.time.elapsed; + shape.physics.velocity.x += this._velocityDelta; + shape.position.x += this._delta; - this._velocityDelta = (this.computeVelocity(obj.physics.velocity.y, obj.physics.gravity.y, obj.physics.acceleration.y, obj.physics.drag.y) - obj.physics.velocity.y) / 2; - obj.physics.velocity.y += this._velocityDelta; - this._delta = obj.physics.velocity.y * this.game.time.elapsed; - obj.physics.velocity.y += this._velocityDelta; - obj.position.y += this._delta; + this._velocityDelta = (this.computeVelocity(shape.physics.velocity.y, shape.physics.gravity.y, shape.physics.acceleration.y, shape.physics.drag.y) - shape.physics.velocity.y) / 2; + shape.physics.velocity.y += this._velocityDelta; + this._delta = shape.physics.velocity.y * this.game.time.elapsed; + shape.physics.velocity.y += this._velocityDelta; + shape.position.y += this._delta; } @@ -163,6 +196,70 @@ module Phaser.Physics { } + private collideShapes(shapeA: IPhysicsShape, shapeB: IPhysicsShape) { + + if (shapeA.physics.immovable && shapeB.physics.immovable) + { + return; + } + + // Simple bounds check first + if (RectangleUtils.intersects(shapeA.bounds, shapeB.bounds)) + { + // Collide on the x-axis + if (shapeA.bounds.right >= shapeB.bounds.x && shapeA.bounds.right <= shapeB.bounds.right) + { + // The right side of ShapeA hit the left side of ShapeB + this._distance.x = shapeB.bounds.x - shapeA.bounds.right; + + if (this._distance.x != 0) + { + this._tangent.setTo(-1, 0); + this.separateX(shapeA, shapeB, this._distance, this._tangent); + } + } + else if (shapeA.bounds.x <= shapeB.bounds.right && shapeA.bounds.x >= shapeB.bounds.x) + { + // The left side of ShapeA hit the right side of ShapeB + this._distance.x = shapeB.bounds.right - shapeA.bounds.x; + + if (this._distance.x != 0) + { + this._tangent.setTo(1, 0); + this.separateX(shapeA, shapeB, this._distance, this._tangent); + } + } + + // Collide on the y-axis + if (shapeA.bounds.y <= shapeB.bounds.bottom && shapeA.bounds.y >= shapeB.bounds.y) + { + console.log(shapeA.bounds.y, shapeB.bounds.bottom, shapeB.bounds.y); + // The top of ShapeA hit the bottom of ShapeB + this._distance.y = shapeB.bounds.bottom - shapeA.bounds.y; + + if (this._distance.y != 0) + { + this._tangent.setTo(0, 1); + this.separateY(shapeA, shapeB, this._distance, this._tangent); + } + } + else if (shapeA.bounds.bottom >= shapeB.bounds.y && shapeA.bounds.bottom <= shapeB.bounds.bottom) + { + console.log(shapeA.bounds.bottom, shapeB.bounds.y, shapeB.bounds.bottom); + // The bottom of ShapeA hit the top of ShapeB + this._distance.y = shapeB.bounds.y - shapeA.bounds.bottom; + + if (this._distance.y != 0) + { + this._tangent.setTo(0, -1); + this.separateY(shapeA, shapeB, this._distance, this._tangent); + } + } + + } + + } + private collideWorld(shape:IPhysicsShape) { // Collide on the x-axis @@ -172,7 +269,7 @@ module Phaser.Physics { { // Hit Left this._tangent.setTo(1, 0); - this.separateX(shape, this._distance, this._tangent); + this.separateXWall(shape, this._distance, this._tangent); } else { @@ -183,7 +280,7 @@ module Phaser.Physics { // Hit Right this._tangent.setTo(-1, 0); this._distance.reverse(); - this.separateX(shape, this._distance, this._tangent); + this.separateXWall(shape, this._distance, this._tangent); } } @@ -194,7 +291,7 @@ module Phaser.Physics { { // Hit Top this._tangent.setTo(0, 1); - this.separateY(shape, this._distance, this._tangent); + this.separateYWall(shape, this._distance, this._tangent); } else { @@ -205,110 +302,151 @@ module Phaser.Physics { // Hit Bottom this._tangent.setTo(0, -1); this._distance.reverse(); - this.separateY(shape, this._distance, this._tangent); + this.separateYWall(shape, this._distance, this._tangent); } } } - /* - private OLDButWorkingcollideWorld(obj:IPhysicsShape) { + private separateX(shapeA: IPhysicsShape, shapeB: IPhysicsShape, distance: Vec2, tangent: Vec2) { - this._distance.setTo(0, 0); - - // Collide on the x-axis - this._distance.x = obj.world.bounds.x - (obj.position.x - obj.bounds.halfWidth); - - if (0 < this._distance.x) + if (tangent.x == 1) { - // Hit Left - // Parameter order: px, py (distance), dx, dy (tangent) - this._tangent.setTo(1, 0); - this.separate(obj, this._distance, this._tangent); + console.log('The left side of ShapeA hit the right side of ShapeB', distance.x); + shapeA.physics.touching |= Phaser.Types.LEFT; + shapeB.physics.touching |= Phaser.Types.RIGHT; } else { - this._distance.x = (obj.position.x + obj.bounds.halfWidth) - obj.world.bounds.right; + console.log('The right side of ShapeA hit the left side of ShapeB', distance.x); + shapeA.physics.touching |= Phaser.Types.RIGHT; + shapeB.physics.touching |= Phaser.Types.LEFT; + } - if (0 < this._distance.x) + // collision edges + shapeA.oH = tangent.x; + + // only apply collision response forces if the object is travelling into, and not out of, the collision + if (Vec2Utils.dot(shapeA.physics.velocity, tangent) < 0) + { + // Apply horizontal bounce + if (shapeA.physics.bounce.x > 0) { - // Hit Right - // Parameter order: px, py (distance), dx, dy (tangent) - this._tangent.setTo(-1, 0); - this._distance.x = -this._distance.x; - this.separate(obj, this._distance, this._tangent); + shapeA.physics.velocity.x *= -(shapeA.physics.bounce.x); + } + else + { + shapeA.physics.velocity.x = 0; } } - // Collide on the y-axis - this._distance.x = 0; - this._distance.y = obj.world.bounds.y - (obj.position.y - obj.bounds.halfHeight); + shapeA.position.x += distance.x; + shapeA.bounds.x += distance.x; - if (0 < this._distance.y) + } + + private separateY(shapeA: IPhysicsShape, shapeB: IPhysicsShape, distance: Vec2, tangent: Vec2) { + + if (tangent.y == 1) { - // Hit Top - this._tangent.setTo(0, 1); - this.separate(obj, this._distance, this._tangent); + console.log('The top of ShapeA hit the bottom of ShapeB', distance.y); + shapeA.physics.touching |= Phaser.Types.UP; + shapeB.physics.touching |= Phaser.Types.DOWN; } else { - this._distance.y = (obj.position.y + obj.bounds.halfHeight) - obj.world.bounds.bottom; - - if (0 < this._distance.y) - { - // Hit Bottom - this._tangent.setTo(0, -1); - this._distance.y = -this._distance.y; - this.separate(obj, this._distance, this._tangent); - } + console.log('The bottom of ShapeA hit the top of ShapeB', distance.y); + shapeA.physics.touching |= Phaser.Types.DOWN; + shapeB.physics.touching |= Phaser.Types.UP; } - } - */ - - private separateX(shape: IPhysicsShape, distance: Vec2, tangent: Vec2) { - // collision edges - shape.oH = tangent.x; + shapeA.oV = tangent.y; // only apply collision response forces if the object is travelling into, and not out of, the collision - if (Vec2Utils.dot(shape.physics.velocity, tangent) < 0) + if (Vec2Utils.dot(shapeA.physics.velocity, tangent) < 0) { // Apply horizontal bounce - if (shape.physics.bounce.x > 0) + if (shapeA.physics.bounce.y > 0) { - shape.physics.velocity.x *= -(shape.physics.bounce.x); + shapeA.physics.velocity.y *= -(shapeA.physics.bounce.y); } else { - shape.physics.velocity.x = 0; + shapeA.physics.velocity.y = 0; } } - shape.position.x += distance.x; + shapeA.position.y += distance.y; + shapeA.bounds.y += distance.y; } - private separateY(shape: IPhysicsShape, distance: Vec2, tangent: Vec2) { + private separateXWall(shapeA: IPhysicsShape, distance: Vec2, tangent: Vec2) { + + if (tangent.x == 1) + { + console.log('The left side of ShapeA hit the right side of ShapeB', distance.x); + shapeA.physics.touching |= Phaser.Types.LEFT; + } + else + { + console.log('The right side of ShapeA hit the left side of ShapeB', distance.x); + shapeA.physics.touching |= Phaser.Types.RIGHT; + } // collision edges - shape.oV = tangent.y; + shapeA.oH = tangent.x; // only apply collision response forces if the object is travelling into, and not out of, the collision - if (Vec2Utils.dot(shape.physics.velocity, tangent) < 0) + if (Vec2Utils.dot(shapeA.physics.velocity, tangent) < 0) { // Apply horizontal bounce - if (shape.physics.bounce.y > 0) + if (shapeA.physics.bounce.x > 0) { - shape.physics.velocity.y *= -(shape.physics.bounce.y); + shapeA.physics.velocity.x *= -(shapeA.physics.bounce.x); } else { - shape.physics.velocity.y = 0; + shapeA.physics.velocity.x = 0; } } - shape.position.y += distance.y; + shapeA.position.x += distance.x; + + } + + private separateYWall(shapeA: IPhysicsShape, distance: Vec2, tangent: Vec2) { + + if (tangent.y == 1) + { + console.log('The top of ShapeA hit the bottom of ShapeB', distance.y); + shapeA.physics.touching |= Phaser.Types.UP; + } + else + { + console.log('The bottom of ShapeA hit the top of ShapeB', distance.y); + shapeA.physics.touching |= Phaser.Types.DOWN; + } + + // collision edges + shapeA.oV = tangent.y; + + // only apply collision response forces if the object is travelling into, and not out of, the collision + if (Vec2Utils.dot(shapeA.physics.velocity, tangent) < 0) + { + // Apply horizontal bounce + if (shapeA.physics.bounce.y > 0) + { + shapeA.physics.velocity.y *= -(shapeA.physics.bounce.y); + } + else + { + shapeA.physics.velocity.y = 0; + } + } + + shapeA.position.y += distance.y; } @@ -373,6 +511,47 @@ module Phaser.Physics { } + /** + * Checks for overlaps between two objects using the world QuadTree. Can be GameObject vs. GameObject, GameObject vs. Group or Group vs. Group. + * Note: Does not take the objects scrollFactor into account. All overlaps are check in world space. + * @param object1 The first GameObject or Group to check. If null the world.group is used. + * @param object2 The second GameObject or Group to check. + * @param notifyCallback A 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 passed them to Collision.overlap. + * @param processCallback A callback function that lets you perform additional checks against the two objects if they overlap. If this is set then notifyCallback will only be called if processCallback returns true. + * @param context The context in which the callbacks will be called + * @returns {boolean} true if the objects overlap, otherwise false. + */ + /* + public overlap(object1: Basic = null, object2: Basic = null, notifyCallback = null, processCallback = null, context = null): bool { + + if (object1 == null) + { + object1 = this._game.world.group; + } + + if (object2 == object1) + { + object2 = null; + } + + QuadTree.divisions = this._game.world.worldDivisions; + + var quadTree: QuadTree = new QuadTree(this._game.world.bounds.x, this._game.world.bounds.y, this._game.world.bounds.width, this._game.world.bounds.height); + + quadTree.load(object1, object2, notifyCallback, processCallback, context); + + var result: bool = quadTree.execute(); + + quadTree.destroy(); + + quadTree = null; + + return result; + + } + */ + + } } \ No newline at end of file diff --git a/Phaser/utils/RectangleUtils.ts b/Phaser/utils/RectangleUtils.ts index e8db1ffb..49e581d8 100644 --- a/Phaser/utils/RectangleUtils.ts +++ b/Phaser/utils/RectangleUtils.ts @@ -176,18 +176,7 @@ module Phaser { * @return {Boolean} A value of true if the specified object intersects with this Rectangle object; otherwise false. **/ static intersects(a: Rectangle, b: Rectangle, tolerance?: number = 0): bool { - - // ? - //return (a.right > b.x) && (a.x < b.right) && (a.bottom > b.y) && (a.y < b.bottom); return !(a.left > b.right + tolerance || a.right < b.left - tolerance || a.top > b.bottom + tolerance || a.bottom < b.top - tolerance); - - //if (a.x >= b.right || a.right <= b.x || a.bottom <= b.y || a.y >= b.bottom) - //{ - // return false; - //} - - //return true; - } /** diff --git a/Tests/Tests.csproj b/Tests/Tests.csproj index 24d352bf..ed58efb4 100644 --- a/Tests/Tests.csproj +++ b/Tests/Tests.csproj @@ -74,6 +74,14 @@ + + + + aabb vs aabb 1.ts + + + circle 1.ts + ballscroller.ts diff --git a/Tests/phaser.js b/Tests/phaser.js index 365aa70d..a414e491 100644 --- a/Tests/phaser.js +++ b/Tests/phaser.js @@ -2958,15 +2958,8 @@ var Phaser; **/ function intersects(a, b, tolerance) { if (typeof tolerance === "undefined") { tolerance = 0; } - // ? - //return (a.right > b.x) && (a.x < b.right) && (a.bottom > b.y) && (a.y < b.bottom); return !(a.left > b.right + tolerance || a.right < b.left - tolerance || a.top > b.bottom + tolerance || a.bottom < b.top - tolerance); - //if (a.x >= b.right || a.right <= b.x || a.bottom <= b.y || a.y >= b.bottom) - //{ - // return false; - //} - //return true; - }; + }; RectangleUtils.intersectsRaw = /** * Determines whether the object specified intersects (overlaps) with the given values. * @method intersectsRaw @@ -3262,7 +3255,6 @@ var Phaser; if (typeof diameter === "undefined") { diameter = 0; } this._diameter = 0; this._radius = 0; - //private _pos: Vec2; /** * The x coordinate of the center of the circle * @property x @@ -5858,6 +5850,7 @@ var Phaser; (function (Phaser) { /// /// + /// /** * Phaser - PhysicsManager * @@ -5883,22 +5876,41 @@ var Phaser; this._objects.push(shape); return shape; }; + PhysicsManager.prototype.remove = function (shape) { + this._length = this._objects.length; + for(var i = 0; i < this._length; i++) { + if(this._objects[i] === shape) { + this._objects[i] = null; + } + } + }; PhysicsManager.prototype.update = function () { this._length = this._objects.length; for(var i = 0; i < this._length; i++) { - this._objects[i].preUpdate(); - this.updateMotion(this._objects[i]); - this.collideWorld(this._objects[i]); + if(this._objects[i]) { + this._objects[i].preUpdate(); + this.updateMotion(this._objects[i]); + this.collideWorld(this._objects[i]); + if(this._objects[i].physics.immovable == false) { + for(var x = 0; x < this._length; x++) { + if(this._objects[x] !== this._objects[i]) { + this.collideShapes(this._objects[i], this._objects[x]); + } + } + } + } } }; PhysicsManager.prototype.render = function () { // iterate through the objects here, updating and colliding for(var i = 0; i < this._length; i++) { - this._objects[i].render(this.game.stage.context); + if(this._objects[i]) { + this._objects[i].render(this.game.stage.context); + } } }; - PhysicsManager.prototype.updateMotion = function (obj) { - if(obj.physics.moves == false) { + PhysicsManager.prototype.updateMotion = function (shape) { + if(shape.physics.moves == false) { return; } /* @@ -5907,16 +5919,16 @@ var Phaser; this._angle += this.angularVelocity * this._game.time.elapsed; this.angularVelocity += velocityDelta; */ - this._velocityDelta = (this.computeVelocity(obj.physics.velocity.x, obj.physics.gravity.x, obj.physics.acceleration.x, obj.physics.drag.x) - obj.physics.velocity.x) / 2; - obj.physics.velocity.x += this._velocityDelta; - this._delta = obj.physics.velocity.x * this.game.time.elapsed; - obj.physics.velocity.x += this._velocityDelta; - obj.position.x += this._delta; - this._velocityDelta = (this.computeVelocity(obj.physics.velocity.y, obj.physics.gravity.y, obj.physics.acceleration.y, obj.physics.drag.y) - obj.physics.velocity.y) / 2; - obj.physics.velocity.y += this._velocityDelta; - this._delta = obj.physics.velocity.y * this.game.time.elapsed; - obj.physics.velocity.y += this._velocityDelta; - obj.position.y += this._delta; + this._velocityDelta = (this.computeVelocity(shape.physics.velocity.x, shape.physics.gravity.x, shape.physics.acceleration.x, shape.physics.drag.x) - shape.physics.velocity.x) / 2; + shape.physics.velocity.x += this._velocityDelta; + this._delta = shape.physics.velocity.x * this.game.time.elapsed; + shape.physics.velocity.x += this._velocityDelta; + shape.position.x += this._delta; + this._velocityDelta = (this.computeVelocity(shape.physics.velocity.y, shape.physics.gravity.y, shape.physics.acceleration.y, shape.physics.drag.y) - shape.physics.velocity.y) / 2; + shape.physics.velocity.y += this._velocityDelta; + this._delta = shape.physics.velocity.y * this.game.time.elapsed; + shape.physics.velocity.y += this._velocityDelta; + shape.position.y += this._delta; }; PhysicsManager.prototype.computeVelocity = /** * A tween-like function that takes a starting velocity and some other factors and returns an altered velocity. @@ -5955,119 +5967,169 @@ var Phaser; } return velocity; }; - PhysicsManager.prototype.collideWorld = function (obj) { + PhysicsManager.prototype.collideShapes = function (shapeA, shapeB) { + if(shapeA.physics.immovable && shapeB.physics.immovable) { + return; + } + // Simple bounds check first + if(Phaser.RectangleUtils.intersects(shapeA.bounds, shapeB.bounds)) { + // Collide on the x-axis + if(shapeA.bounds.right >= shapeB.bounds.x && shapeA.bounds.right <= shapeB.bounds.right) { + // The right side of ShapeA hit the left side of ShapeB + this._distance.x = shapeB.bounds.x - shapeA.bounds.right; + if(this._distance.x != 0) { + this._tangent.setTo(-1, 0); + this.separateX(shapeA, shapeB, this._distance, this._tangent); + } + } else if(shapeA.bounds.x <= shapeB.bounds.right && shapeA.bounds.x >= shapeB.bounds.x) { + // The left side of ShapeA hit the right side of ShapeB + this._distance.x = shapeB.bounds.right - shapeA.bounds.x; + if(this._distance.x != 0) { + this._tangent.setTo(1, 0); + this.separateX(shapeA, shapeB, this._distance, this._tangent); + } + } + // Collide on the y-axis + if(shapeA.bounds.y <= shapeB.bounds.bottom && shapeA.bounds.y >= shapeB.bounds.y) { + console.log(shapeA.bounds.y, shapeB.bounds.bottom, shapeB.bounds.y); + // The top of ShapeA hit the bottom of ShapeB + this._distance.y = shapeB.bounds.bottom - shapeA.bounds.y; + if(this._distance.y != 0) { + this._tangent.setTo(0, 1); + this.separateY(shapeA, shapeB, this._distance, this._tangent); + } + } else if(shapeA.bounds.bottom >= shapeB.bounds.y && shapeA.bounds.bottom <= shapeB.bounds.bottom) { + console.log(shapeA.bounds.bottom, shapeB.bounds.y, shapeB.bounds.bottom); + // The bottom of ShapeA hit the top of ShapeB + this._distance.y = shapeB.bounds.y - shapeA.bounds.bottom; + if(this._distance.y != 0) { + this._tangent.setTo(0, -1); + this.separateY(shapeA, shapeB, this._distance, this._tangent); + } + } + } + }; + PhysicsManager.prototype.collideWorld = function (shape) { // Collide on the x-axis - this._distance.x = obj.world.bounds.x - (obj.position.x - obj.bounds.halfWidth); + this._distance.x = shape.world.bounds.x - (shape.position.x - shape.bounds.halfWidth); if(0 < this._distance.x) { // Hit Left this._tangent.setTo(1, 0); - this.separateX(obj, this._distance, this._tangent); + this.separateXWall(shape, this._distance, this._tangent); } else { - this._distance.x = (obj.position.x + obj.bounds.halfWidth) - obj.world.bounds.right; + this._distance.x = (shape.position.x + shape.bounds.halfWidth) - shape.world.bounds.right; if(0 < this._distance.x) { // Hit Right this._tangent.setTo(-1, 0); this._distance.reverse(); - this.separateX(obj, this._distance, this._tangent); + this.separateXWall(shape, this._distance, this._tangent); } } // Collide on the y-axis - this._distance.y = obj.world.bounds.y - (obj.position.y - obj.bounds.halfHeight); + this._distance.y = shape.world.bounds.y - (shape.position.y - shape.bounds.halfHeight); if(0 < this._distance.y) { // Hit Top this._tangent.setTo(0, 1); - this.separateY(obj, this._distance, this._tangent); + this.separateYWall(shape, this._distance, this._tangent); } else { - this._distance.y = (obj.position.y + obj.bounds.halfHeight) - obj.world.bounds.bottom; + this._distance.y = (shape.position.y + shape.bounds.halfHeight) - shape.world.bounds.bottom; if(0 < this._distance.y) { // Hit Bottom this._tangent.setTo(0, -1); this._distance.reverse(); - this.separateY(obj, this._distance, this._tangent); + this.separateYWall(shape, this._distance, this._tangent); } } }; - PhysicsManager.prototype.separateX = /* - private OLDButWorkingcollideWorld(obj:IPhysicsShape) { - - this._distance.setTo(0, 0); - - // Collide on the x-axis - this._distance.x = obj.world.bounds.x - (obj.position.x - obj.bounds.halfWidth); - - if (0 < this._distance.x) - { - // Hit Left - // Parameter order: px, py (distance), dx, dy (tangent) - this._tangent.setTo(1, 0); - this.separate(obj, this._distance, this._tangent); - } - else - { - this._distance.x = (obj.position.x + obj.bounds.halfWidth) - obj.world.bounds.right; - - if (0 < this._distance.x) - { - // Hit Right - // Parameter order: px, py (distance), dx, dy (tangent) - this._tangent.setTo(-1, 0); - this._distance.x = -this._distance.x; - this.separate(obj, this._distance, this._tangent); - } - } - - // Collide on the y-axis - this._distance.x = 0; - this._distance.y = obj.world.bounds.y - (obj.position.y - obj.bounds.halfHeight); - - if (0 < this._distance.y) - { - // Hit Top - this._tangent.setTo(0, 1); - this.separate(obj, this._distance, this._tangent); - } - else - { - this._distance.y = (obj.position.y + obj.bounds.halfHeight) - obj.world.bounds.bottom; - - if (0 < this._distance.y) - { - // Hit Bottom - this._tangent.setTo(0, -1); - this._distance.y = -this._distance.y; - this.separate(obj, this._distance, this._tangent); - } - } - - } - */ - function (shape, distance, tangent) { + PhysicsManager.prototype.separateX = function (shapeA, shapeB, distance, tangent) { + if(tangent.x == 1) { + console.log('The left side of ShapeA hit the right side of ShapeB', distance.x); + shapeA.physics.touching |= Phaser.Types.LEFT; + shapeB.physics.touching |= Phaser.Types.RIGHT; + } else { + console.log('The right side of ShapeA hit the left side of ShapeB', distance.x); + shapeA.physics.touching |= Phaser.Types.RIGHT; + shapeB.physics.touching |= Phaser.Types.LEFT; + } // collision edges - shape.oH = tangent.x; + shapeA.oH = tangent.x; // only apply collision response forces if the object is travelling into, and not out of, the collision - if(Phaser.Vec2Utils.dot(shape.physics.velocity, tangent) < 0) { + if(Phaser.Vec2Utils.dot(shapeA.physics.velocity, tangent) < 0) { // Apply horizontal bounce - if(shape.physics.bounce.x > 0) { - shape.physics.velocity.x *= -(shape.physics.bounce.x); + if(shapeA.physics.bounce.x > 0) { + shapeA.physics.velocity.x *= -(shapeA.physics.bounce.x); } else { - shape.physics.velocity.x = 0; + shapeA.physics.velocity.x = 0; } } - shape.position.x += distance.x; + shapeA.position.x += distance.x; + shapeA.bounds.x += distance.x; }; - PhysicsManager.prototype.separateY = function (shape, distance, tangent) { + PhysicsManager.prototype.separateY = function (shapeA, shapeB, distance, tangent) { + if(tangent.y == 1) { + console.log('The top of ShapeA hit the bottom of ShapeB', distance.y); + shapeA.physics.touching |= Phaser.Types.UP; + shapeB.physics.touching |= Phaser.Types.DOWN; + } else { + console.log('The bottom of ShapeA hit the top of ShapeB', distance.y); + shapeA.physics.touching |= Phaser.Types.DOWN; + shapeB.physics.touching |= Phaser.Types.UP; + } // collision edges - shape.oV = tangent.y; + shapeA.oV = tangent.y; // only apply collision response forces if the object is travelling into, and not out of, the collision - if(Phaser.Vec2Utils.dot(shape.physics.velocity, tangent) < 0) { + if(Phaser.Vec2Utils.dot(shapeA.physics.velocity, tangent) < 0) { // Apply horizontal bounce - if(shape.physics.bounce.y > 0) { - shape.physics.velocity.y *= -(shape.physics.bounce.y); + if(shapeA.physics.bounce.y > 0) { + shapeA.physics.velocity.y *= -(shapeA.physics.bounce.y); } else { - shape.physics.velocity.y = 0; + shapeA.physics.velocity.y = 0; } } - shape.position.y += distance.y; + shapeA.position.y += distance.y; + shapeA.bounds.y += distance.y; + }; + PhysicsManager.prototype.separateXWall = function (shapeA, distance, tangent) { + if(tangent.x == 1) { + console.log('The left side of ShapeA hit the right side of ShapeB', distance.x); + shapeA.physics.touching |= Phaser.Types.LEFT; + } else { + console.log('The right side of ShapeA hit the left side of ShapeB', distance.x); + shapeA.physics.touching |= Phaser.Types.RIGHT; + } + // collision edges + shapeA.oH = tangent.x; + // only apply collision response forces if the object is travelling into, and not out of, the collision + if(Phaser.Vec2Utils.dot(shapeA.physics.velocity, tangent) < 0) { + // Apply horizontal bounce + if(shapeA.physics.bounce.x > 0) { + shapeA.physics.velocity.x *= -(shapeA.physics.bounce.x); + } else { + shapeA.physics.velocity.x = 0; + } + } + shapeA.position.x += distance.x; + }; + PhysicsManager.prototype.separateYWall = function (shapeA, distance, tangent) { + if(tangent.y == 1) { + console.log('The top of ShapeA hit the bottom of ShapeB', distance.y); + shapeA.physics.touching |= Phaser.Types.UP; + } else { + console.log('The bottom of ShapeA hit the top of ShapeB', distance.y); + shapeA.physics.touching |= Phaser.Types.DOWN; + } + // collision edges + shapeA.oV = tangent.y; + // only apply collision response forces if the object is travelling into, and not out of, the collision + if(Phaser.Vec2Utils.dot(shapeA.physics.velocity, tangent) < 0) { + // Apply horizontal bounce + if(shapeA.physics.bounce.y > 0) { + shapeA.physics.velocity.y *= -(shapeA.physics.bounce.y); + } else { + shapeA.physics.velocity.y = 0; + } + } + shapeA.position.y += distance.y; }; PhysicsManager.prototype.separate = function (shape, distance, tangent) { // collision edges @@ -6111,7 +6173,46 @@ var Phaser; return PhysicsManager; })(); Physics.PhysicsManager = PhysicsManager; - })(Phaser.Physics || (Phaser.Physics = {})); + /** + * Checks for overlaps between two objects using the world QuadTree. Can be GameObject vs. GameObject, GameObject vs. Group or Group vs. Group. + * Note: Does not take the objects scrollFactor into account. All overlaps are check in world space. + * @param object1 The first GameObject or Group to check. If null the world.group is used. + * @param object2 The second GameObject or Group to check. + * @param notifyCallback A 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 passed them to Collision.overlap. + * @param processCallback A callback function that lets you perform additional checks against the two objects if they overlap. If this is set then notifyCallback will only be called if processCallback returns true. + * @param context The context in which the callbacks will be called + * @returns {boolean} true if the objects overlap, otherwise false. + */ + /* + public overlap(object1: Basic = null, object2: Basic = null, notifyCallback = null, processCallback = null, context = null): bool { + + if (object1 == null) + { + object1 = this._game.world.group; + } + + if (object2 == object1) + { + object2 = null; + } + + QuadTree.divisions = this._game.world.worldDivisions; + + var quadTree: QuadTree = new QuadTree(this._game.world.bounds.x, this._game.world.bounds.y, this._game.world.bounds.width, this._game.world.bounds.height); + + quadTree.load(object1, object2, notifyCallback, processCallback, context); + + var result: bool = quadTree.execute(); + + quadTree.destroy(); + + quadTree = null; + + return result; + + } + */ + })(Phaser.Physics || (Phaser.Physics = {})); var Physics = Phaser.Physics; })(Phaser || (Phaser = {})); var Phaser; @@ -6144,6 +6245,8 @@ var Phaser; } AABB.prototype.preUpdate = function () { this.oldPosition.copyFrom(this.position); + this.bounds.x = this.position.x - this.bounds.halfWidth; + this.bounds.y = this.position.y - this.bounds.halfHeight; if(this.sprite) { this.position.setTo((this.sprite.x + this.bounds.halfWidth) + this.offset.x, (this.sprite.y + this.bounds.halfHeight) + this.offset.y); // Update scale / dimensions @@ -6155,14 +6258,19 @@ var Phaser; } }; AABB.prototype.update = function () { - this.bounds.x = this.position.x; - this.bounds.y = this.position.y; - }; + //this.bounds.x = this.position.x; + //this.bounds.y = this.position.y; + }; AABB.prototype.setSize = function (width, height) { this.bounds.width = width; this.bounds.height = height; }; AABB.prototype.render = function (context) { + //context.beginPath(); + //context.strokeStyle = 'rgb(255,255,0)'; + //context.strokeRect(this.bounds.x, this.bounds.y, this.bounds.width, this.bounds.height); + //context.stroke(); + //context.closePath(); context.beginPath(); context.strokeStyle = 'rgb(0,255,0)'; context.strokeRect(this.position.x - this.bounds.halfWidth, this.position.y - this.bounds.halfHeight, this.bounds.width, this.bounds.height); @@ -6171,14 +6279,14 @@ var Phaser; // center point context.fillStyle = 'rgb(0,255,0)'; context.fillRect(this.position.x, this.position.y, 2, 2); - if(this.oH == 1) { + if(this.physics.touching == Phaser.Types.LEFT) { context.beginPath(); context.strokeStyle = 'rgb(255,0,0)'; context.moveTo(this.position.x - this.bounds.halfWidth, this.position.y - this.bounds.halfHeight); context.lineTo(this.position.x - this.bounds.halfWidth, this.position.y + this.bounds.halfHeight); context.stroke(); context.closePath(); - } else if(this.oH == -1) { + } else if(this.physics.touching == Phaser.Types.RIGHT) { context.beginPath(); context.strokeStyle = 'rgb(255,0,0)'; context.moveTo(this.position.x + this.bounds.halfWidth, this.position.y - this.bounds.halfHeight); @@ -6186,14 +6294,14 @@ var Phaser; context.stroke(); context.closePath(); } - if(this.oV == 1) { + if(this.physics.touching == Phaser.Types.UP) { context.beginPath(); context.strokeStyle = 'rgb(255,0,0)'; context.moveTo(this.position.x - this.bounds.halfWidth, this.position.y - this.bounds.halfHeight); context.lineTo(this.position.x + this.bounds.halfWidth, this.position.y - this.bounds.halfHeight); context.stroke(); context.closePath(); - } else if(this.oV == -1) { + } else if(this.physics.touching == Phaser.Types.DOWN) { context.beginPath(); context.strokeStyle = 'rgb(255,0,0)'; context.moveTo(this.position.x - this.bounds.halfWidth, this.position.y + this.bounds.halfHeight); @@ -6439,6 +6547,15 @@ var Phaser; Types.GEOM_RECTANGLE = 2; Types.GEOM_LINE = 3; Types.GEOM_POLYGON = 4; + Types.LEFT = 0x0001; + Types.RIGHT = 0x0010; + Types.UP = 0x0100; + Types.DOWN = 0x1000; + Types.NONE = 0; + Types.CEILING = Types.UP; + Types.FLOOR = Types.DOWN; + Types.WALL = Types.LEFT | Types.RIGHT; + Types.ANY = Types.LEFT | Types.RIGHT | Types.UP | Types.DOWN; return Types; })(); Phaser.Types = Types; @@ -12132,17 +12249,120 @@ var Phaser; Phaser.Vec2 = Vec2; })(Phaser || (Phaser = {})); var Phaser; +(function (Phaser) { + /// + /// + /// + /// + /// + /** + * Phaser - Physics - Circle + */ + (function (Physics) { + var Circle = (function () { + function Circle(game, sprite, x, y, diameter) { + this.game = game; + this.world = game.world.physics; + if(sprite !== null) { + this.sprite = sprite; + this.scale = Phaser.Vec2Utils.clone(this.sprite.scale); + } else { + this.sprite = null; + this.physics = null; + this.scale = new Phaser.Vec2(1, 1); + } + this.radius = diameter / 2; + this.bounds = new Phaser.Rectangle(x + Math.round(diameter / 2), y + Math.round(diameter / 2), diameter, diameter); + this.position = new Phaser.Vec2(x + this.bounds.halfWidth, y + this.bounds.halfHeight); + this.oldPosition = new Phaser.Vec2(x + this.bounds.halfWidth, y + this.bounds.halfHeight); + this.offset = new Phaser.Vec2(0, 0); + } + Circle.prototype.preUpdate = function () { + this.oldPosition.copyFrom(this.position); + if(this.sprite) { + this.position.setTo((this.sprite.x + this.bounds.halfWidth) + this.offset.x, (this.sprite.y + this.bounds.halfHeight) + this.offset.y); + // Update scale / dimensions + if(Phaser.Vec2Utils.equals(this.scale, this.sprite.scale) == false) { + this.scale.copyFrom(this.sprite.scale); + // needs to be radius based (+ square) + //this.bounds.width = this.sprite.width; + //this.bounds.height = this.sprite.height; + } + } + }; + Circle.prototype.update = function () { + this.bounds.x = this.position.x; + this.bounds.y = this.position.y; + }; + Circle.prototype.setSize = function (width, height) { + this.bounds.width = width; + this.bounds.height = height; + }; + Circle.prototype.render = function (context) { + context.beginPath(); + context.strokeStyle = 'rgb(0,255,0)'; + context.arc(this.position.x, this.position.y, this.radius, 0, Math.PI * 2); + context.stroke(); + context.closePath(); + // center point + context.fillStyle = 'rgb(0,255,0)'; + context.fillRect(this.position.x, this.position.y, 2, 2); + if(this.oH == 1) { + context.beginPath(); + context.strokeStyle = 'rgb(255,0,0)'; + context.moveTo(this.position.x - this.bounds.halfWidth, this.position.y - this.bounds.halfHeight); + context.lineTo(this.position.x - this.bounds.halfWidth, this.position.y + this.bounds.halfHeight); + context.stroke(); + context.closePath(); + } else if(this.oH == -1) { + context.beginPath(); + context.strokeStyle = 'rgb(255,0,0)'; + context.moveTo(this.position.x + this.bounds.halfWidth, this.position.y - this.bounds.halfHeight); + context.lineTo(this.position.x + this.bounds.halfWidth, this.position.y + this.bounds.halfHeight); + context.stroke(); + context.closePath(); + } + if(this.oV == 1) { + context.beginPath(); + context.strokeStyle = 'rgb(255,0,0)'; + context.moveTo(this.position.x - this.bounds.halfWidth, this.position.y - this.bounds.halfHeight); + context.lineTo(this.position.x + this.bounds.halfWidth, this.position.y - this.bounds.halfHeight); + context.stroke(); + context.closePath(); + } else if(this.oV == -1) { + context.beginPath(); + context.strokeStyle = 'rgb(255,0,0)'; + context.moveTo(this.position.x - this.bounds.halfWidth, this.position.y + this.bounds.halfHeight); + context.lineTo(this.position.x + this.bounds.halfWidth, this.position.y + this.bounds.halfHeight); + context.stroke(); + context.closePath(); + } + }; + return Circle; + })(); + Physics.Circle = Circle; + })(Phaser.Physics || (Phaser.Physics = {})); + var Physics = Phaser.Physics; +})(Phaser || (Phaser = {})); +var Phaser; (function (Phaser) { /// /// /// /// + /// + /// /** * Phaser - Components - Physics */ (function (Components) { var Physics = (function () { function Physics(parent) { + /** + * Whether this object will be moved by impacts with other objects or not. + * @type {boolean} + */ + this.immovable = false; /** * Set this to false if you want to skip the automatic movement stuff * @type {boolean} @@ -12156,13 +12376,19 @@ var Phaser; this.friction = Phaser.Vec2Utils.clone(this.game.world.physics.friction); this.velocity = new Phaser.Vec2(); this.acceleration = new Phaser.Vec2(); + this.touching = Phaser.Types.NONE; this.shape = this.game.world.physics.add(new Phaser.Physics.AABB(this.game, this._sprite, this._sprite.x, this._sprite.y, this._sprite.width, this._sprite.height)); } + Physics.prototype.setCircle = function (diameter) { + this.game.world.physics.remove(this.shape); + this.shape = this.game.world.physics.add(new Phaser.Physics.Circle(this.game, this._sprite, this._sprite.x, this._sprite.y, diameter)); + this._sprite.physics.shape.physics = this; + }; Physics.prototype.update = /** * Internal function for updating the position and speed of this object. */ function () { - if(this.moves) { + if(this.moves && this.shape) { this._sprite.x = (this.shape.position.x - this.shape.bounds.halfWidth) - this.shape.offset.x; this._sprite.y = (this.shape.position.y - this.shape.bounds.halfHeight) - this.shape.offset.y; } @@ -12177,7 +12403,8 @@ var Phaser; if (typeof color === "undefined") { color = 'rgb(255,255,255)'; } this._sprite.texture.context.fillStyle = color; this._sprite.texture.context.fillText('Sprite: (' + this._sprite.frameBounds.width + ' x ' + this._sprite.frameBounds.height + ')', x, y); - this._sprite.texture.context.fillText('x: ' + this._sprite.frameBounds.x.toFixed(1) + ' y: ' + this._sprite.frameBounds.y.toFixed(1) + ' rotation: ' + this._sprite.rotation.toFixed(1), x, y + 14); + //this._sprite.texture.context.fillText('x: ' + this._sprite.frameBounds.x.toFixed(1) + ' y: ' + this._sprite.frameBounds.y.toFixed(1) + ' rotation: ' + this._sprite.rotation.toFixed(1), x, y + 14); + this._sprite.texture.context.fillText('x: ' + this.shape.bounds.x.toFixed(1) + ' y: ' + this.shape.bounds.y.toFixed(1) + ' rotation: ' + this._sprite.rotation.toFixed(1), x, y + 14); this._sprite.texture.context.fillText('vx: ' + this.velocity.x.toFixed(1) + ' vy: ' + this.velocity.y.toFixed(1), x, y + 28); this._sprite.texture.context.fillText('ax: ' + this.acceleration.x.toFixed(1) + ' ay: ' + this.acceleration.y.toFixed(1), x, y + 42); }; diff --git a/Tests/physics/aabb vs aabb 1.js b/Tests/physics/aabb vs aabb 1.js new file mode 100644 index 00000000..10a698ff --- /dev/null +++ b/Tests/physics/aabb vs aabb 1.js @@ -0,0 +1,47 @@ +/// +(function () { + var game = new Phaser.Game(this, 'game', 800, 600, init, create, update, render); + function init() { + // Using Phasers asset loader we load up a PNG from the assets folder + game.loader.addImageFile('atari', 'assets/sprites/atari800xl.png'); + game.loader.addImageFile('card', 'assets/sprites/mana_card.png'); + game.loader.load(); + } + var atari; + var card; + function create() { + //atari = game.add.sprite(350, 100, 'atari'); + //atari = game.add.sprite(350, 500, 'atari'); + atari = game.add.sprite(0, 310, 'atari'); + card = game.add.sprite(400, 300, 'card'); + card.physics.immovable = true; + //atari.texture.alpha = 0.5; + //atari.scale.setTo(1.5, 1.5); + //atari.physics.shape.setSize(150, 50); + //atari.physics.shape.offset.setTo(50, 25); + //atari.physics.gravity.setTo(0, 2); + atari.physics.bounce.setTo(0.7, 0.7); + //atari.physics.drag.setTo(10, 10); + //card.physics.bounce.setTo(0.7, 0.7); + //card.physics.velocity.x = -50; + } + function update() { + atari.physics.acceleration.x = 0; + atari.physics.acceleration.y = 0; + if(game.input.keyboard.isDown(Phaser.Keyboard.LEFT)) { + atari.physics.acceleration.x = -150; + } else if(game.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) { + atari.physics.acceleration.x = 150; + } + if(game.input.keyboard.isDown(Phaser.Keyboard.UP)) { + atari.physics.acceleration.y = -150; + } else if(game.input.keyboard.isDown(Phaser.Keyboard.DOWN)) { + atari.physics.acceleration.y = 150; + } + // collide? + } + function render() { + atari.physics.renderDebugInfo(16, 16); + card.physics.renderDebugInfo(200, 16); + } +})(); diff --git a/Tests/physics/aabb vs aabb 1.ts b/Tests/physics/aabb vs aabb 1.ts new file mode 100644 index 00000000..68cf8e3e --- /dev/null +++ b/Tests/physics/aabb vs aabb 1.ts @@ -0,0 +1,77 @@ +/// + +(function () { + + var game = new Phaser.Game(this, 'game', 800, 600, init, create, update, render); + + function init() { + + // Using Phasers asset loader we load up a PNG from the assets folder + game.loader.addImageFile('atari', 'assets/sprites/atari800xl.png'); + game.loader.addImageFile('card', 'assets/sprites/mana_card.png'); + game.loader.load(); + + } + + var atari: Phaser.Sprite; + var card: Phaser.Sprite; + + function create() { + + //atari = game.add.sprite(350, 100, 'atari'); + //atari = game.add.sprite(350, 500, 'atari'); + atari = game.add.sprite(0, 310, 'atari'); + card = game.add.sprite(400, 300, 'card'); + + card.physics.immovable = true; + + //atari.texture.alpha = 0.5; + //atari.scale.setTo(1.5, 1.5); + + //atari.physics.shape.setSize(150, 50); + //atari.physics.shape.offset.setTo(50, 25); + + //atari.physics.gravity.setTo(0, 2); + atari.physics.bounce.setTo(0.7, 0.7); + //atari.physics.drag.setTo(10, 10); + + //card.physics.bounce.setTo(0.7, 0.7); + //card.physics.velocity.x = -50; + + } + + function update() { + + atari.physics.acceleration.x = 0; + atari.physics.acceleration.y = 0; + + if (game.input.keyboard.isDown(Phaser.Keyboard.LEFT)) + { + atari.physics.acceleration.x = -150; + } + else if (game.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) + { + atari.physics.acceleration.x = 150; + } + + if (game.input.keyboard.isDown(Phaser.Keyboard.UP)) + { + atari.physics.acceleration.y = -150; + } + else if (game.input.keyboard.isDown(Phaser.Keyboard.DOWN)) + { + atari.physics.acceleration.y = 150; + } + + // collide? + + } + + function render() { + + atari.physics.renderDebugInfo(16, 16); + card.physics.renderDebugInfo(200, 16); + + } + +})(); diff --git a/Tests/physics/circle 1.js b/Tests/physics/circle 1.js new file mode 100644 index 00000000..0d0bc7fd --- /dev/null +++ b/Tests/physics/circle 1.js @@ -0,0 +1,38 @@ +/// +(function () { + var game = new Phaser.Game(this, 'game', 800, 600, init, create, update, render); + function init() { + // Using Phasers asset loader we load up a PNG from the assets folder + game.loader.addImageFile('atari', 'assets/sprites/mushroom2.png'); + game.loader.load(); + } + var atari; + function create() { + atari = game.add.sprite(200, 300, 'atari'); + atari.texture.alpha = 0.5; + //atari.scale.setTo(1.5, 1.5); + atari.physics.setCircle(50); + //atari.physics.shape.setSize(150, 50); + atari.physics.shape.offset.setTo(7, 5); + //atari.physics.gravity.setTo(0, 2); + atari.physics.bounce.setTo(0.7, 0.7); + atari.physics.drag.setTo(10, 10); + } + function update() { + atari.physics.acceleration.x = 0; + atari.physics.acceleration.y = 0; + if(game.input.keyboard.isDown(Phaser.Keyboard.LEFT)) { + atari.physics.acceleration.x = -150; + } else if(game.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) { + atari.physics.acceleration.x = 150; + } + if(game.input.keyboard.isDown(Phaser.Keyboard.UP)) { + atari.physics.acceleration.y = -150; + } else if(game.input.keyboard.isDown(Phaser.Keyboard.DOWN)) { + atari.physics.acceleration.y = 150; + } + } + function render() { + atari.physics.renderDebugInfo(16, 16); + } +})(); diff --git a/Tests/physics/circle 1.ts b/Tests/physics/circle 1.ts new file mode 100644 index 00000000..556dc117 --- /dev/null +++ b/Tests/physics/circle 1.ts @@ -0,0 +1,65 @@ +/// + +(function () { + + var game = new Phaser.Game(this, 'game', 800, 600, init, create, update, render); + + function init() { + + // Using Phasers asset loader we load up a PNG from the assets folder + game.loader.addImageFile('atari', 'assets/sprites/mushroom2.png'); + game.loader.load(); + + } + + var atari: Phaser.Sprite; + + function create() { + + atari = game.add.sprite(200, 300, 'atari'); + atari.texture.alpha = 0.5; + //atari.scale.setTo(1.5, 1.5); + + atari.physics.setCircle(50); + + //atari.physics.shape.setSize(150, 50); + atari.physics.shape.offset.setTo(7, 5); + + //atari.physics.gravity.setTo(0, 2); + atari.physics.bounce.setTo(0.7, 0.7); + atari.physics.drag.setTo(10, 10); + + } + + function update() { + + atari.physics.acceleration.x = 0; + atari.physics.acceleration.y = 0; + + if (game.input.keyboard.isDown(Phaser.Keyboard.LEFT)) + { + atari.physics.acceleration.x = -150; + } + else if (game.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) + { + atari.physics.acceleration.x = 150; + } + + if (game.input.keyboard.isDown(Phaser.Keyboard.UP)) + { + atari.physics.acceleration.y = -150; + } + else if (game.input.keyboard.isDown(Phaser.Keyboard.DOWN)) + { + atari.physics.acceleration.y = 150; + } + + } + + function render() { + + atari.physics.renderDebugInfo(16, 16); + + } + +})(); diff --git a/build/phaser.d.ts b/build/phaser.d.ts index e583854f..19ffd41c 100644 --- a/build/phaser.d.ts +++ b/build/phaser.d.ts @@ -3250,9 +3250,10 @@ module Phaser.Physics { public bounce: Vec2; public friction: Vec2; public add(shape: IPhysicsShape): IPhysicsShape; + public remove(shape: IPhysicsShape): void; public update(): void; public render(): void; - private updateMotion(obj); + private updateMotion(shape); /** * A tween-like function that takes a starting velocity and some other factors and returns an altered velocity. * @@ -3264,9 +3265,12 @@ module Phaser.Physics { * @return {number} The altered Velocity value. */ public computeVelocity(velocity: number, gravity?: number, acceleration?: number, drag?: number, max?: number): number; - private collideWorld(obj); - private separateX(shape, distance, tangent); - private separateY(shape, distance, tangent); + private collideShapes(shapeA, shapeB); + private collideWorld(shape); + private separateX(shapeA, shapeB, distance, tangent); + private separateY(shapeA, shapeB, distance, tangent); + private separateXWall(shapeA, distance, tangent); + private separateYWall(shapeA, distance, tangent); private separate(shape, distance, tangent); } } @@ -3422,6 +3426,51 @@ module Phaser { static GEOM_RECTANGLE: number; static GEOM_LINE: number; static GEOM_POLYGON: number; + /** + * Flag used to allow GameObjects to collide on their left side + * @type {number} + */ + static LEFT: number; + /** + * Flag used to allow GameObjects to collide on their right side + * @type {number} + */ + static RIGHT: number; + /** + * Flag used to allow GameObjects to collide on their top side + * @type {number} + */ + static UP: number; + /** + * Flag used to allow GameObjects to collide on their bottom side + * @type {number} + */ + static DOWN: number; + /** + * Flag used with GameObjects to disable collision + * @type {number} + */ + static NONE: number; + /** + * Flag used to allow GameObjects to collide with a ceiling + * @type {number} + */ + static CEILING: number; + /** + * Flag used to allow GameObjects to collide with a floor + * @type {number} + */ + static FLOOR: number; + /** + * Flag used to allow GameObjects to collide with a wall (same as LEFT+RIGHT) + * @type {number} + */ + static WALL: number; + /** + * Flag used to allow GameObjects to collide on any face + * @type {number} + */ + static ANY: number; } } /** @@ -6605,6 +6654,30 @@ module Phaser { } } /** +* Phaser - Physics - Circle +*/ +module Phaser.Physics { + class Circle implements IPhysicsShape { + constructor(game: Game, sprite: Sprite, x: number, y: number, diameter: number); + public game: Game; + public world: PhysicsManager; + public sprite: Sprite; + public physics: Components.Physics; + public position: Vec2; + public oldPosition: Vec2; + public offset: Vec2; + public scale: Vec2; + public bounds: Rectangle; + public radius: number; + public oH: number; + public oV: number; + public preUpdate(): void; + public update(): void; + public setSize(width: number, height: number): void; + public render(context: CanvasRenderingContext2D): void; + } +} +/** * Phaser - Components - Physics */ module Phaser.Components { @@ -6629,6 +6702,8 @@ module Phaser.Components { public friction: Vec2; public velocity: Vec2; public acceleration: Vec2; + public touching: number; + public setCircle(diameter: number): void; /** * Internal function for updating the position and speed of this object. */ diff --git a/build/phaser.js b/build/phaser.js index 365aa70d..a414e491 100644 --- a/build/phaser.js +++ b/build/phaser.js @@ -2958,15 +2958,8 @@ var Phaser; **/ function intersects(a, b, tolerance) { if (typeof tolerance === "undefined") { tolerance = 0; } - // ? - //return (a.right > b.x) && (a.x < b.right) && (a.bottom > b.y) && (a.y < b.bottom); return !(a.left > b.right + tolerance || a.right < b.left - tolerance || a.top > b.bottom + tolerance || a.bottom < b.top - tolerance); - //if (a.x >= b.right || a.right <= b.x || a.bottom <= b.y || a.y >= b.bottom) - //{ - // return false; - //} - //return true; - }; + }; RectangleUtils.intersectsRaw = /** * Determines whether the object specified intersects (overlaps) with the given values. * @method intersectsRaw @@ -3262,7 +3255,6 @@ var Phaser; if (typeof diameter === "undefined") { diameter = 0; } this._diameter = 0; this._radius = 0; - //private _pos: Vec2; /** * The x coordinate of the center of the circle * @property x @@ -5858,6 +5850,7 @@ var Phaser; (function (Phaser) { /// /// + /// /** * Phaser - PhysicsManager * @@ -5883,22 +5876,41 @@ var Phaser; this._objects.push(shape); return shape; }; + PhysicsManager.prototype.remove = function (shape) { + this._length = this._objects.length; + for(var i = 0; i < this._length; i++) { + if(this._objects[i] === shape) { + this._objects[i] = null; + } + } + }; PhysicsManager.prototype.update = function () { this._length = this._objects.length; for(var i = 0; i < this._length; i++) { - this._objects[i].preUpdate(); - this.updateMotion(this._objects[i]); - this.collideWorld(this._objects[i]); + if(this._objects[i]) { + this._objects[i].preUpdate(); + this.updateMotion(this._objects[i]); + this.collideWorld(this._objects[i]); + if(this._objects[i].physics.immovable == false) { + for(var x = 0; x < this._length; x++) { + if(this._objects[x] !== this._objects[i]) { + this.collideShapes(this._objects[i], this._objects[x]); + } + } + } + } } }; PhysicsManager.prototype.render = function () { // iterate through the objects here, updating and colliding for(var i = 0; i < this._length; i++) { - this._objects[i].render(this.game.stage.context); + if(this._objects[i]) { + this._objects[i].render(this.game.stage.context); + } } }; - PhysicsManager.prototype.updateMotion = function (obj) { - if(obj.physics.moves == false) { + PhysicsManager.prototype.updateMotion = function (shape) { + if(shape.physics.moves == false) { return; } /* @@ -5907,16 +5919,16 @@ var Phaser; this._angle += this.angularVelocity * this._game.time.elapsed; this.angularVelocity += velocityDelta; */ - this._velocityDelta = (this.computeVelocity(obj.physics.velocity.x, obj.physics.gravity.x, obj.physics.acceleration.x, obj.physics.drag.x) - obj.physics.velocity.x) / 2; - obj.physics.velocity.x += this._velocityDelta; - this._delta = obj.physics.velocity.x * this.game.time.elapsed; - obj.physics.velocity.x += this._velocityDelta; - obj.position.x += this._delta; - this._velocityDelta = (this.computeVelocity(obj.physics.velocity.y, obj.physics.gravity.y, obj.physics.acceleration.y, obj.physics.drag.y) - obj.physics.velocity.y) / 2; - obj.physics.velocity.y += this._velocityDelta; - this._delta = obj.physics.velocity.y * this.game.time.elapsed; - obj.physics.velocity.y += this._velocityDelta; - obj.position.y += this._delta; + this._velocityDelta = (this.computeVelocity(shape.physics.velocity.x, shape.physics.gravity.x, shape.physics.acceleration.x, shape.physics.drag.x) - shape.physics.velocity.x) / 2; + shape.physics.velocity.x += this._velocityDelta; + this._delta = shape.physics.velocity.x * this.game.time.elapsed; + shape.physics.velocity.x += this._velocityDelta; + shape.position.x += this._delta; + this._velocityDelta = (this.computeVelocity(shape.physics.velocity.y, shape.physics.gravity.y, shape.physics.acceleration.y, shape.physics.drag.y) - shape.physics.velocity.y) / 2; + shape.physics.velocity.y += this._velocityDelta; + this._delta = shape.physics.velocity.y * this.game.time.elapsed; + shape.physics.velocity.y += this._velocityDelta; + shape.position.y += this._delta; }; PhysicsManager.prototype.computeVelocity = /** * A tween-like function that takes a starting velocity and some other factors and returns an altered velocity. @@ -5955,119 +5967,169 @@ var Phaser; } return velocity; }; - PhysicsManager.prototype.collideWorld = function (obj) { + PhysicsManager.prototype.collideShapes = function (shapeA, shapeB) { + if(shapeA.physics.immovable && shapeB.physics.immovable) { + return; + } + // Simple bounds check first + if(Phaser.RectangleUtils.intersects(shapeA.bounds, shapeB.bounds)) { + // Collide on the x-axis + if(shapeA.bounds.right >= shapeB.bounds.x && shapeA.bounds.right <= shapeB.bounds.right) { + // The right side of ShapeA hit the left side of ShapeB + this._distance.x = shapeB.bounds.x - shapeA.bounds.right; + if(this._distance.x != 0) { + this._tangent.setTo(-1, 0); + this.separateX(shapeA, shapeB, this._distance, this._tangent); + } + } else if(shapeA.bounds.x <= shapeB.bounds.right && shapeA.bounds.x >= shapeB.bounds.x) { + // The left side of ShapeA hit the right side of ShapeB + this._distance.x = shapeB.bounds.right - shapeA.bounds.x; + if(this._distance.x != 0) { + this._tangent.setTo(1, 0); + this.separateX(shapeA, shapeB, this._distance, this._tangent); + } + } + // Collide on the y-axis + if(shapeA.bounds.y <= shapeB.bounds.bottom && shapeA.bounds.y >= shapeB.bounds.y) { + console.log(shapeA.bounds.y, shapeB.bounds.bottom, shapeB.bounds.y); + // The top of ShapeA hit the bottom of ShapeB + this._distance.y = shapeB.bounds.bottom - shapeA.bounds.y; + if(this._distance.y != 0) { + this._tangent.setTo(0, 1); + this.separateY(shapeA, shapeB, this._distance, this._tangent); + } + } else if(shapeA.bounds.bottom >= shapeB.bounds.y && shapeA.bounds.bottom <= shapeB.bounds.bottom) { + console.log(shapeA.bounds.bottom, shapeB.bounds.y, shapeB.bounds.bottom); + // The bottom of ShapeA hit the top of ShapeB + this._distance.y = shapeB.bounds.y - shapeA.bounds.bottom; + if(this._distance.y != 0) { + this._tangent.setTo(0, -1); + this.separateY(shapeA, shapeB, this._distance, this._tangent); + } + } + } + }; + PhysicsManager.prototype.collideWorld = function (shape) { // Collide on the x-axis - this._distance.x = obj.world.bounds.x - (obj.position.x - obj.bounds.halfWidth); + this._distance.x = shape.world.bounds.x - (shape.position.x - shape.bounds.halfWidth); if(0 < this._distance.x) { // Hit Left this._tangent.setTo(1, 0); - this.separateX(obj, this._distance, this._tangent); + this.separateXWall(shape, this._distance, this._tangent); } else { - this._distance.x = (obj.position.x + obj.bounds.halfWidth) - obj.world.bounds.right; + this._distance.x = (shape.position.x + shape.bounds.halfWidth) - shape.world.bounds.right; if(0 < this._distance.x) { // Hit Right this._tangent.setTo(-1, 0); this._distance.reverse(); - this.separateX(obj, this._distance, this._tangent); + this.separateXWall(shape, this._distance, this._tangent); } } // Collide on the y-axis - this._distance.y = obj.world.bounds.y - (obj.position.y - obj.bounds.halfHeight); + this._distance.y = shape.world.bounds.y - (shape.position.y - shape.bounds.halfHeight); if(0 < this._distance.y) { // Hit Top this._tangent.setTo(0, 1); - this.separateY(obj, this._distance, this._tangent); + this.separateYWall(shape, this._distance, this._tangent); } else { - this._distance.y = (obj.position.y + obj.bounds.halfHeight) - obj.world.bounds.bottom; + this._distance.y = (shape.position.y + shape.bounds.halfHeight) - shape.world.bounds.bottom; if(0 < this._distance.y) { // Hit Bottom this._tangent.setTo(0, -1); this._distance.reverse(); - this.separateY(obj, this._distance, this._tangent); + this.separateYWall(shape, this._distance, this._tangent); } } }; - PhysicsManager.prototype.separateX = /* - private OLDButWorkingcollideWorld(obj:IPhysicsShape) { - - this._distance.setTo(0, 0); - - // Collide on the x-axis - this._distance.x = obj.world.bounds.x - (obj.position.x - obj.bounds.halfWidth); - - if (0 < this._distance.x) - { - // Hit Left - // Parameter order: px, py (distance), dx, dy (tangent) - this._tangent.setTo(1, 0); - this.separate(obj, this._distance, this._tangent); - } - else - { - this._distance.x = (obj.position.x + obj.bounds.halfWidth) - obj.world.bounds.right; - - if (0 < this._distance.x) - { - // Hit Right - // Parameter order: px, py (distance), dx, dy (tangent) - this._tangent.setTo(-1, 0); - this._distance.x = -this._distance.x; - this.separate(obj, this._distance, this._tangent); - } - } - - // Collide on the y-axis - this._distance.x = 0; - this._distance.y = obj.world.bounds.y - (obj.position.y - obj.bounds.halfHeight); - - if (0 < this._distance.y) - { - // Hit Top - this._tangent.setTo(0, 1); - this.separate(obj, this._distance, this._tangent); - } - else - { - this._distance.y = (obj.position.y + obj.bounds.halfHeight) - obj.world.bounds.bottom; - - if (0 < this._distance.y) - { - // Hit Bottom - this._tangent.setTo(0, -1); - this._distance.y = -this._distance.y; - this.separate(obj, this._distance, this._tangent); - } - } - - } - */ - function (shape, distance, tangent) { + PhysicsManager.prototype.separateX = function (shapeA, shapeB, distance, tangent) { + if(tangent.x == 1) { + console.log('The left side of ShapeA hit the right side of ShapeB', distance.x); + shapeA.physics.touching |= Phaser.Types.LEFT; + shapeB.physics.touching |= Phaser.Types.RIGHT; + } else { + console.log('The right side of ShapeA hit the left side of ShapeB', distance.x); + shapeA.physics.touching |= Phaser.Types.RIGHT; + shapeB.physics.touching |= Phaser.Types.LEFT; + } // collision edges - shape.oH = tangent.x; + shapeA.oH = tangent.x; // only apply collision response forces if the object is travelling into, and not out of, the collision - if(Phaser.Vec2Utils.dot(shape.physics.velocity, tangent) < 0) { + if(Phaser.Vec2Utils.dot(shapeA.physics.velocity, tangent) < 0) { // Apply horizontal bounce - if(shape.physics.bounce.x > 0) { - shape.physics.velocity.x *= -(shape.physics.bounce.x); + if(shapeA.physics.bounce.x > 0) { + shapeA.physics.velocity.x *= -(shapeA.physics.bounce.x); } else { - shape.physics.velocity.x = 0; + shapeA.physics.velocity.x = 0; } } - shape.position.x += distance.x; + shapeA.position.x += distance.x; + shapeA.bounds.x += distance.x; }; - PhysicsManager.prototype.separateY = function (shape, distance, tangent) { + PhysicsManager.prototype.separateY = function (shapeA, shapeB, distance, tangent) { + if(tangent.y == 1) { + console.log('The top of ShapeA hit the bottom of ShapeB', distance.y); + shapeA.physics.touching |= Phaser.Types.UP; + shapeB.physics.touching |= Phaser.Types.DOWN; + } else { + console.log('The bottom of ShapeA hit the top of ShapeB', distance.y); + shapeA.physics.touching |= Phaser.Types.DOWN; + shapeB.physics.touching |= Phaser.Types.UP; + } // collision edges - shape.oV = tangent.y; + shapeA.oV = tangent.y; // only apply collision response forces if the object is travelling into, and not out of, the collision - if(Phaser.Vec2Utils.dot(shape.physics.velocity, tangent) < 0) { + if(Phaser.Vec2Utils.dot(shapeA.physics.velocity, tangent) < 0) { // Apply horizontal bounce - if(shape.physics.bounce.y > 0) { - shape.physics.velocity.y *= -(shape.physics.bounce.y); + if(shapeA.physics.bounce.y > 0) { + shapeA.physics.velocity.y *= -(shapeA.physics.bounce.y); } else { - shape.physics.velocity.y = 0; + shapeA.physics.velocity.y = 0; } } - shape.position.y += distance.y; + shapeA.position.y += distance.y; + shapeA.bounds.y += distance.y; + }; + PhysicsManager.prototype.separateXWall = function (shapeA, distance, tangent) { + if(tangent.x == 1) { + console.log('The left side of ShapeA hit the right side of ShapeB', distance.x); + shapeA.physics.touching |= Phaser.Types.LEFT; + } else { + console.log('The right side of ShapeA hit the left side of ShapeB', distance.x); + shapeA.physics.touching |= Phaser.Types.RIGHT; + } + // collision edges + shapeA.oH = tangent.x; + // only apply collision response forces if the object is travelling into, and not out of, the collision + if(Phaser.Vec2Utils.dot(shapeA.physics.velocity, tangent) < 0) { + // Apply horizontal bounce + if(shapeA.physics.bounce.x > 0) { + shapeA.physics.velocity.x *= -(shapeA.physics.bounce.x); + } else { + shapeA.physics.velocity.x = 0; + } + } + shapeA.position.x += distance.x; + }; + PhysicsManager.prototype.separateYWall = function (shapeA, distance, tangent) { + if(tangent.y == 1) { + console.log('The top of ShapeA hit the bottom of ShapeB', distance.y); + shapeA.physics.touching |= Phaser.Types.UP; + } else { + console.log('The bottom of ShapeA hit the top of ShapeB', distance.y); + shapeA.physics.touching |= Phaser.Types.DOWN; + } + // collision edges + shapeA.oV = tangent.y; + // only apply collision response forces if the object is travelling into, and not out of, the collision + if(Phaser.Vec2Utils.dot(shapeA.physics.velocity, tangent) < 0) { + // Apply horizontal bounce + if(shapeA.physics.bounce.y > 0) { + shapeA.physics.velocity.y *= -(shapeA.physics.bounce.y); + } else { + shapeA.physics.velocity.y = 0; + } + } + shapeA.position.y += distance.y; }; PhysicsManager.prototype.separate = function (shape, distance, tangent) { // collision edges @@ -6111,7 +6173,46 @@ var Phaser; return PhysicsManager; })(); Physics.PhysicsManager = PhysicsManager; - })(Phaser.Physics || (Phaser.Physics = {})); + /** + * Checks for overlaps between two objects using the world QuadTree. Can be GameObject vs. GameObject, GameObject vs. Group or Group vs. Group. + * Note: Does not take the objects scrollFactor into account. All overlaps are check in world space. + * @param object1 The first GameObject or Group to check. If null the world.group is used. + * @param object2 The second GameObject or Group to check. + * @param notifyCallback A 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 passed them to Collision.overlap. + * @param processCallback A callback function that lets you perform additional checks against the two objects if they overlap. If this is set then notifyCallback will only be called if processCallback returns true. + * @param context The context in which the callbacks will be called + * @returns {boolean} true if the objects overlap, otherwise false. + */ + /* + public overlap(object1: Basic = null, object2: Basic = null, notifyCallback = null, processCallback = null, context = null): bool { + + if (object1 == null) + { + object1 = this._game.world.group; + } + + if (object2 == object1) + { + object2 = null; + } + + QuadTree.divisions = this._game.world.worldDivisions; + + var quadTree: QuadTree = new QuadTree(this._game.world.bounds.x, this._game.world.bounds.y, this._game.world.bounds.width, this._game.world.bounds.height); + + quadTree.load(object1, object2, notifyCallback, processCallback, context); + + var result: bool = quadTree.execute(); + + quadTree.destroy(); + + quadTree = null; + + return result; + + } + */ + })(Phaser.Physics || (Phaser.Physics = {})); var Physics = Phaser.Physics; })(Phaser || (Phaser = {})); var Phaser; @@ -6144,6 +6245,8 @@ var Phaser; } AABB.prototype.preUpdate = function () { this.oldPosition.copyFrom(this.position); + this.bounds.x = this.position.x - this.bounds.halfWidth; + this.bounds.y = this.position.y - this.bounds.halfHeight; if(this.sprite) { this.position.setTo((this.sprite.x + this.bounds.halfWidth) + this.offset.x, (this.sprite.y + this.bounds.halfHeight) + this.offset.y); // Update scale / dimensions @@ -6155,14 +6258,19 @@ var Phaser; } }; AABB.prototype.update = function () { - this.bounds.x = this.position.x; - this.bounds.y = this.position.y; - }; + //this.bounds.x = this.position.x; + //this.bounds.y = this.position.y; + }; AABB.prototype.setSize = function (width, height) { this.bounds.width = width; this.bounds.height = height; }; AABB.prototype.render = function (context) { + //context.beginPath(); + //context.strokeStyle = 'rgb(255,255,0)'; + //context.strokeRect(this.bounds.x, this.bounds.y, this.bounds.width, this.bounds.height); + //context.stroke(); + //context.closePath(); context.beginPath(); context.strokeStyle = 'rgb(0,255,0)'; context.strokeRect(this.position.x - this.bounds.halfWidth, this.position.y - this.bounds.halfHeight, this.bounds.width, this.bounds.height); @@ -6171,14 +6279,14 @@ var Phaser; // center point context.fillStyle = 'rgb(0,255,0)'; context.fillRect(this.position.x, this.position.y, 2, 2); - if(this.oH == 1) { + if(this.physics.touching == Phaser.Types.LEFT) { context.beginPath(); context.strokeStyle = 'rgb(255,0,0)'; context.moveTo(this.position.x - this.bounds.halfWidth, this.position.y - this.bounds.halfHeight); context.lineTo(this.position.x - this.bounds.halfWidth, this.position.y + this.bounds.halfHeight); context.stroke(); context.closePath(); - } else if(this.oH == -1) { + } else if(this.physics.touching == Phaser.Types.RIGHT) { context.beginPath(); context.strokeStyle = 'rgb(255,0,0)'; context.moveTo(this.position.x + this.bounds.halfWidth, this.position.y - this.bounds.halfHeight); @@ -6186,14 +6294,14 @@ var Phaser; context.stroke(); context.closePath(); } - if(this.oV == 1) { + if(this.physics.touching == Phaser.Types.UP) { context.beginPath(); context.strokeStyle = 'rgb(255,0,0)'; context.moveTo(this.position.x - this.bounds.halfWidth, this.position.y - this.bounds.halfHeight); context.lineTo(this.position.x + this.bounds.halfWidth, this.position.y - this.bounds.halfHeight); context.stroke(); context.closePath(); - } else if(this.oV == -1) { + } else if(this.physics.touching == Phaser.Types.DOWN) { context.beginPath(); context.strokeStyle = 'rgb(255,0,0)'; context.moveTo(this.position.x - this.bounds.halfWidth, this.position.y + this.bounds.halfHeight); @@ -6439,6 +6547,15 @@ var Phaser; Types.GEOM_RECTANGLE = 2; Types.GEOM_LINE = 3; Types.GEOM_POLYGON = 4; + Types.LEFT = 0x0001; + Types.RIGHT = 0x0010; + Types.UP = 0x0100; + Types.DOWN = 0x1000; + Types.NONE = 0; + Types.CEILING = Types.UP; + Types.FLOOR = Types.DOWN; + Types.WALL = Types.LEFT | Types.RIGHT; + Types.ANY = Types.LEFT | Types.RIGHT | Types.UP | Types.DOWN; return Types; })(); Phaser.Types = Types; @@ -12132,17 +12249,120 @@ var Phaser; Phaser.Vec2 = Vec2; })(Phaser || (Phaser = {})); var Phaser; +(function (Phaser) { + /// + /// + /// + /// + /// + /** + * Phaser - Physics - Circle + */ + (function (Physics) { + var Circle = (function () { + function Circle(game, sprite, x, y, diameter) { + this.game = game; + this.world = game.world.physics; + if(sprite !== null) { + this.sprite = sprite; + this.scale = Phaser.Vec2Utils.clone(this.sprite.scale); + } else { + this.sprite = null; + this.physics = null; + this.scale = new Phaser.Vec2(1, 1); + } + this.radius = diameter / 2; + this.bounds = new Phaser.Rectangle(x + Math.round(diameter / 2), y + Math.round(diameter / 2), diameter, diameter); + this.position = new Phaser.Vec2(x + this.bounds.halfWidth, y + this.bounds.halfHeight); + this.oldPosition = new Phaser.Vec2(x + this.bounds.halfWidth, y + this.bounds.halfHeight); + this.offset = new Phaser.Vec2(0, 0); + } + Circle.prototype.preUpdate = function () { + this.oldPosition.copyFrom(this.position); + if(this.sprite) { + this.position.setTo((this.sprite.x + this.bounds.halfWidth) + this.offset.x, (this.sprite.y + this.bounds.halfHeight) + this.offset.y); + // Update scale / dimensions + if(Phaser.Vec2Utils.equals(this.scale, this.sprite.scale) == false) { + this.scale.copyFrom(this.sprite.scale); + // needs to be radius based (+ square) + //this.bounds.width = this.sprite.width; + //this.bounds.height = this.sprite.height; + } + } + }; + Circle.prototype.update = function () { + this.bounds.x = this.position.x; + this.bounds.y = this.position.y; + }; + Circle.prototype.setSize = function (width, height) { + this.bounds.width = width; + this.bounds.height = height; + }; + Circle.prototype.render = function (context) { + context.beginPath(); + context.strokeStyle = 'rgb(0,255,0)'; + context.arc(this.position.x, this.position.y, this.radius, 0, Math.PI * 2); + context.stroke(); + context.closePath(); + // center point + context.fillStyle = 'rgb(0,255,0)'; + context.fillRect(this.position.x, this.position.y, 2, 2); + if(this.oH == 1) { + context.beginPath(); + context.strokeStyle = 'rgb(255,0,0)'; + context.moveTo(this.position.x - this.bounds.halfWidth, this.position.y - this.bounds.halfHeight); + context.lineTo(this.position.x - this.bounds.halfWidth, this.position.y + this.bounds.halfHeight); + context.stroke(); + context.closePath(); + } else if(this.oH == -1) { + context.beginPath(); + context.strokeStyle = 'rgb(255,0,0)'; + context.moveTo(this.position.x + this.bounds.halfWidth, this.position.y - this.bounds.halfHeight); + context.lineTo(this.position.x + this.bounds.halfWidth, this.position.y + this.bounds.halfHeight); + context.stroke(); + context.closePath(); + } + if(this.oV == 1) { + context.beginPath(); + context.strokeStyle = 'rgb(255,0,0)'; + context.moveTo(this.position.x - this.bounds.halfWidth, this.position.y - this.bounds.halfHeight); + context.lineTo(this.position.x + this.bounds.halfWidth, this.position.y - this.bounds.halfHeight); + context.stroke(); + context.closePath(); + } else if(this.oV == -1) { + context.beginPath(); + context.strokeStyle = 'rgb(255,0,0)'; + context.moveTo(this.position.x - this.bounds.halfWidth, this.position.y + this.bounds.halfHeight); + context.lineTo(this.position.x + this.bounds.halfWidth, this.position.y + this.bounds.halfHeight); + context.stroke(); + context.closePath(); + } + }; + return Circle; + })(); + Physics.Circle = Circle; + })(Phaser.Physics || (Phaser.Physics = {})); + var Physics = Phaser.Physics; +})(Phaser || (Phaser = {})); +var Phaser; (function (Phaser) { /// /// /// /// + /// + /// /** * Phaser - Components - Physics */ (function (Components) { var Physics = (function () { function Physics(parent) { + /** + * Whether this object will be moved by impacts with other objects or not. + * @type {boolean} + */ + this.immovable = false; /** * Set this to false if you want to skip the automatic movement stuff * @type {boolean} @@ -12156,13 +12376,19 @@ var Phaser; this.friction = Phaser.Vec2Utils.clone(this.game.world.physics.friction); this.velocity = new Phaser.Vec2(); this.acceleration = new Phaser.Vec2(); + this.touching = Phaser.Types.NONE; this.shape = this.game.world.physics.add(new Phaser.Physics.AABB(this.game, this._sprite, this._sprite.x, this._sprite.y, this._sprite.width, this._sprite.height)); } + Physics.prototype.setCircle = function (diameter) { + this.game.world.physics.remove(this.shape); + this.shape = this.game.world.physics.add(new Phaser.Physics.Circle(this.game, this._sprite, this._sprite.x, this._sprite.y, diameter)); + this._sprite.physics.shape.physics = this; + }; Physics.prototype.update = /** * Internal function for updating the position and speed of this object. */ function () { - if(this.moves) { + if(this.moves && this.shape) { this._sprite.x = (this.shape.position.x - this.shape.bounds.halfWidth) - this.shape.offset.x; this._sprite.y = (this.shape.position.y - this.shape.bounds.halfHeight) - this.shape.offset.y; } @@ -12177,7 +12403,8 @@ var Phaser; if (typeof color === "undefined") { color = 'rgb(255,255,255)'; } this._sprite.texture.context.fillStyle = color; this._sprite.texture.context.fillText('Sprite: (' + this._sprite.frameBounds.width + ' x ' + this._sprite.frameBounds.height + ')', x, y); - this._sprite.texture.context.fillText('x: ' + this._sprite.frameBounds.x.toFixed(1) + ' y: ' + this._sprite.frameBounds.y.toFixed(1) + ' rotation: ' + this._sprite.rotation.toFixed(1), x, y + 14); + //this._sprite.texture.context.fillText('x: ' + this._sprite.frameBounds.x.toFixed(1) + ' y: ' + this._sprite.frameBounds.y.toFixed(1) + ' rotation: ' + this._sprite.rotation.toFixed(1), x, y + 14); + this._sprite.texture.context.fillText('x: ' + this.shape.bounds.x.toFixed(1) + ' y: ' + this.shape.bounds.y.toFixed(1) + ' rotation: ' + this._sprite.rotation.toFixed(1), x, y + 14); this._sprite.texture.context.fillText('vx: ' + this.velocity.x.toFixed(1) + ' vy: ' + this.velocity.y.toFixed(1), x, y + 28); this._sprite.texture.context.fillText('ax: ' + this.acceleration.x.toFixed(1) + ' ay: ' + this.acceleration.y.toFixed(1), x, y + 42); };