diff --git a/README.md b/README.md index b00b3007..92b7c791 100644 --- a/README.md +++ b/README.md @@ -172,6 +172,7 @@ Updates: * Completely empty Tilemaps can now be created. This allows for dynamic map generation at runtime. * Keyboard.event now stores the most recent DOM keyboard event. * Animation.stop has a new parameter: dispatchComplete. If true it'll dispatch an Animation.onComplete event. +* TileSprites now have a physics body property and call it in the pre and post updates. As with all physics bodies it's null by default. Bug Fixes: diff --git a/examples/wip/ninja aabb vs aabb.js b/examples/wip/ninja aabb vs aabb.js new file mode 100644 index 00000000..88bc348f --- /dev/null +++ b/examples/wip/ninja aabb vs aabb.js @@ -0,0 +1,62 @@ + +// var game = new Phaser.Game(800, 600, Phaser.AUTO, 'phaser-example', { preload: preload, create: create, update: update, render: render }); +var game = new Phaser.Game(800, 600, Phaser.CANVAS, 'phaser-example', { preload: preload, create: create, update: update, render: render }); + +function preload() { + + game.load.image('block', 'assets/sprites/block.png'); + +} + +var sprite1; +var sprite2; +var cursors; + +function create() { + + game.physics.startSystem(Phaser.Physics.NINJA); + + sprite1 = game.add.sprite(100, 100, 'block'); + sprite2 = game.add.sprite(400, 100, 'block'); + + // game.physics.ninja.enableAABB([sprite1]); + game.physics.ninja.enableAABB([sprite1, sprite2]); + + cursors = game.input.keyboard.createCursorKeys(); + +} + +function update() { + + game.physics.ninja.collide(sprite1, sprite2); + + if (cursors.left.isDown) + { + sprite1.body.moveLeft(20); + } + else if (cursors.right.isDown) + { + sprite1.body.moveRight(20); + } + + // if (cursors.up.isDown && sprite1.body.touching.down) + if (cursors.up.isDown) + { + // sprite1.body.moveUp(1000); + sprite1.body.moveUp(30); + } + // else if (cursors.down.isDown) + // { + // sprite1.body.moveDown(20); + // } + +} + +function render() { + + game.debug.text('left: ' + sprite1.body.touching.left, 32, 32); + game.debug.text('right: ' + sprite1.body.touching.right, 256, 32); + game.debug.text('up: ' + sprite1.body.touching.up, 32, 64); + game.debug.text('down: ' + sprite1.body.touching.down, 256, 64); + +} diff --git a/examples/wip/ninja tilesprite.js b/examples/wip/ninja tilesprite.js new file mode 100644 index 00000000..d15e0070 --- /dev/null +++ b/examples/wip/ninja tilesprite.js @@ -0,0 +1,77 @@ + +// var game = new Phaser.Game(800, 600, Phaser.AUTO, 'phaser-example', { preload: preload, create: create, update: update, render: render }); +var game = new Phaser.Game(800, 600, Phaser.CANVAS, 'phaser-example', { preload: preload, create: create, update: update, render: render }); + +function preload() { + + game.load.image('starfield', 'assets/misc/starfield.jpg'); + game.load.image('ball', 'assets/sprites/shinyball.png'); + +} + +var ball; +var sprite; +var cursors; + +function create() { + + game.physics.startSystem(Phaser.Physics.NINJA); + + sprite = game.add.tileSprite(100, 100, 200, 200, 'starfield'); + sprite.autoScroll(0, 100); + + game.physics.ninja.enableAABB(sprite); + // game.physics.ninja.enableTile(sprite, 14); + + // By default Tiles have gravity and world collision disabled (as they are mostly used for platforms and the likes) + // We re-enable it here + // sprite.body.gravityScale = 1; + // sprite.body.collideWorldBounds = true; + + ball = game.add.sprite(400, 0, 'ball'); + + // Enable the physics body for the Ninja physics system + game.physics.ninja.enableCircle(ball, ball.width / 2); + + // A little more bounce + ball.body.bounce = 0.5; + + cursors = game.input.keyboard.createCursorKeys(); + +} + +function update() { + + game.physics.ninja.collide(sprite, ball); + + if (cursors.left.isDown) + { + sprite.body.moveLeft(20); + } + else if (cursors.right.isDown) + { + sprite.body.moveRight(20); + } + + if (cursors.up.isDown) + { + sprite.body.moveUp(20); + } + else if (cursors.down.isDown) + { + sprite.body.moveDown(20); + } + +} + +function render() { + + var r = new Phaser.Rectangle(sprite.body.x - (sprite.body.width / 2), sprite.body.y - (sprite.body.height / 2), sprite.body.width, sprite.body.height); + + // game.debug.text(sprite.body.x, 32, 32); + // game.debug.text(sprite.body.y, 32, 64); + // game.debug.text(sprite.body.width, 128, 32); + // game.debug.text(sprite.body.height, 128, 64); + // game.debug.geom(r, 'rgba(0,255,0,0.4)', true); + +} diff --git a/examples/wip/ninja3.js b/examples/wip/ninja3.js index 06a3fa48..608e5154 100644 --- a/examples/wip/ninja3.js +++ b/examples/wip/ninja3.js @@ -15,7 +15,6 @@ var sprite1; var tiles; var cursors; - function create() { var sky = game.add.image(0, 0, 'sky'); diff --git a/src/physics/ninja/AABB.js b/src/physics/ninja/AABB.js index 6010ebf0..be774d72 100644 --- a/src/physics/ninja/AABB.js +++ b/src/physics/ninja/AABB.js @@ -11,40 +11,80 @@ * @class Phaser.Physics.Ninja.AABB * @classdesc Arcade Physics Constructor * @constructor -* @param {Phaser.Physics.Ninja} system - A reference to the physics system. +* @param {Phaser.Physics.Ninja.Body} body - The body that owns this shape. * @param {number} x - The x coordinate to create this shape at. * @param {number} y - The y coordinate to create this shape at. * @param {number} width - The width of this AABB. * @param {number} height - The height of this AABB. */ -Phaser.Physics.Ninja.AABB = function (system, x, y, width, height) { +Phaser.Physics.Ninja.AABB = function (body, x, y, width, height) { - this.system = system; + /** + * @property {Phaser.Physics.Ninja.Body} system - A reference to the body that owns this shape. + */ + this.body = body; + + /** + * @property {Phaser.Physics.Ninja} system - A reference to the physics system. + */ + this.system = body.system; + + /** + * @property {Phaser.Point} pos - The position of this object. + */ this.pos = new Phaser.Point(x, y); + + /** + * @property {Phaser.Point} oldpos - The position of this object in the previous update. + */ this.oldpos = new Phaser.Point(x, y); + /** + * @property {number} xw - Half the width. + * @readonly + */ this.xw = Math.abs(width / 2); + + /** + * @property {number} xw - Half the height. + * @readonly + */ this.yw = Math.abs(height / 2); + /** + * @property {number} width - The width. + * @readonly + */ this.width = width; + + /** + * @property {number} height - The height. + * @readonly + */ this.height = height; + /** + * @property {number} oH - Internal var. + * @private + */ this.oH = 0; + + /** + * @property {number} oV - Internal var. + * @private + */ this.oV = 0; - // Setting drag to 0 and friction to 0 means you get a normalised speed (px psec) - this.drag = 1; - this.friction = 0.05; - this.gravityScale = 1; - this.bounce = 0.3; + /** + * @property {Phaser.Point} velocity - The velocity of this object. + */ this.velocity = new Phaser.Point(); - // temp collision values - this.px = 0; - this.py = 0; - - // collision mappings + /** + * @property {object} aabbTileProjections - All of the collision response handlers. + */ this.aabbTileProjections = {}; + this.aabbTileProjections[Phaser.Physics.Ninja.Tile.TYPE_FULL] = this.projAABB_Full; this.aabbTileProjections[Phaser.Physics.Ninja.Tile.TYPE_45DEG] = this.projAABB_45Deg; this.aabbTileProjections[Phaser.Physics.Ninja.Tile.TYPE_CONCAVE] = this.projAABB_Concave; @@ -76,8 +116,12 @@ Phaser.Physics.Ninja.AABB.prototype = { var py = this.pos.y; // integrate - this.pos.x += (this.drag * this.pos.x) - (this.drag * this.oldpos.x); - this.pos.y += (this.drag * this.pos.y) - (this.drag * this.oldpos.y) + (this.system.gravity * this.gravityScale); + // this.pos.x += (this.body.drag * this.pos.x) - (this.body.drag * this.oldpos.x); + // this.pos.y += (this.body.drag * this.pos.y) - (this.body.drag * this.oldpos.y) + (this.system.gravity * this.body.gravityScale); + + this.pos.x += (this.body.drag * this.pos.x) - (this.body.drag * this.oldpos.x); + this.pos.y += (this.body.drag * this.pos.y) - (this.body.drag * this.oldpos.y) + (this.system.gravity * this.body.gravityScale); + // store this.velocity.set(this.pos.x - px, this.pos.y - py); @@ -100,48 +144,247 @@ Phaser.Physics.Ninja.AABB.prototype = { var p = this.pos; var o = this.oldpos; - //calc velocity + // Calc velocity var vx = p.x - o.x; var vy = p.y - o.y; - //find component of velocity parallel to collision normal + // Find component of velocity parallel to collision normal var dp = (vx * dx + vy * dy); - var nx = dp * dx;//project velocity onto collision normal + var nx = dp * dx; //project velocity onto collision normal - var ny = dp * dy;//nx,ny is normal velocity + var ny = dp * dy; //nx,ny is normal velocity - var tx = vx - nx;//px,py is tangent velocity + var tx = vx - nx; //px,py is tangent velocity var ty = vy - ny; - //we only want to apply collision response forces if the object is travelling into, and not out of, the collision - var b, bx, by, f, fx, fy; + // We only want to apply collision response forces if the object is travelling into, and not out of, the collision + var b, bx, by, fx, fy; if (dp < 0) { - //f = FRICTION; - fx = tx * this.friction; - fy = ty * this.friction; + fx = tx * this.body.friction; + fy = ty * this.body.friction; - b = 1 + this.bounce; + b = 1 + this.body.bounce; bx = (nx * b); by = (ny * b); + if (dx === 1) + { + this.body.touching.left = true; + } + else if (dx === -1) + { + this.body.touching.right = true; + } + + if (dy === 1) + { + this.body.touching.up = true; + } + else if (dy === -1) + { + this.body.touching.down = true; + } } else { - //moving out of collision, do not apply forces + // Moving out of collision, do not apply forces bx = by = fx = fy = 0; } - p.x += px;//project object out of collision + // Project object out of collision + p.x += px; p.y += py; - o.x += px + bx + fx;//apply bounce+friction impulses which alter velocity + // Apply bounce+friction impulses which alter velocity + o.x += px + bx + fx; o.y += py + by + fy; }, + /** + * Process a body collision and apply the resulting forces. + * + * @method Phaser.Physics.Ninja.AABB#reportCollisionVsBody + * @param {number} px - The tangent velocity + * @param {number} py - The tangent velocity + * @param {number} dx - Collision normal + * @param {number} dy - Collision normal + * @param {number} obj - Object this AABB collided with + */ + reportCollisionVsBody: function (px, py, dx, dy, obj) { + + var p = this.pos; + var o = this.oldpos; + + // Calc velocity + var vx = p.x - o.x; + var vy = p.y - o.y; + + // Find component of velocity parallel to collision normal + var dp = (vx * dx + vy * dy); + var nx = dp * dx; //project velocity onto collision normal + + var ny = dp * dy; //nx,ny is normal velocity + + var tx = vx - nx; //px,py is tangent velocity + var ty = vy - ny; + + // We only want to apply collision response forces if the object is travelling into, and not out of, the collision + var b, bx, by, fx, fy; + + if (dp < 0) + { + fx = tx * this.body.friction; + fy = ty * this.body.friction; + + b = 1 + this.body.bounce; + + bx = (nx * b); + by = (ny * b); + } + else + { + // Moving out of collision, do not apply forces + bx = by = fx = fy = 0; + } + + // Project object out of collision + p.x += px; + p.y += py; + + // Apply bounce+friction impulses which alter velocity + o.x += px + bx + fx; + o.y += py + by + fy; + + // apply to obj + if (obj) + { + // obj.oldpos.x = + // console.log('reportCollisionVsBody'); + // obj.reportCollisionVsBody(px *= -1, py *= -1, dx *= -1, dy *= -1, null); + } + + }, + + /** + * Collides this AABB against the world bounds. + * + * @method Phaser.Physics.Ninja.AABB#collideWorldBounds + */ + collideWorldBounds: function () { + + var dx = this.system.bounds.x - (this.pos.x - this.xw); + + if (0 < dx) + { + this.reportCollisionVsWorld(dx, 0, 1, 0, null); + } + else + { + dx = (this.pos.x + this.xw) - this.system.bounds.width; + + if (0 < dx) + { + this.reportCollisionVsWorld(-dx, 0, -1, 0, null); + } + } + + var dy = this.system.bounds.y - (this.pos.y - this.yw); + + if (0 < dy) + { + this.reportCollisionVsWorld(0, dy, 0, 1, null); + } + else + { + dy = (this.pos.y + this.yw) - this.system.bounds.height; + + if (0 < dy) + { + this.reportCollisionVsWorld(0, -dy, 0, -1, null); + } + } + + }, + + /** + * Collides this AABB against a AABB. + * + * @method Phaser.Physics.Ninja.AABB#collideAABBVsAABB + * @param {Phaser.Physics.Ninja.AABB} aabb - The AABB to collide against. + */ + collideAABBVsAABB: function (aabb) { + + var pos = this.pos; + var c = aabb; + + var tx = c.pos.x; + var ty = c.pos.y; + var txw = c.xw; + var tyw = c.yw; + + var dx = pos.x - tx;//tile->obj delta + var px = (txw + this.xw) - Math.abs(dx);//penetration depth in x + + if (0 < px) + { + var dy = pos.y - ty;//tile->obj delta + var py = (tyw + this.yw) - Math.abs(dy);//pen depth in y + + if (0 < py) + { + //object may be colliding with tile; call tile-specific collision function + + //calculate projection vectors + if (px < py) + { + //project in x + if (dx < 0) + { + //project to the left + px *= -1; + py = 0; + } + else + { + //proj to right + py = 0; + } + } + else + { + //project in y + if (dy < 0) + { + //project up + px = 0; + py *= -1; + } + else + { + //project down + px = 0; + } + } + + // return this.aabbTileProjections[1](px, py, this, c); + + var l = Math.sqrt(px * px + py * py); + // this.reportCollisionVsWorld(px, py, px / l, py / l, c); + this.reportCollisionVsBody(px, py, px / l, py / l, c); + + return Phaser.Physics.Ninja.AABB.COL_AXIS; + + } + } + + return false; + + }, + /** * Collides this AABB against a Tile. * @@ -210,114 +453,6 @@ Phaser.Physics.Ninja.AABB.prototype = { }, - /** - * Collides this AABB against the world bounds. - * - * @method Phaser.Physics.Ninja.AABB#collideWorldBounds - */ - collideWorldBounds: function () { - - var p = this.pos; - var xw = this.xw; - var yw = this.yw; - - var XMIN = this.system.bounds.x; - var XMAX = this.system.bounds.width; - var YMIN = this.system.bounds.y; - var YMAX = this.system.bounds.height; - - var dx = this.system.bounds.x - (this.pos.x - this.xw); - - if (0 < dx) - { - this.reportCollisionVsWorld(dx, 0, 1, 0, null); - } - else - { - dx = (this.pos.x + this.xw) - this.system.bounds.width; - - if (0 < dx) - { - this.reportCollisionVsWorld(-dx, 0, -1, 0, null); - } - } - - var dy = this.system.bounds.y - (this.pos.y - this.yw); - - if (0 < dy) - { - this.reportCollisionVsWorld(0, dy, 0, 1, null); - } - else - { - dy = (this.pos.y + this.yw) - this.system.bounds.height; - - if (0 < dy) - { - this.reportCollisionVsWorld(0, -dy, 0, -1, null); - } - } - - }, - - /** - * Renders this AABB to the context. - * - * @method Phaser.Physics.Ninja.AABB#render - */ - render: function (context) { - - context.beginPath(); - context.strokeStyle = 'rgb(0,255,0)'; - context.strokeRect(this.pos.x - this.xw, this.pos.y - this.yw, this.xw * 2, this.yw * 2); - context.stroke(); - context.closePath(); - - context.fillStyle = 'rgb(0,255,0)'; - context.fillRect(this.pos.x, this.pos.y, 2, 2); - - /* - if (this.oH == 1) - { - context.beginPath(); - context.strokeStyle = 'rgb(255,0,0)'; - context.moveTo(this.pos.x - this.radius, this.pos.y - this.radius); - context.lineTo(this.pos.x - this.radius, this.pos.y + this.radius); - context.stroke(); - context.closePath(); - } - else if (this.oH == -1) - { - context.beginPath(); - context.strokeStyle = 'rgb(255,0,0)'; - context.moveTo(this.pos.x + this.radius, this.pos.y - this.radius); - context.lineTo(this.pos.x + this.radius, this.pos.y + this.radius); - context.stroke(); - context.closePath(); - } - - if (this.oV == 1) - { - context.beginPath(); - context.strokeStyle = 'rgb(255,0,0)'; - context.moveTo(this.pos.x - this.radius, this.pos.y - this.radius); - context.lineTo(this.pos.x + this.radius, this.pos.y - this.radius); - context.stroke(); - context.closePath(); - } - else if (this.oV == -1) - { - context.beginPath(); - context.strokeStyle = 'rgb(255,0,0)'; - context.moveTo(this.pos.x - this.radius, this.pos.y + this.radius); - context.lineTo(this.pos.x + this.radius, this.pos.y + this.radius); - context.stroke(); - context.closePath(); - } - */ - - }, - /** * Resolves tile collision. * @@ -336,7 +471,7 @@ Phaser.Physics.Ninja.AABB.prototype = { } else { - console.warn("Ninja.AABB.resolveTile was called with an empty (or unknown) tile!: id=" + tile.id + ")"); + // console.warn("Ninja.AABB.resolveTile was called with an empty (or unknown) tile!: id=" + tile.id + ")"); return false; } diff --git a/src/physics/ninja/Body.js b/src/physics/ninja/Body.js index 75e2ab33..d769b440 100644 --- a/src/physics/ninja/Body.js +++ b/src/physics/ninja/Body.js @@ -63,35 +63,6 @@ Phaser.Physics.Ninja.Body = function (system, sprite, type, id, radius) { */ this.shape = null; - var sx = sprite.x; - var sy = sprite.y; - - if (sprite.anchor.x === 0) - { - sx += (sprite.width * 0.5); - } - - if (sprite.anchor.y === 0) - { - sy += (sprite.height * 0.5); - } - - if (type === 1) - { - this.aabb = new Phaser.Physics.Ninja.AABB(this, sx, sy, sprite.width, sprite.height); - this.shape = this.aabb; - } - else if (type === 2) - { - this.circle = new Phaser.Physics.Ninja.Circle(this, sx, sy, radius); - this.shape = this.circle; - } - else if (type === 3) - { - this.tile = new Phaser.Physics.Ninja.Tile(this, sx, sy, sprite.width, sprite.height, id); - this.shape = this.tile; - } - // Setting drag to 0 and friction to 0 means you get a normalised speed (px psec) /** @@ -160,8 +131,41 @@ Phaser.Physics.Ninja.Body = function (system, sprite, type, id, radius) { */ this.wasTouching = { none: true, up: false, down: false, left: false, right: false }; + /** + * @property {number} maxSpeed - The maximum speed this body can travel at (taking drag and friction into account) + * @default + */ this.maxSpeed = 8; + var sx = sprite.x; + var sy = sprite.y; + + if (sprite.anchor.x === 0) + { + sx += (sprite.width * 0.5); + } + + if (sprite.anchor.y === 0) + { + sy += (sprite.height * 0.5); + } + + if (type === 1) + { + this.aabb = new Phaser.Physics.Ninja.AABB(this, sx, sy, sprite.width, sprite.height); + this.shape = this.aabb; + } + else if (type === 2) + { + this.circle = new Phaser.Physics.Ninja.Circle(this, sx, sy, radius); + this.shape = this.circle; + } + else if (type === 3) + { + this.tile = new Phaser.Physics.Ninja.Tile(this, sx, sy, sprite.width, sprite.height, id); + this.shape = this.tile; + } + }; Phaser.Physics.Ninja.Body.prototype = { @@ -173,18 +177,6 @@ Phaser.Physics.Ninja.Body.prototype = { * @protected */ updateBounds: function (centerX, centerY, scaleX, scaleY) { - - if (scaleX != this._sx || scaleY != this._sy) - { - // 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._sx = scaleX; - // this._sy = scaleY; - // this.center.setTo(this.x + this.halfWidth, this.y + this.halfHeight); - } - }, /** @@ -228,8 +220,17 @@ Phaser.Physics.Ninja.Body.prototype = { */ postUpdate: function () { - this.sprite.x = this.shape.pos.x; - this.sprite.y = this.shape.pos.y; + if (this.sprite.type === Phaser.TILESPRITE) + { + // TileSprites don't use their anchor property, so we need to adjust the coordinates + this.sprite.x = this.shape.pos.x - this.shape.xw; + this.sprite.y = this.shape.pos.y - this.shape.yw; + } + else + { + this.sprite.x = this.shape.pos.x; + this.sprite.y = this.shape.pos.y; + } }, @@ -403,6 +404,44 @@ Object.defineProperty(Phaser.Physics.Ninja.Body.prototype, "y", { }); +/** +* @name Phaser.Physics.Ninja.Body#width +* @property {number} width - The width of this Body +* @readonly +*/ +Object.defineProperty(Phaser.Physics.Ninja.Body.prototype, "width", { + + /** + * The width of this Body + * @method width + * @return {number} + * @readonly + */ + get: function () { + return this.shape.width; + } + +}); + +/** +* @name Phaser.Physics.Ninja.Body#height +* @property {number} height - The height of this Body +* @readonly +*/ +Object.defineProperty(Phaser.Physics.Ninja.Body.prototype, "height", { + + /** + * The height of this Body + * @method height + * @return {number} + * @readonly + */ + get: function () { + return this.shape.height; + } + +}); + /** * @name Phaser.Physics.Ninja.Body#bottom * @property {number} bottom - The bottom value of this Body (same as Body.y + Body.height) diff --git a/src/physics/ninja/Circle.js b/src/physics/ninja/Circle.js index 9c31a257..92af178d 100644 --- a/src/physics/ninja/Circle.js +++ b/src/physics/ninja/Circle.js @@ -158,6 +158,23 @@ Phaser.Physics.Ninja.Circle.prototype = { bx = (nx * b); by = (ny * b); + if (dx === 1) + { + this.body.touching.left = true; + } + else if (dx === -1) + { + this.body.touching.right = true; + } + + if (dy === 1) + { + this.body.touching.up = true; + } + else if (dy === -1) + { + this.body.touching.down = true; + } } else { @@ -182,15 +199,7 @@ Phaser.Physics.Ninja.Circle.prototype = { */ collideWorldBounds: function () { - var p = this.pos; - var r = this.radius; - - var XMIN = this.system.bounds.x; - var XMAX = this.system.bounds.width; - var YMIN = this.system.bounds.y; - var YMAX = this.system.bounds.height; - - var dx = this.system.bounds.x - (this.pos.x - this.radius); + var dx = this.system.bounds.x - (this.pos.x - this.xw); if (0 < dx) { @@ -198,7 +207,7 @@ Phaser.Physics.Ninja.Circle.prototype = { } else { - dx = (this.pos.x + this.radius) - this.system.bounds.width; + dx = (this.pos.x + this.xw) - this.system.bounds.width; if (0 < dx) { @@ -206,7 +215,7 @@ Phaser.Physics.Ninja.Circle.prototype = { } } - var dy = this.system.bounds.y - (this.pos.y - this.radius); + var dy = this.system.bounds.y - (this.pos.y - this.yw); if (0 < dy) { @@ -214,7 +223,7 @@ Phaser.Physics.Ninja.Circle.prototype = { } else { - dy = (this.pos.y + this.radius) - this.system.bounds.height; + dy = (this.pos.y + this.yw) - this.system.bounds.height; if (0 < dy) { diff --git a/src/physics/ninja/Tile.js b/src/physics/ninja/Tile.js index 12a0d628..cf2dcf0d 100644 --- a/src/physics/ninja/Tile.js +++ b/src/physics/ninja/Tile.js @@ -6,6 +6,9 @@ /** * Ninja Physics Tile constructor. +* A Tile is defined by its width, height and type. It's type can include slope data, such as 45 degree slopes, or convex slopes. +* Understand that for any type including a slope (types 2 to 29) the Tile must be SQUARE, i.e. have an equal width and height. +* Also note that as Tiles are primarily used for levels they have gravity disabled and world bounds collision disabled by default. * * Note: This class could be massively optimised and reduced in size. I leave that challenge up to you. * @@ -114,6 +117,10 @@ Phaser.Physics.Ninja.Tile = function (body, x, y, width, height, type) { */ this.sy = 0; + // By default Tiles disable gravity and world bounds collision + this.body.gravityScale = 0; + this.body.collideWorldBounds = false; + if (this.id > 0) { this.setType(this.id); @@ -136,7 +143,7 @@ Phaser.Physics.Ninja.Tile.prototype = { var py = this.pos.y; this.pos.x += (this.body.drag * this.pos.x) - (this.body.drag * this.oldpos.x); - this.pos.y += (this.body.drag * this.pos.y) - (this.body.drag * this.oldpos.y); + this.pos.y += (this.body.drag * this.pos.y) - (this.body.drag * this.oldpos.y) + (this.system.gravity * this.body.gravityScale); this.velocity.set(this.pos.x - px, this.pos.y - py); this.oldpos.set(px, py); @@ -149,6 +156,114 @@ Phaser.Physics.Ninja.Tile.prototype = { * @method Phaser.Physics.Ninja.Tile#collideWorldBounds */ collideWorldBounds: function () { + + var dx = this.system.bounds.x - (this.pos.x - this.xw); + + if (0 < dx) + { + this.reportCollisionVsWorld(dx, 0, 1, 0, null); + } + else + { + dx = (this.pos.x + this.xw) - this.system.bounds.width; + + if (0 < dx) + { + this.reportCollisionVsWorld(-dx, 0, -1, 0, null); + } + } + + var dy = this.system.bounds.y - (this.pos.y - this.yw); + + if (0 < dy) + { + this.reportCollisionVsWorld(0, dy, 0, 1, null); + } + else + { + dy = (this.pos.y + this.yw) - this.system.bounds.height; + + if (0 < dy) + { + this.reportCollisionVsWorld(0, -dy, 0, -1, null); + } + } + + }, + + /** + * Process a world collision and apply the resulting forces. + * + * @method Phaser.Physics.Ninja.Tile#reportCollisionVsWorld + * @param {number} px - The tangent velocity + * @param {number} py - The tangent velocity + * @param {number} dx - Collision normal + * @param {number} dy - Collision normal + * @param {number} obj - Object this Tile collided with + */ + reportCollisionVsWorld: function (px, py, dx, dy, obj) { + + var p = this.pos; + var o = this.oldpos; + + // Calc velocity + var vx = p.x - o.x; + var vy = p.y - o.y; + + // Find component of velocity parallel to collision normal + var dp = (vx * dx + vy * dy); + var nx = dp * dx; //project velocity onto collision normal + + var ny = dp * dy; //nx,ny is normal velocity + + var tx = vx - nx; //px,py is tangent velocity + var ty = vy - ny; + + // We only want to apply collision response forces if the object is travelling into, and not out of, the collision + var b, bx, by, fx, fy; + + if (dp < 0) + { + fx = tx * this.body.friction; + fy = ty * this.body.friction; + + b = 1 + this.body.bounce; + + bx = (nx * b); + by = (ny * b); + + if (dx === 1) + { + this.body.touching.left = true; + } + else if (dx === -1) + { + this.body.touching.right = true; + } + + if (dy === 1) + { + this.body.touching.up = true; + } + else if (dy === -1) + { + this.body.touching.down = true; + } + } + else + { + // Moving out of collision, do not apply forces + bx = by = fx = fy = 0; + } + + // Project object out of collision + p.x += px; + p.y += py; + + // Apply bounce+friction impulses which alter velocity + o.x += px + bx + fx; + o.y += py + by + fy; + }, /** diff --git a/src/physics/ninja/World.js b/src/physics/ninja/World.js index 0a10d537..d6aee8d9 100644 --- a/src/physics/ninja/World.js +++ b/src/physics/ninja/World.js @@ -457,7 +457,12 @@ Phaser.Physics.Ninja.prototype = { if (body1.type !== Phaser.Physics.NINJA || body2.type !== Phaser.Physics.NINJA) { - return; + return false; + } + + if (body1.aabb && body2.aabb) + { + return body1.aabb.collideAABBVsAABB(body2.aabb); } if (body1.aabb && body2.tile)