diff --git a/Gruntfile.js b/Gruntfile.js index 0ca79839..0a5d314d 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -180,14 +180,26 @@ module.exports = function (grunt) { 'src/utils/Debug.js', 'src/utils/Color.js', - 'src/physics/World.js', - 'src/physics/PointProxy.js', - 'src/physics/InversePointProxy.js', - 'src/physics/Body.js', - 'src/physics/Spring.js', - 'src/physics/Material.js', - 'src/physics/ContactMaterial.js', - 'src/physics/CollisionGroup.js', + '/src/physics/Physics.js', + + '/src/physics/arcade/World.js', + '/src/physics/arcade/Body.js', + '/src/physics/arcade/QuadTree.js', + + '/src/physics/ninja/World.js', + '/src/physics/ninja/Body.js', + '/src/physics/ninja/AABB.js', + '/src/physics/ninja/Tile.js', + '/src/physics/ninja/Circle.js', + + '/src/physics/p2/World.js', + '/src/physics/p2/PointProxy.js', + '/src/physics/p2/InversePointProxy.js', + '/src/physics/p2/Body.js', + '/src/physics/p2/Spring.js', + '/src/physics/p2/Material.js', + '/src/physics/p2/ContactMaterial.js', + '/src/physics/p2/CollisionGroup.js', 'src/particles/Particles.js', 'src/particles/arcade/ArcadeParticles.js', diff --git a/README.md b/README.md index 9634ff9a..83001d7c 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ By Richard Davey, [Photon Storm](http://www.photonstorm.com) * Join the [Forum](http://www.html5gamedevs.com/forum/14-phaser/) * Try out 220+ [Phaser Examples](http://examples.phaser.io) * Read the [documentation online](http://docs.phaser.io) +* Join our [#phaserio IRC channel](http://www.html5gamedevs.com/topic/4470-official-phaserio-irc-channel-phaserio-on-freenode/) on freenode [Subscribe to our new Phaser Newsletter](https://confirmsubscription.com/h/r/369DE48E3E86AF1E). We'll email you when new versions are released as well as send you our regular Phaser game making magazine. diff --git a/build/config.php b/build/config.php index 1281d836..ae12bc0e 100644 --- a/build/config.php +++ b/build/config.php @@ -141,6 +141,12 @@ + + + + + + diff --git a/examples/assets/physics/ninja-tiles.png b/examples/assets/physics/ninja-tiles.png new file mode 100644 index 00000000..0fb0024d Binary files /dev/null and b/examples/assets/physics/ninja-tiles.png differ diff --git a/examples/assets/pics/mighty_no_09_cover_art_by_robduenas.jpg b/examples/assets/pics/mighty_no_09_cover_art_by_robduenas.jpg new file mode 100644 index 00000000..14134245 Binary files /dev/null and b/examples/assets/pics/mighty_no_09_cover_art_by_robduenas.jpg differ diff --git a/examples/wip/ninja1.js b/examples/wip/ninja1.js new file mode 100644 index 00000000..8ec203c8 --- /dev/null +++ b/examples/wip/ninja1.js @@ -0,0 +1,122 @@ + +// 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.spritesheet('ninja-tiles', 'assets/physics/ninja-tiles.png', 128, 128, 34); + game.load.image('a', 'assets/sprites/firstaid.png'); + +} + +var sprite1; +var cursors; + +var tile1; +var tile2; + +var t; +var running = false; + +function create() { + + game.stage.smoothed = true; + + // Activate the Ninja physics system + game.physics.startSystem(Phaser.Physics.NINJA); + + // game.physics.ninja.gravity = 0.1; + + sprite1 = game.add.sprite(500, 200, 'a'); + + // Enable the physics body for the Ninja physics system + // By default it will create an AABB body for the sprite + game.physics.ninja.enableAABB(sprite1); + + // But you can change it to either a Tile or a Circle + tile1 = game.add.sprite(0, 550, 'ninja-tiles', 14); + tile1.width = 100; + tile1.height = 100; + + game.physics.ninja.enableTile(tile1, 14); + + // sprite1.body.aabb.friction = 0; + + // tile1 = new Phaser.Physics.Ninja.Tile(game.physics.ninja, 100, 500, 100, 100, Phaser.Physics.Ninja.Tile.SLOPE_45DEGpn); + // tile1 = new Phaser.Physics.Ninja.Tile(game.physics.ninja, 100, 500, 100, 100, 15); + // tile2 = new Phaser.Physics.Ninja.Tile(game.physics.ninja, 300, 500, 100, 100, 7); + + cursors = game.input.keyboard.createCursorKeys(); + +} + +function collisionHandler() { + game.stage.backgroundColor = 0xff0000; +} + +function update() { + + game.physics.ninja.collide(sprite1, tile1, collisionHandler, null, this); + + tile1.body.moveRight(1); + + /* + if (cursors.up.isDown && !running) + { + running = true; + t = Date.now(); + } + + sprite1.body.setZeroVelocity(); + + if (running) + { + sprite1.body.moveRight(100); + + if (sprite1.body.x >= 200) + { + var ms = Date.now() - t; + console.log('100px in ', ms); + running = false; + sprite1.body.setZeroVelocity(); + } + } + */ + + // sprite1.body.setZeroVelocity(); + + if (cursors.left.isDown) + { + sprite1.body.moveLeft(20); + } + else if (cursors.right.isDown) + { + sprite1.body.moveRight(20); + } + + if (cursors.up.isDown) + { + sprite1.body.moveUp(20); + } + else if (cursors.down.isDown) + { + sprite1.body.moveUp(20); + } + +} + +function render() { + + game.debug.text(sprite1.body.shape.velocity.x, 32, 32); + game.debug.text(sprite1.body.shape.velocity.y, 32, 64); + game.debug.text(game.math.radToDeg(sprite1.body.angle), 32, 96); + + // tile1.render(game.context, 'ninja-tiles'); + // tile2.render(game.context, 'ninja-tiles'); + + // game.debug.geom(sprite1.body, 'rgba(0,255,0,0.4)', true, 1); + + // game.debug.geom(tile1, 'rgba(0,255,0,0.4)', true, 1); + // game.debug.geom(tile1, 'rgba(0,255,0,0.4)', true, 1); + +} diff --git a/examples/wip/physics-1.js b/examples/wip/physics-1.js index b50fd293..dc688219 100644 --- a/examples/wip/physics-1.js +++ b/examples/wip/physics-1.js @@ -19,6 +19,7 @@ function create() { sprite1 = game.add.sprite(50, 200, 'atari'); sprite1.name = 'atari'; + // sprite1.anchor.set(0.5); sprite2 = game.add.sprite(700, 220, 'mushroom'); sprite2.name = 'mushroom'; @@ -37,6 +38,9 @@ function update() { // object1, object2, collideCallback, processCallback, callbackContext game.physics.arcade.collide(sprite1, sprite2, collisionHandler, null, this); + // var b = sprite1.getBounds(); + // console.log(b); + } function collisionHandler (obj1, obj2) { @@ -46,9 +50,14 @@ function collisionHandler (obj1, obj2) { } + function render() { - // game.debug.physicsBody(sprite1.body); - // game.debug.physicsBody(sprite2.body); + // var b = sprite1.getBounds(); + + // game.debug.geom(b, 'rgba(255,0,0,0.8)', true, 1); + + game.debug.geom(sprite1.body, 'rgba(0,255,0,0.4)', true, 1); + game.debug.geom(sprite2.body, 'rgba(0,255,0,0.4)', true, 1); } \ No newline at end of file diff --git a/examples/wip/sci-fly2.js b/examples/wip/sci-fly2.js index cdc82363..2bb97d3e 100644 --- a/examples/wip/sci-fly2.js +++ b/examples/wip/sci-fly2.js @@ -52,7 +52,7 @@ function create() { game.physics.enable(sprite); - sprite.body.setSize(14, 14, 2, 0); + // sprite.body.setSize(14, 14, 2, 0); console.log(sprite.body); @@ -107,6 +107,8 @@ function render() { // game.debug.text(game.physics.arcade._intersection.width, 32, 32); // game.debug.text(game.physics.arcade._intersection.height, 32, 64); + game.debug.geom(sprite.body, 'rgba(0,255,0,0.4)', true, 1); + game.debug.text(sprite.body.overlapX, 32, 32); game.debug.text(sprite.body.overlapY, 32, 64); diff --git a/examples/wip/tilemap new 1.js b/examples/wip/tilemap new 1.js new file mode 100644 index 00000000..d630b9c9 --- /dev/null +++ b/examples/wip/tilemap new 1.js @@ -0,0 +1,77 @@ + +var game = new Phaser.Game(800, 600, Phaser.CANVAS, 'phaser-example', { preload: preload, create: create, update: update, render: render }); + +function preload() { + + game.load.tilemap('map', 'assets/tilemaps/maps/collision_test.json', null, Phaser.Tilemap.TILED_JSON); + game.load.image('ground_1x1', 'assets/tilemaps/tiles/ground_1x1.png'); + game.load.image('phaser', 'assets/sprites/phaser-dude.png'); + // game.load.image('phaser', 'assets/sprites/phaser-ship.png'); + +} + +var map; +var layer; +var cursors; +var sprite; + +function create() { + + map = game.add.tilemap('map'); + + map.addTilesetImage('ground_1x1'); + + layer = map.createLayer('Tile Layer 1'); + + layer.resizeWorld(); + + map.setCollisionBetween(1, 12); + + layer.debug = true; + + sprite = game.add.sprite(260, 70, 'phaser'); + + game.physics.enable(sprite); + + game.camera.follow(sprite); + + // game.physics.arcade.gravity.y = 500; + // sprite.body.velocity.x = 100; + + cursors = game.input.keyboard.createCursorKeys(); + +} + +function update() { + + game.physics.arcade.collide(sprite, layer); + + sprite.body.velocity.x = 0; + sprite.body.velocity.y = 0; + + if (cursors.up.isDown) + { + sprite.body.velocity.y = -100; + } + else if (cursors.down.isDown) + { + sprite.body.velocity.y = 100; + } + + if (cursors.left.isDown) + { + sprite.body.velocity.x = -100; + } + else if (cursors.right.isDown) + { + sprite.body.velocity.x = 100; + } + +} + +function render() { + + game.debug.text(sprite.body.velocity.x, 32, 32); + game.debug.text(sprite.body.velocity.y, 64, 32); + +} diff --git a/examples/wip/tilesprite.js b/examples/wip/tilesprite.js index 377604ad..c5d147c6 100644 --- a/examples/wip/tilesprite.js +++ b/examples/wip/tilesprite.js @@ -21,6 +21,7 @@ function create() { // sprite = game.add.tileSprite(100, 100, 400, 300, 'starfield'); sprite = game.add.tileSprite(100, 100, 400, 300, 'mummy'); + sprite.pivot.setTo(200, 200); sprite.animations.add('walk'); @@ -32,6 +33,8 @@ function create() { function update() { + sprite.rotation += 0.01; + if (cursors.left.isDown) { sprite.tilePosition.x += 8; diff --git a/resources/Ninja Physics Debug Tiles/0.png b/resources/Ninja Physics Debug Tiles/0.png new file mode 100644 index 00000000..e590060f Binary files /dev/null and b/resources/Ninja Physics Debug Tiles/0.png differ diff --git a/resources/Ninja Physics Debug Tiles/1.png b/resources/Ninja Physics Debug Tiles/1.png new file mode 100644 index 00000000..df66b19d Binary files /dev/null and b/resources/Ninja Physics Debug Tiles/1.png differ diff --git a/resources/Ninja Physics Debug Tiles/10.png b/resources/Ninja Physics Debug Tiles/10.png new file mode 100644 index 00000000..63531664 Binary files /dev/null and b/resources/Ninja Physics Debug Tiles/10.png differ diff --git a/resources/Ninja Physics Debug Tiles/11.png b/resources/Ninja Physics Debug Tiles/11.png new file mode 100644 index 00000000..2858d7c4 Binary files /dev/null and b/resources/Ninja Physics Debug Tiles/11.png differ diff --git a/resources/Ninja Physics Debug Tiles/12.png b/resources/Ninja Physics Debug Tiles/12.png new file mode 100644 index 00000000..60a6a47a Binary files /dev/null and b/resources/Ninja Physics Debug Tiles/12.png differ diff --git a/resources/Ninja Physics Debug Tiles/13.png b/resources/Ninja Physics Debug Tiles/13.png new file mode 100644 index 00000000..ce3b813e Binary files /dev/null and b/resources/Ninja Physics Debug Tiles/13.png differ diff --git a/resources/Ninja Physics Debug Tiles/14.png b/resources/Ninja Physics Debug Tiles/14.png new file mode 100644 index 00000000..e58665f9 Binary files /dev/null and b/resources/Ninja Physics Debug Tiles/14.png differ diff --git a/resources/Ninja Physics Debug Tiles/15.png b/resources/Ninja Physics Debug Tiles/15.png new file mode 100644 index 00000000..93b34523 Binary files /dev/null and b/resources/Ninja Physics Debug Tiles/15.png differ diff --git a/resources/Ninja Physics Debug Tiles/16.png b/resources/Ninja Physics Debug Tiles/16.png new file mode 100644 index 00000000..c5c25ecb Binary files /dev/null and b/resources/Ninja Physics Debug Tiles/16.png differ diff --git a/resources/Ninja Physics Debug Tiles/17.png b/resources/Ninja Physics Debug Tiles/17.png new file mode 100644 index 00000000..8eaf55a6 Binary files /dev/null and b/resources/Ninja Physics Debug Tiles/17.png differ diff --git a/resources/Ninja Physics Debug Tiles/18.png b/resources/Ninja Physics Debug Tiles/18.png new file mode 100644 index 00000000..05fff19d Binary files /dev/null and b/resources/Ninja Physics Debug Tiles/18.png differ diff --git a/resources/Ninja Physics Debug Tiles/19.png b/resources/Ninja Physics Debug Tiles/19.png new file mode 100644 index 00000000..3b781548 Binary files /dev/null and b/resources/Ninja Physics Debug Tiles/19.png differ diff --git a/resources/Ninja Physics Debug Tiles/2.png b/resources/Ninja Physics Debug Tiles/2.png new file mode 100644 index 00000000..d48acecf Binary files /dev/null and b/resources/Ninja Physics Debug Tiles/2.png differ diff --git a/resources/Ninja Physics Debug Tiles/20.png b/resources/Ninja Physics Debug Tiles/20.png new file mode 100644 index 00000000..c239a8d7 Binary files /dev/null and b/resources/Ninja Physics Debug Tiles/20.png differ diff --git a/resources/Ninja Physics Debug Tiles/21.png b/resources/Ninja Physics Debug Tiles/21.png new file mode 100644 index 00000000..8cd38663 Binary files /dev/null and b/resources/Ninja Physics Debug Tiles/21.png differ diff --git a/resources/Ninja Physics Debug Tiles/22.png b/resources/Ninja Physics Debug Tiles/22.png new file mode 100644 index 00000000..fff81de9 Binary files /dev/null and b/resources/Ninja Physics Debug Tiles/22.png differ diff --git a/resources/Ninja Physics Debug Tiles/23.png b/resources/Ninja Physics Debug Tiles/23.png new file mode 100644 index 00000000..328585d0 Binary files /dev/null and b/resources/Ninja Physics Debug Tiles/23.png differ diff --git a/resources/Ninja Physics Debug Tiles/24.png b/resources/Ninja Physics Debug Tiles/24.png new file mode 100644 index 00000000..3ea04c52 Binary files /dev/null and b/resources/Ninja Physics Debug Tiles/24.png differ diff --git a/resources/Ninja Physics Debug Tiles/25.png b/resources/Ninja Physics Debug Tiles/25.png new file mode 100644 index 00000000..421ab753 Binary files /dev/null and b/resources/Ninja Physics Debug Tiles/25.png differ diff --git a/resources/Ninja Physics Debug Tiles/26.png b/resources/Ninja Physics Debug Tiles/26.png new file mode 100644 index 00000000..aa1bf958 Binary files /dev/null and b/resources/Ninja Physics Debug Tiles/26.png differ diff --git a/resources/Ninja Physics Debug Tiles/27.png b/resources/Ninja Physics Debug Tiles/27.png new file mode 100644 index 00000000..f42cd4b7 Binary files /dev/null and b/resources/Ninja Physics Debug Tiles/27.png differ diff --git a/resources/Ninja Physics Debug Tiles/28.png b/resources/Ninja Physics Debug Tiles/28.png new file mode 100644 index 00000000..2c69e216 Binary files /dev/null and b/resources/Ninja Physics Debug Tiles/28.png differ diff --git a/resources/Ninja Physics Debug Tiles/29.png b/resources/Ninja Physics Debug Tiles/29.png new file mode 100644 index 00000000..b25589d5 Binary files /dev/null and b/resources/Ninja Physics Debug Tiles/29.png differ diff --git a/resources/Ninja Physics Debug Tiles/3.png b/resources/Ninja Physics Debug Tiles/3.png new file mode 100644 index 00000000..9d998dda Binary files /dev/null and b/resources/Ninja Physics Debug Tiles/3.png differ diff --git a/resources/Ninja Physics Debug Tiles/30.png b/resources/Ninja Physics Debug Tiles/30.png new file mode 100644 index 00000000..c908b775 Binary files /dev/null and b/resources/Ninja Physics Debug Tiles/30.png differ diff --git a/resources/Ninja Physics Debug Tiles/31.png b/resources/Ninja Physics Debug Tiles/31.png new file mode 100644 index 00000000..f4bea611 Binary files /dev/null and b/resources/Ninja Physics Debug Tiles/31.png differ diff --git a/resources/Ninja Physics Debug Tiles/32.png b/resources/Ninja Physics Debug Tiles/32.png new file mode 100644 index 00000000..29daee68 Binary files /dev/null and b/resources/Ninja Physics Debug Tiles/32.png differ diff --git a/resources/Ninja Physics Debug Tiles/33.png b/resources/Ninja Physics Debug Tiles/33.png new file mode 100644 index 00000000..20b48b35 Binary files /dev/null and b/resources/Ninja Physics Debug Tiles/33.png differ diff --git a/resources/Ninja Physics Debug Tiles/4.png b/resources/Ninja Physics Debug Tiles/4.png new file mode 100644 index 00000000..74058839 Binary files /dev/null and b/resources/Ninja Physics Debug Tiles/4.png differ diff --git a/resources/Ninja Physics Debug Tiles/5.png b/resources/Ninja Physics Debug Tiles/5.png new file mode 100644 index 00000000..23699d94 Binary files /dev/null and b/resources/Ninja Physics Debug Tiles/5.png differ diff --git a/resources/Ninja Physics Debug Tiles/6.png b/resources/Ninja Physics Debug Tiles/6.png new file mode 100644 index 00000000..ae6ae39a Binary files /dev/null and b/resources/Ninja Physics Debug Tiles/6.png differ diff --git a/resources/Ninja Physics Debug Tiles/7.png b/resources/Ninja Physics Debug Tiles/7.png new file mode 100644 index 00000000..b54798c2 Binary files /dev/null and b/resources/Ninja Physics Debug Tiles/7.png differ diff --git a/resources/Ninja Physics Debug Tiles/8.png b/resources/Ninja Physics Debug Tiles/8.png new file mode 100644 index 00000000..136f70a2 Binary files /dev/null and b/resources/Ninja Physics Debug Tiles/8.png differ diff --git a/resources/Ninja Physics Debug Tiles/9.png b/resources/Ninja Physics Debug Tiles/9.png new file mode 100644 index 00000000..62d77de1 Binary files /dev/null and b/resources/Ninja Physics Debug Tiles/9.png differ diff --git a/resources/Ninja Physics Debug Tiles/ninja-tiles.png b/resources/Ninja Physics Debug Tiles/ninja-tiles.png new file mode 100644 index 00000000..0fb0024d Binary files /dev/null and b/resources/Ninja Physics Debug Tiles/ninja-tiles.png differ diff --git a/src/gameobjects/Sprite.js b/src/gameobjects/Sprite.js index b7f75483..bdad316d 100644 --- a/src/gameobjects/Sprite.js +++ b/src/gameobjects/Sprite.js @@ -99,7 +99,15 @@ Phaser.Sprite = function (game, x, y, key, frame) { this.input = null; /** - * @property {Phaser.Physics.Arcade.Body|Phaser.Physics.P2.Body|null} body - The Sprites physics Body. Will be null unless physics has been enabled via `Sprite.physicsEnabled = true`. + * By default Sprites won't add themselves to any physics system and their physics body will be `null`. + * To enable them for physics you need to call `game.physics.enable(sprite, system)` where `sprite` is this object + * and `system` is the Physics system you want to use to manage this body. Once enabled you can access all physics related properties via `Sprite.body`. + * + * Important: Enabling a Sprite for P2 or Ninja physics will automatically set `Sprite.anchor` to 0.5 so the physics body is centered on the Sprite. + * If you need a different result then adjust or re-create the Body shape offsets manually, and/or reset the anchor after enabling physics. + * + * @property {Phaser.Physics.Arcade.Body|Phaser.Physics.P2.Body|Phaser.Physics.Ninja.Body|null} body + * @default */ this.body = null; @@ -850,46 +858,6 @@ Object.defineProperty(Phaser.Sprite.prototype, "inputEnabled", { }); -/** -* By default Sprites won't add themselves to the physics simulation. By setting physicsEnabled to true a Rectangle physics body is -* attached to this Sprite matching its placement and dimensions, and will then start to process physics world updates. -* You can access all physics related properties via Sprite.body. -* -* Important: Enabling a Sprite for physics will automatically set `Sprite.anchor` to 0.5 so the physics body is centered on the Sprite. -* If you need a different result then adjust or re-create the Body shape offsets manually, and/or reset the anchor after enabling physics. -* -* @name Phaser.Sprite#physicsEnabled -* @property {boolean} physicsEnabled - Set to true to add this Sprite to the physics world. Set to false to destroy the body and remove it from the physics world. -Object.defineProperty(Phaser.Sprite.prototype, "physicsEnabled", { - - get: function () { - - return (this.body !== null); - - }, - - set: function (value) { - - if (value) - { - if (this.body === null) - { - this.body = new Phaser.Physics.Body(this.game, this, this.x, this.y, 1); - this.anchor.set(0.5); - } - } - else - { - if (this.body) - { - this.body.destroy(); - } - } - } - -}); -*/ - /** * Sprite.exists controls if the core game loop and physics update this Sprite or not. * When you set Sprite.exists to false it will remove its Body from the physics world (if it has one) and also set Sprite.visible to false. @@ -913,7 +881,7 @@ Object.defineProperty(Phaser.Sprite.prototype, "exists", { // exists = true this._cache[6] = 1; - if (this.body) + if (this.body && this.body.type === Phaser.Physics.P2) { this.body.addToWorld(); } @@ -925,7 +893,7 @@ Object.defineProperty(Phaser.Sprite.prototype, "exists", { // exists = false this._cache[6] = 0; - if (this.body) + if (this.body && this.body.type === Phaser.Physics.P2) { this.body.removeFromWorld(); } diff --git a/src/input/Gestures.js b/src/input/Gestures.js index f5269c33..f6be209f 100644 --- a/src/input/Gestures.js +++ b/src/input/Gestures.js @@ -1,6 +1,6 @@ /** * @author Antony Woods -* @copyright 2013 Photon Storm Ltd. +* @copyright 2014 Photon Storm Ltd. * @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License} */ diff --git a/src/physics/Physics.js b/src/physics/Physics.js index d275a52a..d346bf01 100644 --- a/src/physics/Physics.js +++ b/src/physics/Physics.js @@ -5,12 +5,15 @@ */ /** -* The Physics manager. +* The Physics Manager is responsible for looking after all of the running physics systems. +* Phaser supports 3 physics systems: Arcade Physics, P2 and Ninja Physics (with Box2D and Chipmunk in development) +* Game Objects can belong to only 1 physics system, but you can have multiple systems active in a single game. +* +* For example you could have P2 managing a polygon-built terrain landscape that an vehicle drives over, while it could be firing bullets that use the +* faster (due to being much simpler) Arcade Physics system. * * @class Phaser.Physics * -* @classdesc todo -* * @constructor * @param {Phaser.Game} game - A reference to the currently running game. * @param {object} [physicsConfig=null] - A physics configuration object to pass to the Physics world on creation. @@ -22,14 +25,34 @@ Phaser.Physics = function (game, config) { */ this.game = game; + /** + * @property {object} config - The physics configuration object as passed to the game on creation. + */ this.config = config; + /** + * @property {Phaser.Physics.Arcade} arcade - The Arcade Physics system. + */ this.arcade = new Phaser.Physics.Arcade(game); + /** + * @property {Phaser.Physics.P2} p2 - The P2.JS Physics system. + */ this.p2 = null; + /** + * @property {Phaser.Physics.Ninja} ninja - The N+ Ninja Physics System. + */ + this.ninja = null; + + /** + * @property {Phaser.Physics.Box2D} box2d - The Box2D Physics system (to be done). + */ this.box2d = null; + /** + * @property {Phaser.Physics.Chipmunk} chipmunk - The Chipmunk Physics system (to be done). + */ this.chipmunk = null; }; @@ -50,38 +73,72 @@ Phaser.Physics.P2 = 1; * @const * @type {number} */ -Phaser.Physics.BOX2D = 2; +Phaser.Physics.NINJA = 2; /** * @const * @type {number} */ -Phaser.Physics.CHIPMUNK = 3; +Phaser.Physics.BOX2D = 3; + +/** +* @const +* @type {number} +*/ +Phaser.Physics.CHIPMUNK = 5; Phaser.Physics.prototype = { + /** + * This will create an instance of the requested physics simulation. + * Phaser.Physics.Arcade is running by default, but all others need activating directly. + * You can start the following physics systems: + * Phaser.Physics.P2 - A full-body advanced physics system by Stefan Hedman. + * Phaser.Physics.NINJA - A port of the metanet N+ physics system. + * Phaser.Physics.BOX2D and Phaser.Physics.CHIPMUNK are still in development. + * + * @method Phaser.Physics#startSystem + * @param {number} The physics system to start. + */ startSystem: function (system) { - if (system === Phaser.Physics.ARCADE) + if (system === Phaser.Physics.ARCADE && this.arcade === null) { this.arcade = new Phaser.Physics.Arcade(this.game); } - else if (system === Phaser.Physics.P2) + else if (system === Phaser.Physics.P2 && this.p2 === null) { this.p2 = new Phaser.Physics.P2(this.game, this.config); } - else if (system === Phaser.Physics.BOX2D) + if (system === Phaser.Physics.NINJA && this.ninja === null) + { + this.ninja = new Phaser.Physics.Ninja(this.game); + } + else if (system === Phaser.Physics.BOX2D && this.box2d === null) { // Coming soon } - else if (system === Phaser.Physics.CHIPMUNK) + else if (system === Phaser.Physics.CHIPMUNK && this.chipmunk === null) { // Coming soon } }, - // Enables a sprites physics body + /** + * This will create a physics body on the given game object. + * A game object can only have 1 physics body active at any one time, and it can't be changed until the object is destroyed. + * It can be for any of the physics systems that have been started: + * + * Phaser.Physics.Arcade - A light weight AABB based collision system with basic separation. + * Phaser.Physics.P2 - A full-body advanced physics system supporting multiple object shapes, polygon loading, contact materials, springs and constraints. + * Phaser.Physics.NINJA - A port of the metanet N+ physics system. Advanced AABB and Circle vs. Tile collision. + * Phaser.Physics.BOX2D and Phaser.Physics.CHIPMUNK are still in development. + * + * @method Phaser.Physics#enable + * @param {object|array} object - The game object to create the physics body on. Can also be an array of objects, a body will be created on every object in the array. + * @param {number} [system=0] - The physics system that will be used to create the body. Defaults to Arcade Physics. + */ enable: function (object, system) { if (typeof system === 'undefined') { system = Phaser.Physics.ARCADE; } @@ -111,14 +168,25 @@ Phaser.Physics.prototype = { object[i].body = new Phaser.Physics.P2.Body(this.game, object[i], object[i].x, object[i].y, 1); object[i].anchor.set(0.5); } + else if (system === Phaser.Physics.NINJA) + { + object[i].body = new Phaser.Physics.Ninja.Body(this.ninja, object[i]); + object[i].anchor.set(0.5); + } } } }, + /** + * Updates all running physics systems. + * + * @method Phaser.Physics#update + * @protected + */ update: function () { - // ArcadePhysics doesn't have a core to update + // ArcadePhysics / Ninja don't have a core to update if (this.p2) { @@ -127,10 +195,22 @@ Phaser.Physics.prototype = { }, + /** + * Updates the physics bounds to match the world dimensions. + * + * @method Phaser.Physics#setBoundsToWorld + * @protected + */ setBoundsToWorld: function () { }, + /** + * Clears down all active physics systems. This doesn't destroy them, it just clears them of objects and is called when the State changes. + * + * @method Phaser.Physics#clear + * @protected + */ clear: function () { if (this.p2) diff --git a/src/physics/arcade/BrokenArcadePhysics.js b/src/physics/arcade/ArcadePhysics114.js similarity index 100% rename from src/physics/arcade/BrokenArcadePhysics.js rename to src/physics/arcade/ArcadePhysics114.js diff --git a/src/physics/arcade/Body.js b/src/physics/arcade/Body.js index 3c99b627..3227ecc6 100644 --- a/src/physics/arcade/Body.js +++ b/src/physics/arcade/Body.js @@ -1,6 +1,6 @@ /** * @author Richard Davey -* @copyright 2013 Photon Storm Ltd. +* @copyright 2014 Photon Storm Ltd. * @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License} */ @@ -25,34 +25,27 @@ Phaser.Physics.Arcade.Body = function (sprite) { */ this.game = sprite.game; + /** + * @property {number} type - The type of physics system this body belongs to. + */ + this.type = Phaser.Physics.ARCADE; + /** * @property {Phaser.Point} offset - The offset of the Physics Body from the Sprite x/y position. */ - this.offset = new Phaser.Point(); + this.offset = new Phaser.Point(-(sprite.anchor.x * sprite.width), -(sprite.anchor.y * sprite.height)); /** - * @property {number} x - The x position of the physics body. + * @property {Phaser.Point} position - The position of the physics body. * @readonly */ - this.x = sprite.x; + this.position = new Phaser.Point(sprite.x + this.offset.x, sprite.y + this.offset.y); /** - * @property {number} y - The y position of the physics body. + * @property {Phaser.Point} prev - The previous position of the physics body. * @readonly */ - this.y = sprite.y; - - /** - * @property {number} preX - The previous x position of the physics body. - * @readonly - */ - this.preX = sprite.x; - - /** - * @property {number} preY - The previous y position of the physics body. - * @readonly - */ - this.preY = sprite.y; + this.prev = new Phaser.Point(this.position.x, this.position.y); /** * @property {number} preRotation - The previous rotation of the physics body. @@ -60,18 +53,6 @@ Phaser.Physics.Arcade.Body = function (sprite) { */ this.preRotation = sprite.angle; - /** - * @property {number} screenX - The x position of the physics body translated to screen space. - * @readonly - */ - this.screenX = sprite.x; - - /** - * @property {number} screenY - The y position of the physics body translated to screen space. - * @readonly - */ - this.screenY = sprite.y; - /** * @property {number} sourceWidth - The un-scaled original size. * @readonly @@ -97,17 +78,17 @@ Phaser.Physics.Arcade.Body = function (sprite) { /** * @property {number} halfWidth - The calculated width / 2 of the physics body. */ - this.halfWidth = Math.floor(sprite.width / 2); + this.halfWidth = Math.abs(sprite.width / 2); /** * @property {number} halfHeight - The calculated height / 2 of the physics body. */ - this.halfHeight = Math.floor(sprite.height / 2); + this.halfHeight = Math.abs(sprite.height / 2); /** * @property {Phaser.Point} center - The center coordinate of the Physics Body. */ - this.center = new Phaser.Point(this.x + this.halfWidth, this.y + this.halfHeight); + this.center = new Phaser.Point(sprite.x + this.halfWidth, sprite.y + this.halfHeight); /** * @property {number} _sx - Internal cache var. @@ -126,6 +107,12 @@ Phaser.Physics.Arcade.Body = function (sprite) { */ this.velocity = new Phaser.Point(); + /** + * @property {Phaser.Point} newVelocity - New velocity. + * @readonly + */ + this.newVelocity = new Phaser.Point(0, 0); + /** * @property {Phaser.Point} acceleration - The velocity in pixels per second sq. of the Body. */ @@ -137,9 +124,9 @@ Phaser.Physics.Arcade.Body = function (sprite) { this.drag = new Phaser.Point(); /** - * @property {Phaser.Point} gravity - A private Gravity setting for the Body. + * @property {Phaser.Point} gravityScale - Gravity scaling factor. If you want the body to ignore gravity, set this to zero. If you want to reverse gravity, set it to -1. */ - this.gravity = new Phaser.Point(); + this.gravityScale = new Phaser.Point(1, 1); /** * @property {Phaser.Point} bounce - The elasticitiy of the Body when colliding. bounce.x/y = 1 means full rebound, bounce.x/y = 0.5 means 50% rebound velocity. @@ -218,12 +205,6 @@ Phaser.Physics.Arcade.Body = function (sprite) { */ this.allowRotation = true; - /** - * @property {boolean} allowGravity - Allow this Body to be influenced by the global Gravity? - * @default - */ - this.allowGravity = true; - /** * This flag allows you to disable the custom x separation that takes place by Physics.Arcade.separate. * Used in combination with your own collision processHandler you can create whatever type of collision response you need. @@ -252,18 +233,6 @@ Phaser.Physics.Arcade.Body = function (sprite) { */ this.overlapY = 0; - this.hull = new Phaser.Rectangle(); - - /** - * @property {Phaser.Rectangle} hullX - The dynamically calculated hull used during collision. - */ - this.hullX = new Phaser.Rectangle(); - - /** - * @property {Phaser.Rectangle} hullY - The dynamically calculated hull used during collision. - */ - this.hullY = new Phaser.Rectangle(); - /** * If a body is overlapping with another body, but neither of them are moving (maybe they spawned on-top of each other?) this is set to true. * @property {boolean} embedded - Body embed value. @@ -303,10 +272,29 @@ Phaser.Physics.Arcade.Body = function (sprite) { */ this.blocked = { x: 0, y: 0, up: false, down: false, left: false, right: false }; + /** + * This object is populated with boolean values when the Body collides with the World bounds or a Tile. + * For example if blocked.up is true then the Body cannot move up. + * @property {object} blocked - An object containing on which faces this Body is blocked from moving, if any. + */ + this.result = { x: 0, y: 0, px: 0, py: 0, tx: 0, ty: 0, slope: false }; + }; Phaser.Physics.Arcade.Body.prototype = { + resetResult: function () { + + this.result.x = false; + this.result.y = false; + this.result.slope = false; + this.result.px = this.position.x; + this.result.py = this.position.y; + this.result.tx = 0; + this.result.ty = 0; + + }, + /** * Internal method. * @@ -336,6 +324,8 @@ Phaser.Physics.Arcade.Body.prototype = { */ preUpdate: function () { + this.resetResult(); + // Store and reset collision flags this.wasTouching.none = this.touching.none; this.wasTouching.up = this.touching.up; @@ -354,27 +344,41 @@ Phaser.Physics.Arcade.Body.prototype = { // this.screenX = (this.sprite.worldTransform[2] - (this.sprite.anchor.x * this.width)) + this.offset.x; // this.screenY = (this.sprite.worldTransform[5] - (this.sprite.anchor.y * this.height)) + this.offset.y; - this.preX = (this.sprite.world.x - (this.sprite.anchor.x * this.width)) + this.offset.x; - this.preY = (this.sprite.world.y - (this.sprite.anchor.y * this.height)) + this.offset.y; + // this is where the Sprite currently is, in world coordinates + // this.preX = (this.sprite.world.x - (this.sprite.anchor.x * this.width)) + this.offset.x; + // this.preY = (this.sprite.world.y - (this.sprite.anchor.y * this.height)) + this.offset.y; + // this.preRotation = this.sprite.angle; - this.preRotation = this.sprite.angle; - - this.x = this.preX; - this.y = this.preY; - this.rotation = this.preRotation; + // this.x = this.preX; + // this.y = this.preY; + // this.rotation = this.preRotation; // this.overlapX = 0; // this.overlapY = 0; - this.blocked.up = false; - this.blocked.down = false; - this.blocked.left = false; - this.blocked.right = false; + this.prev.set(this.position.x, this.position.y); + + // this.position.x = (this.sprite.world.x - (this.sprite.anchor.x * this.width)) + this.offset.x; + // this.position.y = (this.sprite.world.y - (this.sprite.anchor.y * this.height)) + this.offset.y; + + + // this.blocked.up = false; + // this.blocked.down = false; + // this.blocked.left = false; + // this.blocked.right = false; if (this.moves) { this.game.physics.arcade.updateMotion(this); + this.newVelocity.set(this.velocity.x * this.game.time.physicsElapsed, this.velocity.y * this.game.time.physicsElapsed); + + this.position.x += this.newVelocity.x; + this.position.y += this.newVelocity.y; + + // Now the State update will throw collision checks at the Body + // And finally we'll integrate the new position back to the Sprite in postUpdate + if (this.collideWorldBounds) { this.checkWorldBounds(); @@ -391,6 +395,56 @@ Phaser.Physics.Arcade.Body.prototype = { */ postUpdate: function () { + // if (this.result.x) + // { + // this.position.x = this.result.px; + // this.velocity.x = 0; + // } + // else + // { + // this.position.x += this.newVelocity.x; + // } + + // this.position.y += this.newVelocity.y; + + // this.position.add(this.newVelocity.x, this.newVelocity.y); + + this.sprite.x = (this.position.x - this.offset.x); + this.sprite.y = (this.position.y - this.offset.y); + + +/* if (this.deltaX() < 0) + { + this.facing = Phaser.LEFT; + this.sprite.x += this.deltaX(); + } + else if (this.deltaX() > 0) + { + this.facing = Phaser.RIGHT; + this.sprite.x += this.deltaX(); + } + + if (this.deltaY() < 0) + { + this.facing = Phaser.UP; + this.sprite.y += this.deltaY(); + } + else if (this.deltaY() > 0) + { + this.facing = Phaser.DOWN; + this.sprite.y += this.deltaY(); + } +*/ + + // this.sprite.x += this.overlapX; + // this.sprite.y += this.overlapY; + + // this is where the Sprite currently is, in world coordinates + // this.sprite.x = (this.sprite.world.x - (this.sprite.anchor.x * this.width)) + this.offset.x; + // this.sprite.y = (this.sprite.world.y - (this.sprite.anchor.y * this.height)) + this.offset.y; + + + /* if (this.deltaX() < 0 && this.blocked.left === false) { this.facing = Phaser.LEFT; @@ -412,6 +466,7 @@ Phaser.Physics.Arcade.Body.prototype = { this.facing = Phaser.DOWN; this.sprite.y += this.deltaY(); } + */ this.center.setTo(this.x + this.halfWidth, this.y + this.halfHeight); @@ -533,7 +588,7 @@ Phaser.Physics.Arcade.Body.prototype = { * @return {number} The delta value. Positive if the motion was to the right, negative if to the left. */ deltaX: function () { - return this.x - this.preX; + return this.position.x - this.prev.x; }, /** @@ -543,9 +598,15 @@ Phaser.Physics.Arcade.Body.prototype = { * @return {number} The delta value. Positive if the motion was downwards, negative if upwards. */ deltaY: function () { - return this.y - this.preY; + return this.position.y - this.prev.y; }, + /** + * Returns the delta z value. The difference between Body.rotation now and in the previous step. + * + * @method Phaser.Physics.Arcade.Body#deltaZ + * @return {number} The delta value. Positive if the motion was clockwise, negative if anti-clockwise. + */ deltaZ: function () { return this.rotation - this.preRotation; } @@ -555,35 +616,18 @@ Phaser.Physics.Arcade.Body.prototype = { /** * @name Phaser.Physics.Arcade.Body#bottom * @property {number} bottom - The bottom value of this Body (same as Body.y + Body.height) +* @readonly */ Object.defineProperty(Phaser.Physics.Arcade.Body.prototype, "bottom", { /** - * The sum of the y and height properties. Changing the bottom property of a Rectangle object has no effect on the x, y and width properties, but does change the height property. + * The sum of the y and height properties. * @method bottom * @return {number} + * @readonly */ get: function () { - // return Math.floor(this.y + this.height); - return this.y + this.height; - }, - - /** - * The sum of the y and height properties. Changing the bottom property of a Rectangle object has no effect on the x, y and width properties, but does change the height property. - * @method bottom - * @param {number} value - */ - set: function (value) { - - if (value <= this.y) - { - this.height = 0; - } - else - { - this.height = (this.y - value); - } - + return this.position.y + this.height; } }); @@ -591,37 +635,72 @@ Object.defineProperty(Phaser.Physics.Arcade.Body.prototype, "bottom", { /** * @name Phaser.Physics.Arcade.Body#right * @property {number} right - The right value of this Body (same as Body.x + Body.width) +* @readonly */ Object.defineProperty(Phaser.Physics.Arcade.Body.prototype, "right", { /** - * The sum of the x and width properties. Changing the right property of a Rectangle object has no effect on the x, y and height properties. - * However it does affect the width property. + * The sum of the x and width properties. * @method right * @return {number} + * @readonly */ get: function () { - // return Math.floor(this.x + this.width); - return this.x + this.width; - }, - - /** - * The sum of the x and width properties. Changing the right property of a Rectangle object has no effect on the x, y and height properties. - * However it does affect the width property. - * @method right - * @param {number} value - */ - set: function (value) { - - if (value <= this.x) - { - this.width = 0; - } - else - { - this.width = this.x + value; - } - + return this.position.x + this.width; } }); + +/** +* @name Phaser.Physics.Arcade.Body#x +* @property {number} x - The x position. +*/ +Object.defineProperty(Phaser.Physics.Arcade.Body.prototype, "x", { + + /** + * The x position. + * @method x + * @return {number} + */ + get: function () { + return this.position.x; + }, + + /** + * The x position. + * @method x + * @param {number} value + */ + set: function (value) { + this.position.x = value; + } + +}); + +/** +* @name Phaser.Physics.Arcade.Body#y +* @property {number} y - The y position. +*/ +Object.defineProperty(Phaser.Physics.Arcade.Body.prototype, "y", { + + /** + * The y position. + * @method y + * @return {number} + */ + get: function () { + return this.position.y; + }, + + /** + * The y position. + * @method y + * @param {number} value + */ + set: function (value) { + this.position.y = value; + } + +}); + +Phaser.Physics.Arcade.Body.prototype.constructor = Phaser.Physics.Arcade.Body; diff --git a/src/physics/arcade/QuadTree.js b/src/physics/arcade/QuadTree.js index 661b7326..598aae46 100644 --- a/src/physics/arcade/QuadTree.js +++ b/src/physics/arcade/QuadTree.js @@ -1,6 +1,6 @@ /** * @author Richard Davey -* @copyright 2013 Photon Storm Ltd. +* @copyright 2014 Photon Storm Ltd. * @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License} */ diff --git a/src/physics/arcade/TempSprite.js b/src/physics/arcade/TempSprite.js deleted file mode 100644 index de08e4c2..00000000 --- a/src/physics/arcade/TempSprite.js +++ /dev/null @@ -1,1126 +0,0 @@ -/** -* @author Richard Davey -* @copyright 2013 Photon Storm Ltd. -* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License} -*/ - -/** -* @class Phaser.Sprite -* -* @classdesc Create a new `Sprite` object. Sprites are the lifeblood of your game, used for nearly everything visual. -* -* At its most basic a Sprite consists of a set of coordinates and a texture that is rendered to the canvas. -* They also contain additional properties allowing for physics motion (via Sprite.body), input handling (via Sprite.input), -* events (via Sprite.events), animation (via Sprite.animations), camera culling and more. Please see the Examples for use cases. -* -* @constructor -* @param {Phaser.Game} game - A reference to the currently running game. -* @param {number} x - The x coordinate (in world space) to position the Sprite at. -* @param {number} y - The y coordinate (in world space) to position the Sprite at. -* @param {string|Phaser.RenderTexture|Phaser.BitmapData|PIXI.Texture} key - This is the image or texture used by the Sprite during rendering. It can be a string which is a reference to the Cache entry, or an instance of a RenderTexture or PIXI.Texture. -* @param {string|number} frame - If this Sprite is using part of a sprite sheet or texture atlas you can specify the exact frame to use by giving a string or numeric index. -*/ -Phaser.Sprite = function (game, x, y, key, frame) { - - x = x || 0; - y = y || 0; - key = key || null; - frame = frame || null; - - /** - * @property {Phaser.Game} game - A reference to the currently running Game. - */ - this.game = game; - - /** - * @property {boolean} exists - If exists = false then the Sprite isn't updated by the core game loop or physics subsystem at all. - * @default - */ - this.exists = true; - - /** - * @property {boolean} alive - This is a handy little var your game can use to determine if a sprite is alive or not, it doesn't effect rendering. - * @default - */ - this.alive = true; - - /** - * @property {Phaser.Group} group - The parent Group of this Sprite. This is usually set after Sprite instantiation by the parent. - */ - this.group = null; - - /** - * @property {string} name - The user defined name given to this Sprite. - * @default - */ - this.name = ''; - - /** - * @property {number} type - The const type of this object. - * @readonly - */ - this.type = Phaser.SPRITE; - - /** - * @property {number} renderOrderID - Used by the Renderer and Input Manager to control picking order. - * @default - */ - this.renderOrderID = -1; - - /** - * If you would like the Sprite to have a lifespan once 'born' you can set this to a positive value. Handy for particles, bullets, etc. - * The lifespan is decremented by game.time.elapsed each update, once it reaches zero the kill() function is called. - * @property {number} lifespan - The lifespan of the Sprite (in ms) before it will be killed. - * @default - */ - this.lifespan = 0; - - /** - * @property {Phaser.Events} events - The Events you can subscribe to that are dispatched when certain things happen on this Sprite or its components. - */ - this.events = new Phaser.Events(this); - - /** - * @property {Phaser.AnimationManager} animations - This manages animations of the sprite. You can modify animations through it (see Phaser.AnimationManager) - */ - this.animations = new Phaser.AnimationManager(this); - - /** - * @property {Phaser.InputHandler} input - The Input Handler Component. - */ - this.input = new Phaser.InputHandler(this); - - /** - * @property {string|Phaser.RenderTexture|Phaser.BitmapData|PIXI.Texture} key - This is the image or texture used by the Sprite during rendering. It can be a string which is a reference to the Cache entry, or an instance of a RenderTexture, BitmapData or PIXI.Texture. - */ - this.key = key; - - /** - * @property {Phaser.Frame} currentFrame - A reference to the currently displayed frame. - */ - this.currentFrame = null; - - if (key instanceof Phaser.RenderTexture) - { - PIXI.Sprite.call(this, key); - - this.currentFrame = this.game.cache.getTextureFrame(key.name); - } - else if (key instanceof Phaser.BitmapData) - { - PIXI.Sprite.call(this, key.texture, key.textureFrame); - - this.currentFrame = key.textureFrame; - } - else if (key instanceof PIXI.Texture) - { - PIXI.Sprite.call(this, key); - - this.currentFrame = frame; - } - else - { - if (key === null || typeof key === 'undefined') - { - key = '__default'; - this.key = key; - } - else if (typeof key === 'string' && this.game.cache.checkImageKey(key) === false) - { - key = '__missing'; - this.key = key; - } - - PIXI.Sprite.call(this, PIXI.TextureCache[key]); - - if (this.game.cache.isSpriteSheet(key)) - { - this.animations.loadFrameData(this.game.cache.getFrameData(key)); - - if (frame !== null) - { - if (typeof frame === 'string') - { - this.frameName = frame; - } - else - { - this.frame = frame; - } - } - } - else - { - this.currentFrame = this.game.cache.getFrame(key); - } - } - - /** - * The rectangular area from the texture that will be rendered. - * @property {Phaser.Rectangle} textureRegion - */ - this.textureRegion = new Phaser.Rectangle(this.texture.frame.x, this.texture.frame.y, this.texture.frame.width, this.texture.frame.height); - - /** - * The anchor sets the origin point of the texture. - * The default is 0,0 this means the textures origin is the top left - * Setting than anchor to 0.5,0.5 means the textures origin is centered - * Setting the anchor to 1,1 would mean the textures origin points will be the bottom right - * - * @property {Phaser.Point} anchor - The anchor around which rotation and scaling takes place. - */ - this.anchor = new Phaser.Point(); - - /** - * @property {number} x - The x coordinate in world space of this Sprite. - */ - this.x = x; - - /** - * @property {number} y - The y coordinate in world space of this Sprite. - */ - this.y = y; - - this.position.x = x; - this.position.y = y; - - /** - * @property {Phaser.Point} world - The world coordinates of this Sprite. This differs from the x/y coordinates which are relative to the Sprites container. - */ - this.world = new Phaser.Point(x, y); - - /** - * Should this Sprite be automatically culled if out of range of the camera? - * A culled sprite has its renderable property set to 'false'. - * - * @property {boolean} autoCull - A flag indicating if the Sprite should be automatically camera culled or not. - * @default - */ - this.autoCull = false; - - /** - * @property {Phaser.Point} scale - The scale of the Sprite when rendered. By default it's set to 1 (no scale). You can modify it via scale.x or scale.y or scale.setTo(x, y). A value of 1 means no change to the scale, 0.5 means "half the size", 2 means "twice the size", etc. - */ - this.scale = new Phaser.Point(1, 1); - - /** - * @property {object} _cache - A mini cache for storing all of the calculated values. - * @private - */ - this._cache = { - - dirty: false, - - // Transform cache - a00: -1, - a01: -1, - a02: -1, - a10: -1, - a11: -1, - a12: -1, - id: -1, - - // Input specific transform cache - i01: -1, - i10: -1, - idi: -1, - - // Bounds check - left: null, - right: null, - top: null, - bottom: null, - - // delta cache - prevX: x, - prevY: y, - - // The previous calculated position - x: -1, - y: -1, - - // The actual scale values based on the worldTransform - scaleX: 1, - scaleY: 1, - - // The width/height of the image, based on the un-modified frame size multiplied by the final calculated scale size - width: this.currentFrame.sourceSizeW, - height: this.currentFrame.sourceSizeH, - - // The actual width/height of the image if from a trimmed atlas, multiplied by the final calculated scale size - halfWidth: Math.floor(this.currentFrame.sourceSizeW / 2), - halfHeight: Math.floor(this.currentFrame.sourceSizeH / 2), - - // The width/height of the image, based on the un-modified frame size multiplied by the final calculated scale size - calcWidth: -1, - calcHeight: -1, - - // The current frame details - // frameID: this.currentFrame.uuid, frameWidth: this.currentFrame.width, frameHeight: this.currentFrame.height, - frameID: -1, - frameWidth: this.currentFrame.width, - frameHeight: this.currentFrame.height, - - // If this sprite visible to the camera (regardless of being set to visible or not) - cameraVisible: true, - - // Crop cache - cropX: 0, - cropY: 0, - cropWidth: this.currentFrame.sourceSizeW, - cropHeight: this.currentFrame.sourceSizeH - - }; - - /** - * @property {Phaser.Point} offset - Corner point defaults. Should not typically be modified. - */ - this.offset = new Phaser.Point(); - - /** - * @property {Phaser.Point} center - A Point containing the center coordinate of the Sprite. Takes rotation and scale into account. - */ - this.center = new Phaser.Point(x + Math.floor(this._cache.width / 2), y + Math.floor(this._cache.height / 2)); - - /** - * @property {Phaser.Point} topLeft - A Point containing the top left coordinate of the Sprite. Takes rotation and scale into account. - */ - this.topLeft = new Phaser.Point(x, y); - - /** - * @property {Phaser.Point} topRight - A Point containing the top right coordinate of the Sprite. Takes rotation and scale into account. - */ - this.topRight = new Phaser.Point(x + this._cache.width, y); - - /** - * @property {Phaser.Point} bottomRight - A Point containing the bottom right coordinate of the Sprite. Takes rotation and scale into account. - */ - this.bottomRight = new Phaser.Point(x + this._cache.width, y + this._cache.height); - - /** - * @property {Phaser.Point} bottomLeft - A Point containing the bottom left coordinate of the Sprite. Takes rotation and scale into account. - */ - this.bottomLeft = new Phaser.Point(x, y + this._cache.height); - - /** - * This Rectangle object fully encompasses the Sprite and is updated in real-time. - * The bounds is the full bounding area after rotation and scale have been taken into account. It should not be modified directly. - * It's used for Camera culling and physics body alignment. - * @property {Phaser.Rectangle} bounds - */ - this.bounds = new Phaser.Rectangle(x, y, this._cache.width, this._cache.height); - - /** - * @property {Phaser.Physics.Arcade.Body} body - By default Sprites have a Phaser.Physics Body attached to them. You can operate physics actions via this property, or null it to skip all physics updates. - */ - this.body = new Phaser.Physics.Arcade.Body(this); - - /** - * @property {number} health - Health value. Used in combination with damage() to allow for quick killing of Sprites. - */ - this.health = 1; - - /** - * @property {boolean} inWorld - This value is set to true if the Sprite is positioned within the World, otherwise false. - */ - this.inWorld = Phaser.Rectangle.intersects(this.bounds, this.game.world.bounds); - - /** - * @property {number} inWorldThreshold - A threshold value applied to the inWorld check. If you don't want a Sprite to be considered "out of the world" until at least 100px away for example then set it to 100. - * @default - */ - this.inWorldThreshold = 0; - - /** - * @property {boolean} outOfBoundsKill - If true the Sprite is killed as soon as Sprite.inWorld is false. - * @default - */ - this.outOfBoundsKill = false; - - /** - * @property {boolean} _outOfBoundsFired - Internal flag. - * @private - * @default - */ - this._outOfBoundsFired = false; - - /** - * A Sprite that is fixed to the camera ignores the position of any ancestors in the display list and uses its x/y coordinates as offsets from the top left of the camera. - * @property {boolean} fixedToCamera - Fixes this Sprite to the Camera. - * @default - */ - this.fixedToCamera = false; - - /** - * @property {Phaser.Point} cameraOffset - If this Sprite is fixed to the camera then use this Point to specify how far away from the Camera x/y it's rendered. - */ - this.cameraOffset = new Phaser.Point(); - - /** - * You can crop the Sprites texture by modifying the crop properties. For example crop.width = 50 would set the Sprite to only render 50px wide. - * The crop is only applied if you have set Sprite.cropEnabled to true. - * @property {Phaser.Rectangle} crop - The crop Rectangle applied to the Sprite texture before rendering. - * @default - */ - this.crop = new Phaser.Rectangle(0, 0, this._cache.width, this._cache.height); - - /** - * @property {boolean} cropEnabled - If true the Sprite.crop property is used to crop the texture before render. Set to false to disable. - * @default - */ - this.cropEnabled = false; - - this.updateCache(); - this.updateBounds(); - -}; - -// Needed to keep the PIXI.Sprite constructor in the prototype chain (as the core pixi renderer uses an instanceof check sadly) -Phaser.Sprite.prototype = Object.create(PIXI.Sprite.prototype); -Phaser.Sprite.prototype.constructor = Phaser.Sprite; - -/** -* Automatically called by World.preUpdate. Handles cache updates, lifespan checks, animation updates and physics updates. -* -* @method Phaser.Sprite#preUpdate -* @memberof Phaser.Sprite -*/ -Phaser.Sprite.prototype.preUpdate = function() { - - if (!this.exists || (this.group && !this.group.exists)) - { - this.renderOrderID = -1; - - // Skip children if not exists - return false; - } - - if (this.lifespan > 0) - { - this.lifespan -= this.game.time.elapsed; - - if (this.lifespan <= 0) - { - this.kill(); - return false; - } - } - - this._cache.dirty = false; - - if (this.visible) - { - this.renderOrderID = this.game.world.currentRenderOrderID++; - } - - this.updateCache(); - - this.updateAnimation(); - - this.updateCrop(); - - // Re-run the camera visibility check - if (this._cache.dirty || this.world.x !== this._cache.prevX || this.world.y !== this._cache.prevY) - { - this.updateBounds(); - } - - if (this.body) - { - this.body.preUpdate(); - } - - return true; - -}; - -/** -* Internal function called by preUpdate. -* -* @method Phaser.Sprite#updateCache -* @memberof Phaser.Sprite -*/ -Phaser.Sprite.prototype.updateCache = function() { - - this._cache.prevX = this.world.x; - this._cache.prevY = this.world.y; - - if (this.fixedToCamera) - { - this.x = this.game.camera.view.x + this.cameraOffset.x; - this.y = this.game.camera.view.y + this.cameraOffset.y; - } - - this.world.setTo(this.game.camera.x + this.worldTransform[2], this.game.camera.y + this.worldTransform[5]); - - if (this.worldTransform[1] != this._cache.i01 || this.worldTransform[3] != this._cache.i10 || this.worldTransform[0] != this._cache.a00 || this.worldTransform[41] != this._cache.a11) - { - this._cache.a00 = this.worldTransform[0]; // scaleX a |a c tx| - this._cache.a01 = this.worldTransform[1]; // skewY c |b d ty| - this._cache.a10 = this.worldTransform[3]; // skewX b |0 0 1| - this._cache.a11 = this.worldTransform[4]; // scaleY d - - this._cache.i01 = this.worldTransform[1]; // skewY c (remains non-modified for input checks) - this._cache.i10 = this.worldTransform[3]; // skewX b (remains non-modified for input checks) - - this._cache.scaleX = Math.sqrt((this._cache.a00 * this._cache.a00) + (this._cache.a01 * this._cache.a01)); // round this off a bit? - this._cache.scaleY = Math.sqrt((this._cache.a10 * this._cache.a10) + (this._cache.a11 * this._cache.a11)); // round this off a bit? - - this._cache.a01 *= -1; - this._cache.a10 *= -1; - - this._cache.id = 1 / (this._cache.a00 * this._cache.a11 + this._cache.a01 * -this._cache.a10); - this._cache.idi = 1 / (this._cache.a00 * this._cache.a11 + this._cache.i01 * -this._cache.i10); - - this._cache.dirty = true; - } - - this._cache.a02 = this.worldTransform[2]; // translateX tx - this._cache.a12 = this.worldTransform[5]; // translateY ty - -}; - -/** -* Internal function called by preUpdate. -* -* @method Phaser.Sprite#updateAnimation -* @memberof Phaser.Sprite -*/ -Phaser.Sprite.prototype.updateAnimation = function() { - - if (this.animations.update() || (this.currentFrame && this.currentFrame.uuid != this._cache.frameID)) - { - this._cache.frameID = this.currentFrame.uuid; - - this._cache.frameWidth = this.texture.frame.width; - this._cache.frameHeight = this.texture.frame.height; - - this._cache.width = this.currentFrame.width; - this._cache.height = this.currentFrame.height; - - this._cache.halfWidth = Math.floor(this._cache.width / 2); - this._cache.halfHeight = Math.floor(this._cache.height / 2); - - this._cache.dirty = true; - - } - -}; - -/** -* Internal function called by preUpdate. -* -* @method Phaser.Sprite#updateCrop -* @memberof Phaser.Sprite -*/ -Phaser.Sprite.prototype.updateCrop = function() { - - // This only runs if crop is enabled - if (this.cropEnabled && (this.crop.width != this._cache.cropWidth || this.crop.height != this._cache.cropHeight || this.crop.x != this._cache.cropX || this.crop.y != this._cache.cropY)) - { - this.crop.floorAll(); - - this._cache.cropX = this.crop.x; - this._cache.cropY = this.crop.y; - this._cache.cropWidth = this.crop.width; - this._cache.cropHeight = this.crop.height; - - this.texture.frame = this.crop; - this.texture.width = this.crop.width; - this.texture.height = this.crop.height; - - this.texture.updateFrame = true; - - PIXI.Texture.frameUpdates.push(this.texture); - } - -}; - -/** -* Internal function called by preUpdate. -* -* @method Phaser.Sprite#updateBounds -* @memberof Phaser.Sprite -*/ -Phaser.Sprite.prototype.updateBounds = function() { - - this.offset.setTo(this._cache.a02 - (this.anchor.x * this.width), this._cache.a12 - (this.anchor.y * this.height)); - - this.getLocalPosition(this.center, this.offset.x + (this.width / 2), this.offset.y + (this.height / 2)); - this.getLocalPosition(this.topLeft, this.offset.x, this.offset.y); - this.getLocalPosition(this.topRight, this.offset.x + this.width, this.offset.y); - this.getLocalPosition(this.bottomLeft, this.offset.x, this.offset.y + this.height); - this.getLocalPosition(this.bottomRight, this.offset.x + this.width, this.offset.y + this.height); - - this._cache.left = Phaser.Math.min(this.topLeft.x, this.topRight.x, this.bottomLeft.x, this.bottomRight.x); - this._cache.right = Phaser.Math.max(this.topLeft.x, this.topRight.x, this.bottomLeft.x, this.bottomRight.x); - this._cache.top = Phaser.Math.min(this.topLeft.y, this.topRight.y, this.bottomLeft.y, this.bottomRight.y); - this._cache.bottom = Phaser.Math.max(this.topLeft.y, this.topRight.y, this.bottomLeft.y, this.bottomRight.y); - - this.bounds.setTo(this._cache.left, this._cache.top, this._cache.right - this._cache.left, this._cache.bottom - this._cache.top); - - this.updateFrame = true; - - if (this.inWorld === false) - { - // Sprite WAS out of the screen, is it still? - this.inWorld = Phaser.Rectangle.intersects(this.bounds, this.game.world.bounds, this.inWorldThreshold); - - if (this.inWorld) - { - // It's back again, reset the OOB check - this._outOfBoundsFired = false; - } - } - else - { - // Sprite WAS in the screen, has it now left? - this.inWorld = Phaser.Rectangle.intersects(this.bounds, this.game.world.bounds, this.inWorldThreshold); - - if (this.inWorld === false) - { - this.events.onOutOfBounds.dispatch(this); - this._outOfBoundsFired = true; - - if (this.outOfBoundsKill) - { - this.kill(); - } - } - } - - this._cache.cameraVisible = Phaser.Rectangle.intersects(this.game.world.camera.screenView, this.bounds, 0); - - if (this.autoCull) - { - // Won't get rendered but will still get its transform updated - this.renderable = this._cache.cameraVisible; - } - - // Update our physics bounds - if (this.body) - { - this.body.updateBounds(this.center.x, this.center.y, this._cache.scaleX, this._cache.scaleY); - } - -}; - -/** -* Gets the local position of a coordinate relative to the Sprite, factoring in rotation and scale. -* Mostly only used internally. -* -* @method Phaser.Sprite#getLocalPosition -* @memberof Phaser.Sprite -* @param {Phaser.Point} p - The Point object to store the results in. -* @param {number} x - x coordinate within the Sprite to translate. -* @param {number} y - x coordinate within the Sprite to translate. -* @param {number} sx - Scale factor to be applied. -* @param {number} sy - Scale factor to be applied. -* @return {Phaser.Point} The translated point. -*/ -Phaser.Sprite.prototype.getLocalPosition = function(p, x, y) { - - p.x = ((this._cache.a11 * this._cache.id * x + -this._cache.a01 * this._cache.id * y + (this._cache.a12 * this._cache.a01 - this._cache.a02 * this._cache.a11) * this._cache.id) * this.scale.x) + this._cache.a02; - p.y = ((this._cache.a00 * this._cache.id * y + -this._cache.a10 * this._cache.id * x + (-this._cache.a12 * this._cache.a00 + this._cache.a02 * this._cache.a10) * this._cache.id) * this.scale.y) + this._cache.a12; - - return p; - -}; - -/** -* Gets the local unmodified position of a coordinate relative to the Sprite, factoring in rotation and scale. -* Mostly only used internally by the Input Manager, but also useful for custom hit detection. -* -* @method Phaser.Sprite#getLocalUnmodifiedPosition -* @memberof Phaser.Sprite -* @param {Phaser.Point} p - The Point object to store the results in. -* @param {number} x - x coordinate within the Sprite to translate. -* @param {number} y - x coordinate within the Sprite to translate. -* @return {Phaser.Point} The translated point. -*/ -Phaser.Sprite.prototype.getLocalUnmodifiedPosition = function(p, gx, gy) { - - p.x = (this._cache.a11 * this._cache.idi * gx + -this._cache.i01 * this._cache.idi * gy + (this._cache.a12 * this._cache.i01 - this._cache.a02 * this._cache.a11) * this._cache.idi) + (this.anchor.x * this._cache.width); - p.y = (this._cache.a00 * this._cache.idi * gy + -this._cache.i10 * this._cache.idi * gx + (-this._cache.a12 * this._cache.a00 + this._cache.a02 * this._cache.i10) * this._cache.idi) + (this.anchor.y * this._cache.height); - - return p; - -}; - -/** -* Resets the Sprite.crop value back to the frame dimensions. -* -* @method Phaser.Sprite#resetCrop -* @memberof Phaser.Sprite -*/ -Phaser.Sprite.prototype.resetCrop = function() { - - this.crop = new Phaser.Rectangle(0, 0, this._cache.width, this._cache.height); - this.texture.setFrame(this.crop); - this.cropEnabled = false; - -}; - -/** -* Internal function called by the World postUpdate cycle. -* -* @method Phaser.Sprite#postUpdate -* @memberof Phaser.Sprite -*/ -Phaser.Sprite.prototype.postUpdate = function() { - - if (this.key instanceof Phaser.BitmapData && this.key._dirty) - { - this.key.render(); - } - - if (this.exists) - { - // The sprite is positioned in this call, after taking into consideration motion updates and collision - if (this.body) - { - this.body.postUpdate(); - } - - if (this.fixedToCamera) - { - this._cache.x = this.game.camera.view.x + this.cameraOffset.x; - this._cache.y = this.game.camera.view.y + this.cameraOffset.y; - } - else - { - this._cache.x = this.x; - this._cache.y = this.y; - } - - this.world.setTo(this.game.camera.x + this.worldTransform[2], this.game.camera.y + this.worldTransform[5]); - - this.position.x = this._cache.x; - this.position.y = this._cache.y; - } - -}; - -/** -* Changes the Texture the Sprite is using entirely. The old texture is removed and the new one is referenced or fetched from the Cache. -* This causes a WebGL texture update, so use sparingly or in low-intensity portions of your game. -* -* @method Phaser.Sprite#loadTexture -* @memberof Phaser.Sprite -* @param {string|Phaser.RenderTexture|Phaser.BitmapData|PIXI.Texture} key - This is the image or texture used by the Sprite during rendering. It can be a string which is a reference to the Cache entry, or an instance of a RenderTexture, BitmapData or PIXI.Texture. -* @param {string|number} frame - If this Sprite is using part of a sprite sheet or texture atlas you can specify the exact frame to use by giving a string or numeric index. -*/ -Phaser.Sprite.prototype.loadTexture = function (key, frame) { - - this.key = key; - - if (key instanceof Phaser.RenderTexture) - { - this.currentFrame = this.game.cache.getTextureFrame(key.name); - } - else if (key instanceof Phaser.BitmapData) - { - this.setTexture(key.texture); - this.currentFrame = key.textureFrame; - } - else if (key instanceof PIXI.Texture) - { - this.currentFrame = frame; - } - else - { - if (typeof key === 'undefined' || this.game.cache.checkImageKey(key) === false) - { - key = '__default'; - this.key = key; - } - - if (this.game.cache.isSpriteSheet(key)) - { - this.animations.loadFrameData(this.game.cache.getFrameData(key)); - - if (typeof frame !== 'undefined') - { - if (typeof frame === 'string') - { - this.frameName = frame; - } - else - { - this.frame = frame; - } - } - } - else - { - this.currentFrame = this.game.cache.getFrame(key); - this.setTexture(PIXI.TextureCache[key]); - } - } - -}; - -/** -* Moves the sprite so its center is located on the given x and y coordinates. -* Doesn't change the anchor point of the sprite. -* -* @method Phaser.Sprite#centerOn -* @memberof Phaser.Sprite -* @param {number} x - The x coordinate (in world space) to position the Sprite at. -* @param {number} y - The y coordinate (in world space) to position the Sprite at. -* @return (Phaser.Sprite) This instance. -*/ -Phaser.Sprite.prototype.centerOn = function(x, y) { - - this.x = x + (this.x - this.center.x); - this.y = y + (this.y - this.center.y); - return this; - -}; - -/** -* Brings a 'dead' Sprite back to life, optionally giving it the health value specified. -* A resurrected Sprite has its alive, exists and visible properties all set to true. -* It will dispatch the onRevived event, you can listen to Sprite.events.onRevived for the signal. -* -* @method Phaser.Sprite#revive -* @memberof Phaser.Sprite -* @param {number} [health=1] - The health to give the Sprite. -* @return (Phaser.Sprite) This instance. -*/ -Phaser.Sprite.prototype.revive = function(health) { - - if (typeof health === 'undefined') { health = 1; } - - this.alive = true; - this.exists = true; - this.visible = true; - this.health = health; - - if (this.events) - { - this.events.onRevived.dispatch(this); - } - - return this; - -}; - -/** -* Kills a Sprite. A killed Sprite has its alive, exists and visible properties all set to false. -* It will dispatch the onKilled event, you can listen to Sprite.events.onKilled for the signal. -* Note that killing a Sprite is a way for you to quickly recycle it in a Sprite pool, it doesn't free it up from memory. -* If you don't need this Sprite any more you should call Sprite.destroy instead. -* -* @method Phaser.Sprite#kill -* @memberof Phaser.Sprite -* @return (Phaser.Sprite) This instance. -*/ -Phaser.Sprite.prototype.kill = function() { - - this.alive = false; - this.exists = false; - this.visible = false; - - if (this.events) - { - this.events.onKilled.dispatch(this); - } - - return this; - -}; - -/** -* Destroys the Sprite. This removes it from its parent group, destroys the input, event and animation handlers if present -* and nulls its reference to game, freeing it up for garbage collection. -* -* @method Phaser.Sprite#destroy -* @memberof Phaser.Sprite -*/ -Phaser.Sprite.prototype.destroy = function() { - - if (this.filters) - { - this.filters = null; - } - - if (this.group) - { - this.group.remove(this); - } - - if (this.input) - { - this.input.destroy(); - } - - if (this.events) - { - this.events.destroy(); - } - - if (this.animations) - { - this.animations.destroy(); - } - - this.alive = false; - this.exists = false; - this.visible = false; - - this.game = null; - -}; - -/** -* Damages the Sprite, this removes the given amount from the Sprites health property. -* If health is then taken below zero Sprite.kill is called. -* -* @method Phaser.Sprite#damage -* @memberof Phaser.Sprite -* @param {number} amount - The amount to subtract from the Sprite.health value. -* @return (Phaser.Sprite) This instance. -*/ -Phaser.Sprite.prototype.damage = function(amount) { - - if (this.alive) - { - this.health -= amount; - - if (this.health < 0) - { - this.kill(); - } - } - - return this; - -}; - -/** -* Resets the Sprite. This places the Sprite at the given x/y world coordinates and then -* sets alive, exists, visible and renderable all to true. Also resets the outOfBounds state and health values. -* If the Sprite has a physics body that too is reset. -* -* @method Phaser.Sprite#reset -* @memberof Phaser.Sprite -* @param {number} x - The x coordinate (in world space) to position the Sprite at. -* @param {number} y - The y coordinate (in world space) to position the Sprite at. -* @param {number} [health=1] - The health to give the Sprite. -* @return (Phaser.Sprite) This instance. -*/ -Phaser.Sprite.prototype.reset = function(x, y, health) { - - if (typeof health === 'undefined') { health = 1; } - - this.x = x; - this.y = y; - this.position.x = this.x; - this.position.y = this.y; - this.alive = true; - this.exists = true; - this.visible = true; - this.renderable = true; - this._outOfBoundsFired = false; - - this.health = health; - - if (this.body) - { - this.body.reset(); - } - - return this; - -}; - -/** -* Brings the Sprite to the top of the display list it is a child of. Sprites that are members of a Phaser.Group are only -* bought to the top of that Group, not the entire display list. -* -* @method Phaser.Sprite#bringToTop -* @memberof Phaser.Sprite -* @return (Phaser.Sprite) This instance. -*/ -Phaser.Sprite.prototype.bringToTop = function() { - - if (this.group) - { - this.group.bringToTop(this); - } - else - { - this.game.world.bringToTop(this); - } - - return this; - -}; - -/** -* Play an animation based on the given key. The animation should previously have been added via sprite.animations.add() -* If the requested animation is already playing this request will be ignored. If you need to reset an already running animation do so directly on the Animation object itself. -* -* @method Phaser.Sprite#play -* @memberof Phaser.Sprite -* @param {string} name - The name of the animation to be played, e.g. "fire", "walk", "jump". -* @param {number} [frameRate=null] - The framerate to play the animation at. The speed is given in frames per second. If not provided the previously set frameRate of the Animation is used. -* @param {boolean} [loop=false] - Should the animation be looped after playback. If not provided the previously set loop value of the Animation is used. -* @param {boolean} [killOnComplete=false] - If set to true when the animation completes (only happens if loop=false) the parent Sprite will be killed. -* @return {Phaser.Animation} A reference to playing Animation instance. -*/ -Phaser.Sprite.prototype.play = function (name, frameRate, loop, killOnComplete) { - - if (this.animations) - { - return this.animations.play(name, frameRate, loop, killOnComplete); - } - -}; - -/** -* Indicates the rotation of the Sprite, in degrees, from its original orientation. Values from 0 to 180 represent clockwise rotation; values from 0 to -180 represent counterclockwise rotation. -* Values outside this range are added to or subtracted from 360 to obtain a value within the range. For example, the statement player.angle = 450 is the same as player.angle = 90. -* If you wish to work in radians instead of degrees use the property Sprite.rotation instead. -* @name Phaser.Sprite#angle -* @property {number} angle - Gets or sets the Sprites angle of rotation in degrees. -*/ -Object.defineProperty(Phaser.Sprite.prototype, 'angle', { - - get: function() { - return Phaser.Math.wrapAngle(Phaser.Math.radToDeg(this.rotation)); - }, - - set: function(value) { - this.rotation = Phaser.Math.degToRad(Phaser.Math.wrapAngle(value)); - } - -}); - -/** -* @name Phaser.Sprite#frame -* @property {number} frame - Gets or sets the current frame index and updates the Texture Cache for display. -*/ -Object.defineProperty(Phaser.Sprite.prototype, "frame", { - - get: function () { - return this.animations.frame; - }, - - set: function (value) { - this.animations.frame = value; - } - -}); - -/** -* @name Phaser.Sprite#frameName -* @property {string} frameName - Gets or sets the current frame name and updates the Texture Cache for display. -*/ -Object.defineProperty(Phaser.Sprite.prototype, "frameName", { - - get: function () { - return this.animations.frameName; - }, - - set: function (value) { - this.animations.frameName = value; - } - -}); - -/** -* @name Phaser.Sprite#inCamera -* @property {boolean} inCamera - Is this sprite visible to the camera or not? -* @readonly -*/ -Object.defineProperty(Phaser.Sprite.prototype, "inCamera", { - - get: function () { - return this._cache.cameraVisible; - } - -}); - -/** -* The width of the sprite in pixels, setting this will actually modify the scale to acheive the value desired. -* If you wish to crop the Sprite instead see the Sprite.crop value. -* -* @name Phaser.Sprite#width -* @property {number} width - The width of the Sprite in pixels. -*/ -Object.defineProperty(Phaser.Sprite.prototype, 'width', { - - get: function() { - return this.scale.x * this.currentFrame.width; - }, - - set: function(value) { - - this.scale.x = value / this.currentFrame.width; - this._cache.scaleX = value / this.currentFrame.width; - this._width = value; - - } - -}); - -/** -* The height of the sprite in pixels, setting this will actually modify the scale to acheive the value desired. -* If you wish to crop the Sprite instead see the Sprite.crop value. -* -* @name Phaser.Sprite#height -* @property {number} height - The height of the Sprite in pixels. -*/ -Object.defineProperty(Phaser.Sprite.prototype, 'height', { - - get: function() { - return this.scale.y * this.currentFrame.height; - }, - - set: function(value) { - - this.scale.y = value / this.currentFrame.height; - this._cache.scaleY = value / this.currentFrame.height; - this._height = value; - - } - -}); - -/** -* By default a Sprite won't process any input events at all. By setting inputEnabled to true the Phaser.InputHandler is -* activated for this Sprite instance and it will then start to process click/touch events and more. -* -* @name Phaser.Sprite#inputEnabled -* @property {boolean} inputEnabled - Set to true to allow this Sprite to receive input events, otherwise false. -*/ -Object.defineProperty(Phaser.Sprite.prototype, "inputEnabled", { - - get: function () { - - return (this.input.enabled); - - }, - - set: function (value) { - - if (value) - { - if (this.input.enabled === false) - { - this.input.start(); - } - } - else - { - if (this.input.enabled) - { - this.input.stop(); - } - } - - } - -}); diff --git a/src/physics/arcade/World.js b/src/physics/arcade/World.js index da0fc623..8a90b7ea 100644 --- a/src/physics/arcade/World.js +++ b/src/physics/arcade/World.js @@ -1,6 +1,6 @@ /** * @author Richard Davey -* @copyright 2013 Photon Storm Ltd. +* @copyright 2014 Photon Storm Ltd. * @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License} */ @@ -161,6 +161,8 @@ Phaser.Physics.Arcade = function (game) { }; +Phaser.Physics.Arcade.prototype.constructor = Phaser.Physics.Arcade; + Phaser.Physics.Arcade.prototype = { /** @@ -172,56 +174,41 @@ Phaser.Physics.Arcade.prototype = { updateMotion: function (body) { // Rotation - this._velocityDelta = (this.computeVelocity(body, body.angularVelocity, body.angularAcceleration, body.angularDrag, body.maxAngular, 0) - body.angularVelocity) * 0.5; + this._velocityDelta = (this.computeVelocity(body.angularVelocity, body.angularAcceleration, body.angularDrag, body.maxAngular, 0) - body.angularVelocity) * 0.5; body.angularVelocity += this._velocityDelta; body.rotation += (body.angularVelocity * this.game.time.physicsElapsed); body.angularVelocity += this._velocityDelta; - if (body.allowGravity) - { - // Gravity was previously applied without taking physicsElapsed into account - // so it needs to be multiplied by 60 (fps) for compatibility with existing games - this._gravityX = (this.gravity.x + body.gravity.x) * 60; - this._gravityY = (this.gravity.y + body.gravity.y) * 60; - } - else - { - this._gravityX = this._gravityY = 0; - } + // Apply gravity using the p2 style gravityScale + body.velocity.x += this.gravity.x * this.game.time.physicsElapsed * body.gravityScale.x; + body.velocity.y += this.gravity.y * this.game.time.physicsElapsed * body.gravityScale.y; - // Horizontal - this._velocityDelta = (this.computeVelocity(body, body.velocity.x, body.acceleration.x, body.drag.x, body.maxVelocity.x, this._gravityX) - body.velocity.x) * 0.5; - body.velocity.x += this._velocityDelta; - body.x += (body.velocity.x * this.game.time.physicsElapsed); - body.velocity.x += this._velocityDelta; - - // Vertical - this._velocityDelta = (this.computeVelocity(body, body.velocity.y, body.acceleration.y, body.drag.y, body.maxVelocity.y, this._gravityY) - body.velocity.y) * 0.5; - body.velocity.y += this._velocityDelta; - body.y += (body.velocity.y * this.game.time.physicsElapsed); - body.velocity.y += this._velocityDelta; + // Apply velocity + body.velocity.x = this.computeVelocity(body.velocity.x, body.acceleration.x, body.drag.x, body.maxVelocity.x); + body.velocity.y = this.computeVelocity(body.velocity.y, body.acceleration.y, body.drag.y, body.maxVelocity.y); }, /** * A tween-like function that takes a starting velocity and some other factors and returns an altered velocity. + * Based on a function in Flixel by @ADAMATOMIC * * @method Phaser.Physics.Arcade#computeVelocity - * @param {Phaser.Physics.Arcade.Body} body - The Body object to be updated. * @param {number} velocity - Any component of velocity (e.g. 20). * @param {number} acceleration - Rate at which the velocity is changing. * @param {number} drag - Really kind of a deceleration, this is how much the velocity changes if Acceleration is not set. * @param {number} [max=10000] - An absolute value cap for the velocity. - * @param {number} gravity - The acceleration due to gravity. Gravity will not induce drag. * @return {number} The altered Velocity value. */ - computeVelocity: function (body, velocity, acceleration, drag, max, gravity) { + computeVelocity: function (velocity, acceleration, drag, max) { max = max || 10000; - velocity += (acceleration + gravity) * this.game.time.physicsElapsed; - - if (acceleration === 0 && drag !== 0) + if (acceleration) + { + velocity + acceleration * this.game.time.physicsElapsed; + } + else if (drag) { this._drag = drag * this.game.time.physicsElapsed; @@ -537,17 +524,30 @@ Phaser.Physics.Arcade.prototype = { collideSpriteVsTilemapLayer: function (sprite, tilemapLayer, collideCallback, processCallback, callbackContext) { // this._mapData = tilemapLayer.getTiles(sprite.body.left, sprite.body.top, sprite.body.width, sprite.body.height, true); - this._mapData = tilemapLayer.getTiles(sprite.body.x, sprite.body.y, sprite.body.width, sprite.body.height, true); + // this._mapData = tilemapLayer.getTiles(sprite.body.x, sprite.body.y, sprite.body.width, sprite.body.height, true); + + this._mapData = tilemapLayer.getIntersectingTiles(sprite.body.position.x, sprite.body.position.y, sprite.body.width, sprite.body.height, true); + + // var vx = sprite.body.x; + // var vy = sprite.body.y; + + // vx += sprite.body.newVelocity.x; + // vy += sprite.body.newVelocity.y; + + // this._mapData = tilemapLayer.getIntersectingTiles(vx, vy, sprite.body.width, sprite.body.height, true); if (this._mapData.length === 0) { return; } - if (this._mapData.length > 1) - { + // if (this._mapData.length > 1) + // { + // Needs process callback added this.separateTiles(sprite.body, this._mapData); - } + // } + + /* else { var i = 0; @@ -578,6 +578,7 @@ Phaser.Physics.Arcade.prototype = { } } } + */ }, @@ -870,62 +871,15 @@ Phaser.Physics.Arcade.prototype = { }, - /** - * Performs a rect intersection test against the two objects. - * Objects must expose properties: width, height, left, right, top, bottom. - * @method Phaser.Physics.Arcade#tileIntersects - * @param {object} body - The Body to test. - * @param {object} tile - The Tile to test. - * @returns {boolean} Returns true if the objects intersect, otherwise false. - */ - tileIntersects: function (body, tile) { - - this._intersection[0] = 0; - this._intersection[1] = 0; - this._intersection[2] = 0; - this._intersection[3] = 0; - this._intersection[4] = 0; - - if (body.width <= 0 || body.height <= 0 || tile.width <= 0 || tile.height <= 0) - { - this._intersection[4] = 0; - return this._intersection; - } - - if (!(body.right < tile.x || body.bottom < tile.y || body.x > tile.right || body.y > tile.bottom)) - { - this._intersection[0] = Math.max(body.x, tile.x); // x - this._intersection[1] = Math.max(body.y, tile.y); // y - this._intersection[2] = Math.min(body.right, tile.right) - this._intersection[0]; // width - this._intersection[3] = Math.min(body.bottom, tile.bottom) - this._intersection[1]; // height - this._intersection[4] = 1; - - return this._intersection; - } - - this._intersection[4] = 0; - - return this._intersection; - - }, - - /** - * The core separation function to separate a physics body and an array of tiles. - * @method Phaser.Physics.Arcade#separateTiles - * @param {Phaser.Physics.Arcade.Body} body - The Body object to separate. - * @param {array} tiles - The array of tiles to collide against. - * @returns {boolean} Returns true if the body was separated, otherwise false. - */ separateTiles: function (body, tiles) { - var tile; + body.resetResult(); + var result = false; for (var i = 0; i < tiles.length; i++) { - tile = tiles[i]; - - if (this.separateTile(body, tile)) + if (this.separateTile(i, body, tiles[i])) { result = true; } @@ -935,6 +889,86 @@ Phaser.Physics.Arcade.prototype = { }, + separateTile: function (i, body, tile) { + + // we re-check for collision in case body was separated in a previous step + if (i > 0 && !tile.intersects(body.position.x, body.position.y, body.right, body.bottom)) + { + // no collision so bail out (separted in a previous step) + console.log('no collision so bail out (separted in a previous step'); + return false; + } + + // console.log('body intersecting tile'); + // console.log('x', body.position.x, 'y', body.position.y, 'r', body.right, 'b', body.bottom, 'wh', body.width, body.height); + // console.log('x', tile.x, 'y', tile.y, 'r', tile.right, 'b', tile.bottom); + + if (body.newVelocity.x && (tile.faceLeft || tile.faceRight)) + { + var px = 0; + var tx = 0; + + if (body.newVelocity.x > 0 && tile.faceLeft) + { + px = body.width; + } + else if (body.newVelocity.x < 0 && tile.faceRight) + { + tx = tile.width; + } + + body.position.x = tile.worldX - px + tx; + + body.velocity.x = 0; // rebound check + // body.newVelocity.x = 0; // rebound check + } + + // Vertical only if still colliding + + // if (tile.intersects(body.position.x, body.position.y, body.right, body.bottom)) + // if (body.newVelocity.y && tile.intersects(body.position.x, body.position.y, body.right, body.bottom)) + if (body.newVelocity.y && (tile.faceTop || tile.faceBottom)) + { + var py = 0; + var ty = 0; + + if (body.newVelocity.y > 0 && tile.faceBottom) + { + py = body.height; + } + else if (body.newVelocity.y < 0 && tile.faceTop) + { + ty = tile.height; + } + + // console.log('cy', body.newVelocity.y, py, ty); + + body.position.y = tile.worldY - py + ty; + + body.velocity.y = 0; // rebound check + // body.newVelocity.y = 0; // rebound check + } + + // var pxOffsetX = (body.newVelocity.x > 0 ? body.width : 0); + // var tileOffsetX = (body.newVelocity.x < 0 ? tile.width : 0); + // var pxOffsetY = (body.newVelocity.y > 0 ? body.height : 0); + // var tileOffsetY = (body.newVelocity.y < 0 ? tile.height : 0); + + // Assume fully solid for now + + // body.result.x = true; + // body.result.tile = tile; + // body.result.px = tile.x - pxOffsetX + tileOffsetX; + // body.position.x = tile.x - pxOffsetX + tileOffsetX; + + // res.pos.y = tileY * this.tilesize - pxOffsetY + tileOffsetY; + + + + // console.log('nv', body.newVelocity.x, 'tile.xy', tile.x, tile.y, 'p', pxOffsetX, 't', tileOffsetX, 'body xy', body.position.x, body.position.y); + + }, + /** * The core separation function to separate a physics body and a tile. * @method Phaser.Physics.Arcade#separateTile @@ -942,7 +976,7 @@ Phaser.Physics.Arcade.prototype = { * @param {Phaser.Tile} tile - The tile to collide against. * @returns {boolean} Returns true if the body was separated, otherwise false. */ - separateTile: function (body, tile) { + XXXseparateTile: function (body, tile) { /* this._intersection = this.tileIntersects(body, tile); @@ -952,7 +986,6 @@ Phaser.Physics.Arcade.prototype = { { return false; } - */ Phaser.Rectangle.intersection(body, tile, this._intersection); @@ -960,6 +993,7 @@ Phaser.Physics.Arcade.prototype = { { return false; } + */ // console.log(this._intersection); // console.log(tile, body.x, body.y); @@ -982,6 +1016,16 @@ Phaser.Physics.Arcade.prototype = { body.overlapX = 0; + if (body.deltaX() < 0 && body.checkCollision.left) + { + // Body is moving LEFT + if (tile.tile.faceRight && body.x < tile.right) + { + + } + } + + var process = false; if (this._intersection.width > 0) @@ -989,18 +1033,21 @@ Phaser.Physics.Arcade.prototype = { // Tile is blocking to the right and body is moving left if (body.deltaX() < 0 && body.checkCollision.left && tile.tile.faceRight) { - process = true; - body.overlapX = -this._intersection.width; + body.x = tile.right; + // process = true; + // body.overlapX = -this._intersection.width; } // Tile is blocking to the left and body is moving right if (body.deltaX() > 0 && body.checkCollision.right && tile.tile.faceLeft) { - process = true; - body.overlapX = this._intersection.width; + body.right = tile.x; + // process = true; + // body.overlapX = this._intersection.width; } } +/* if (body.overlapX !== 0) { if (body.overlapX < 0) @@ -1013,7 +1060,7 @@ Phaser.Physics.Arcade.prototype = { } body.x -= body.overlapX; - body.preX -= body.overlapX; + // body.preX -= body.overlapX; body.blocked.x = Math.floor(body.x); body.blocked.y = Math.floor(body.y); @@ -1026,6 +1073,7 @@ Phaser.Physics.Arcade.prototype = { body.velocity.x = -body.velocity.x * body.bounce.x; } } +*/ Phaser.Rectangle.intersection(body, tile, this._intersection); @@ -1043,8 +1091,9 @@ Phaser.Physics.Arcade.prototype = { // Tile is blocking to the bottom and body is moving up if (body.deltaY() < 0 && body.checkCollision.up && tile.tile.faceBottom) { - process = true; - body.overlapY = -this._intersection.height; + + // process = true; + // body.overlapY = -this._intersection.height; } // Tile is blocking to the top and body is moving down @@ -1067,7 +1116,7 @@ Phaser.Physics.Arcade.prototype = { } body.y -= body.overlapY; - body.preY -= body.overlapY; + // body.preY -= body.overlapY; body.blocked.x = Math.floor(body.x); body.blocked.y = Math.floor(body.y); @@ -1240,274 +1289,6 @@ Phaser.Physics.Arcade.prototype = { }, - /** - * The core separation function to separate a physics body and an array of tiles. - * @method Phaser.Physics.Arcade#separateTiles - * @param {Phaser.Physics.Arcade.Body} body1 - The Body object to separate. - * @param {Phaser.Tile} tile - The tile to collide against. - * @returns {boolean} Returns true if the bodies were separated, otherwise false. - */ - OLDseparateTiles: function (body, tiles) { - - // Can't separate two immovable objects (tiles are always immovable) - // if (body.immovable) - // { - // return false; - // } - - body.overlapX = 0; - body.overlapY = 0; - - var tile; - var localOverlapX = 0; - var localOverlapY = 0; - - for (var i = 0; i < tiles.length; i++) - { - tile = tiles[i]; - - if (Phaser.Rectangle.intersects(body, tile)) - { - if (body.deltaX() < 0 && body.checkCollision.left && tile.tile.faceRight) - { - // LEFT - localOverlapX = body.x - tile.right; - - if (localOverlapX >= body.deltaX()) - { - // console.log('m left overlapX', localOverlapX, body.deltaX()); - // use touching instead of blocked? - body.blocked.left = true; - body.touching.left = true; - body.touching.none = false; - } - } - else if (body.deltaX() > 0 && body.checkCollision.right && tile.tile.faceLeft) - { - // RIGHT - localOverlapX = body.right - tile.x; - - // Distance check - if (localOverlapX <= body.deltaX()) - { - // console.log('m right overlapX', localOverlapX, body.deltaX()); - body.blocked.right = true; - body.touching.right = true; - body.touching.none = false; - } - } - - if (body.deltaY() < 0 && body.checkCollision.up && tile.tile.faceBottom) - { - // UP - localOverlapY = body.y - tile.bottom; - - // Distance check - if (localOverlapY >= body.deltaY()) - { - // console.log('m up overlapY', localOverlapY, body.deltaY()); - body.blocked.up = true; - body.touching.up = true; - body.touching.none = false; - } - } - else if (body.deltaY() > 0 && body.checkCollision.down && tile.tile.faceTop) - { - // DOWN - localOverlapY = body.bottom - tile.y; - - if (localOverlapY <= body.deltaY()) - { - // console.log('m down overlapY', localOverlapY, body.deltaY()); - body.blocked.down = true; - body.touching.down = true; - body.touching.none = false; - } - } - } - } - - if (localOverlapX !== 0) - { - body.overlapX = localOverlapX; - } - - if (localOverlapY !== 0) - { - body.overlapY = localOverlapY; - } - - if (body.touching.none) - { - return false; - } - - // if (body.overlapX !== 0) - if (body.touching.left || body.touching.right) - { - body.x -= body.overlapX; - body.preX -= body.overlapX; - - if (body.bounce.x === 0) - { - body.velocity.x = 0; - } - else - { - body.velocity.x = -body.velocity.x * body.bounce.x; - } - } - - // if (body.overlapY !== 0) - if (body.touching.up || body.touching.down) - { - body.y -= body.overlapY; - body.preY -= body.overlapY; - - if (body.bounce.y === 0) - { - body.velocity.y = 0; - } - else - { - body.velocity.y = -body.velocity.y * body.bounce.y; - } - } - - return true; - - }, - - /** - * The core separation function to separate a physics body and a tile. - * @method Phaser.Physics.Arcade#separateTile - * @param {Phaser.Physics.Arcade.Body} body1 - The Body object to separate. - * @param {Phaser.Tile} tile - The tile to collide against. - * @returns {boolean} Returns true if the bodies were separated, otherwise false. - */ - OLDseparateTile: function (body, tile) { - - // Can't separate two immovable objects (tiles are always immovable) - // if (body.immovable || Phaser.Rectangle.intersects(body, tile) === false) - if (Phaser.Rectangle.intersects(body, tile) === false) - { - // console.log('no intersects'); - // console.log('tx', tile.x, 'ty', tile.y, 'tw', tile.width, 'th', tile.height, 'tr', tile.right, 'tb', tile.bottom); - // console.log('bx', body.x, 'by', body.y, 'bw', body.width, 'bh', body.height, 'br', body.right, 'bb', body.bottom); - return false; - } - - // use body var instead - body.overlapX = 0; - body.overlapY = 0; - - // Remember - this happens AFTER the body has been moved by the motion update, so it needs moving back again - // console.log('---------------------------------------------------------------------------------------------'); - // console.log('tx', tile.x, 'ty', tile.y, 'tw', tile.width, 'th', tile.height, 'tr', tile.right, 'tb', tile.bottom); - // console.log('bx', body.x, 'by', body.y, 'bw', body.width, 'bh', body.height, 'br', body.right, 'bb', body.bottom); - // console.log(Phaser.Rectangle.intersects(body, tile)); - // console.log('dx', body.deltaX(), 'dy', body.deltaY(), 'dax', body.deltaAbsX(), 'day', body.deltaAbsY(), 'cax', Math.ceil(body.deltaAbsX()), 'cay', Math.ceil(body.deltaAbsY())); - - if (body.deltaX() < 0 && body.checkCollision.left && tile.tile.faceRight) - { - // LEFT - body.overlapX = body.x - tile.right; - - if (body.overlapX >= body.deltaX()) - { - // console.log('left overlapX', body.overlapX, body.deltaX()); - // use touching instead of blocked? - body.blocked.left = true; - body.touching.left = true; - body.touching.none = false; - } - } - else if (body.deltaX() > 0 && body.checkCollision.right && tile.tile.faceLeft) - { - // RIGHT - body.overlapX = body.right - tile.x; - - // Distance check - if (body.overlapX <= body.deltaX()) - { - // console.log('right overlapX', body.overlapX, body.deltaX()); - body.blocked.right = true; - body.touching.right = true; - body.touching.none = false; - } - } - - if (body.deltaY() < 0 && body.checkCollision.up && tile.tile.faceBottom) - { - // UP - body.overlapY = body.y - tile.bottom; - - // Distance check - if (body.overlapY >= body.deltaY()) - { - // console.log('up overlapY', body.overlapY, body.deltaY()); - body.blocked.up = true; - body.touching.up = true; - body.touching.none = false; - } - } - else if (body.deltaY() > 0 && body.checkCollision.down && tile.tile.faceTop) - { - // DOWN - body.overlapY = body.bottom - tile.y; - - if (body.overlapY <= body.deltaY()) - { - // console.log('down overlapY', body.overlapY, body.deltaY()); - body.blocked.down = true; - body.touching.down = true; - body.touching.none = false; - } - } - - // Separate in a single sweep - - if (body.touching.none) - { - return false; - } - - // if (body.overlapX !== 0) - if (body.touching.left || body.touching.right) - { - body.x -= body.overlapX; - body.preX -= body.overlapX; - - if (body.bounce.x === 0) - { - body.velocity.x = 0; - } - else - { - body.velocity.x = -body.velocity.x * body.bounce.x; - } - } - - // if (body.overlapY !== 0) - if (body.touching.up || body.touching.down) - { - body.y -= body.overlapY; - body.preY -= body.overlapY; - - if (body.bounce.y === 0) - { - body.velocity.y = 0; - } - else - { - body.velocity.y = -body.velocity.y * body.bounce.y; - } - } - - return true; - - }, - /** * Move the given display object towards the destination object at a steady velocity. * If you specify a maxTime then it will adjust the speed (overwriting what you set) so it arrives at the destination in that number of seconds. diff --git a/src/physics/ninja/AABB.js b/src/physics/ninja/AABB.js new file mode 100644 index 00000000..eec3189a --- /dev/null +++ b/src/physics/ninja/AABB.js @@ -0,0 +1,842 @@ +/** +* @author Richard Davey +* @copyright 2014 Photon Storm Ltd. +* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License} +*/ + +/** +* Ninja Physics AABB constructor. +* +* @class Phaser.Physics.Ninja.AABB +* @classdesc Arcade Physics Constructor +* @constructor +* @param {Phaser.Game} game reference to the current game instance. +*/ +Phaser.Physics.Ninja.AABB = function (system, x, y, width, height) { + + this.system = system; + this.pos = new Phaser.Point(x, y); + this.oldpos = new Phaser.Point(x, y); + + this.xw = Math.abs(width / 2); + this.yw = Math.abs(height / 2); + + this.width = width; + this.height = height; + + this.oH = 0; + 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; + this.velocity = new Phaser.Point(); + + // temp collision values + this.px = 0; + this.py = 0; + + // collision mappings + 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; + this.aabbTileProjections[Phaser.Physics.Ninja.Tile.TYPE_CONVEX] = this.projAABB_Convex; + this.aabbTileProjections[Phaser.Physics.Ninja.Tile.TYPE_22DEGs] = this.projAABB_22DegS; + this.aabbTileProjections[Phaser.Physics.Ninja.Tile.TYPE_22DEGb] = this.projAABB_22DegB; + this.aabbTileProjections[Phaser.Physics.Ninja.Tile.TYPE_67DEGs] = this.projAABB_67DegS; + this.aabbTileProjections[Phaser.Physics.Ninja.Tile.TYPE_67DEGb] = this.projAABB_67DegB; + this.aabbTileProjections[Phaser.Physics.Ninja.Tile.TYPE_HALF] = this.projAABB_Half; + +}; + +Phaser.Physics.Ninja.AABB.prototype.constructor = Phaser.Physics.Ninja.AABB; + +Phaser.Physics.Ninja.AABB.COL_NONE = 0; +Phaser.Physics.Ninja.AABB.COL_AXIS = 1; +Phaser.Physics.Ninja.AABB.COL_OTHER = 2; + +Phaser.Physics.Ninja.AABB.prototype = { + + /** + * Updates this AABBs position. + * + * @method Phaser.Physics.Ninja.AABB#integrate + */ + integrate: function () { + + var px = this.pos.x; + 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); + + // store + this.velocity.set(this.pos.x - px, this.pos.y - py); + this.oldpos.set(px, py); + + }, + + /** + * Process a world collision and apply the resulting forces. + * + * @method Phaser.Physics.Ninja.AABB#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 AABB 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, f, fx, fy; + + if (dp < 0) + { + //f = FRICTION; + fx = tx * this.friction; + fy = ty * this.friction; + + b = 1 + this.bounce; + + bx = (nx * b); + by = (ny * b); + + } + else + { + //moving out of collision, do not apply forces + bx = by = fx = fy = 0; + } + + p.x += px;//project object out of collision + p.y += py; + + o.x += px + bx + fx;//apply bounce+friction impulses which alter velocity + o.y += py + by + fy; + + }, + + /** + * Collides this AABB against a Tile. + * + * @method Phaser.Physics.Ninja.AABB#collideAABBVsTile + * @param {Phaser.Physics.Ninja.Tile} tile - The Tile to collide against. + */ + collideAABBVsTile: function (tile) { + + var pos = this.pos; + var c = tile; + + 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.resolveTile(px, py, this, c); + } + } + + return false; + + }, + + /** + * 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. + * + * @method Phaser.Physics.Ninja.AABB#resolveTile + * @param {number} x - Penetration depth on the x axis. + * @param {number} y - Penetration depth on the y axis. + * @param {Phaser.Physics.Ninja.AABB} body - The AABB involved in the collision. + * @param {Phaser.Physics.Ninja.Tile} tile - The Tile involved in the collision. + * @return {boolean} True if the collision was processed, otherwise false. + */ + resolveTile: function (x, y, body, tile) { + + if (0 < tile.id) + { + return this.aabbTileProjections[tile.type](x, y, body, tile); + } + else + { + console.warn("Ninja.AABB.resolveTile was called with an empty (or unknown) tile!: id=" + tile.id + ")"); + return false; + } + + }, + + /** + * Resolves Full tile collision. + * + * @method Phaser.Physics.Ninja.AABB#projAABB_Full + * @param {number} x - Penetration depth on the x axis. + * @param {number} y - Penetration depth on the y axis. + * @param {Phaser.Physics.Ninja.AABB} obj - The AABB involved in the collision. + * @param {Phaser.Physics.Ninja.Tile} t - The Tile involved in the collision. + * @return {number} The result of the collision. + */ + projAABB_Full: function (x, y, obj, t) { + + var l = Math.sqrt(x * x + y * y); + obj.reportCollisionVsWorld(x, y, x / l, y / l, t); + + return Phaser.Physics.Ninja.AABB.COL_AXIS; + + }, + + /** + * Resolves Half tile collision. + * + * @method Phaser.Physics.Ninja.AABB#projAABB_Half + * @param {number} x - Penetration depth on the x axis. + * @param {number} y - Penetration depth on the y axis. + * @param {Phaser.Physics.Ninja.AABB} obj - The AABB involved in the collision. + * @param {Phaser.Physics.Ninja.Tile} t - The Tile involved in the collision. + * @return {number} The result of the collision. + */ + projAABB_Half: function (x, y, obj, t) { + + //signx or signy must be 0; the other must be -1 or 1 + //calculate the projection vector for the half-edge, and then + //(if collision is occuring) pick the minimum + + var sx = t.signx; + var sy = t.signy; + + var ox = (obj.pos.x - (sx*obj.xw)) - t.pos.x;//this gives is the coordinates of the innermost + var oy = (obj.pos.y - (sy*obj.yw)) - t.pos.y;//point on the AABB, relative to the tile center + + //we perform operations analogous to the 45deg tile, except we're using + //an axis-aligned slope instead of an angled one.. + + //if the dotprod of (ox,oy) and (sx,sy) is negative, the corner is in the slope + //and we need toproject it out by the magnitude of the projection of (ox,oy) onto (sx,sy) + var dp = (ox*sx) + (oy*sy); + + if (dp < 0) + { + //collision; project delta onto slope and use this to displace the object + sx *= -dp;//(sx,sy) is now the projection vector + sy *= -dp; + + var lenN = Math.sqrt(sx*sx + sy*sy); + var lenP = Math.sqrt(x*x + y*y); + + if (lenP < lenN) + { + //project along axis; note that we're assuming that this tile is horizontal OR vertical + //relative to the AABB's current tile, and not diagonal OR the current tile. + obj.reportCollisionVsWorld(x,y,x/lenP, y/lenP, t); + + return Phaser.Physics.Ninja.AABB.COL_AXIS; + } + else + { + //note that we could use -= instead of -dp + obj.reportCollisionVsWorld(sx,sy,t.signx, t.signy, t); + + return Phaser.Physics.Ninja.AABB.COL_OTHER; + } + } + + return Phaser.Physics.Ninja.AABB.COL_NONE; + + }, + + /** + * Resolves 45 Degree tile collision. + * + * @method Phaser.Physics.Ninja.AABB#projAABB_45Deg + * @param {number} x - Penetration depth on the x axis. + * @param {number} y - Penetration depth on the y axis. + * @param {Phaser.Physics.Ninja.AABB} obj - The AABB involved in the collision. + * @param {Phaser.Physics.Ninja.Tile} t - The Tile involved in the collision. + * @return {number} The result of the collision. + */ + projAABB_45Deg: function (x, y, obj, t) { + + var signx = t.signx; + var signy = t.signy; + + var ox = (obj.pos.x - (signx*obj.xw)) - t.pos.x;//this gives is the coordinates of the innermost + var oy = (obj.pos.y - (signy*obj.yw)) - t.pos.y;//point on the AABB, relative to the tile center + + var sx = t.sx; + var sy = t.sy; + + //if the dotprod of (ox,oy) and (sx,sy) is negative, the corner is in the slope + //and we need toproject it out by the magnitude of the projection of (ox,oy) onto (sx,sy) + var dp = (ox*sx) + (oy*sy); + + if (dp < 0) + { + //collision; project delta onto slope and use this to displace the object + sx *= -dp;//(sx,sy) is now the projection vector + sy *= -dp; + + var lenN = Math.sqrt(sx*sx + sy*sy); + var lenP = Math.sqrt(x*x + y*y); + + if (lenP < lenN) + { + //project along axis + obj.reportCollisionVsWorld(x,y,x/lenP, y/lenP, t); + + return Phaser.Physics.Ninja.AABB.COL_AXIS; + } + else + { + //project along slope + obj.reportCollisionVsWorld(sx,sy,t.sx,t.sy); + + return Phaser.Physics.Ninja.AABB.COL_OTHER; + } + } + + return Phaser.Physics.Ninja.AABB.COL_NONE; + }, + + /** + * Resolves 22 Degree tile collision. + * + * @method Phaser.Physics.Ninja.AABB#projAABB_22DegS + * @param {number} x - Penetration depth on the x axis. + * @param {number} y - Penetration depth on the y axis. + * @param {Phaser.Physics.Ninja.AABB} obj - The AABB involved in the collision. + * @param {Phaser.Physics.Ninja.Tile} t - The Tile involved in the collision. + * @return {number} The result of the collision. + */ + projAABB_22DegS: function (x, y, obj, t) { + + var signx = t.signx; + var signy = t.signy; + + //first we need to check to make sure we're colliding with the slope at all + var py = obj.pos.y - (signy*obj.yw); + var penY = t.pos.y - py;//this is the vector from the innermost point on the box to the highest point on + //the tile; if it is positive, this means the box is above the tile and + //no collision is occuring + if (0 < (penY*signy)) + { + var ox = (obj.pos.x - (signx*obj.xw)) - (t.pos.x + (signx*t.xw));//this gives is the coordinates of the innermost + var oy = (obj.pos.y - (signy*obj.yw)) - (t.pos.y - (signy*t.yw));//point on the AABB, relative to a point on the slope + + var sx = t.sx;//get slope unit normal + var sy = t.sy; + + //if the dotprod of (ox,oy) and (sx,sy) is negative, the corner is in the slope + //and we need toproject it out by the magnitude of the projection of (ox,oy) onto (sx,sy) + var dp = (ox*sx) + (oy*sy); + + if (dp < 0) + { + //collision; project delta onto slope and use this to displace the object + sx *= -dp;//(sx,sy) is now the projection vector + sy *= -dp; + + var lenN = Math.sqrt(sx*sx + sy*sy); + var lenP = Math.sqrt(x*x + y*y); + + var aY = Math.abs(penY); + + if (lenP < lenN) + { + if (aY < lenP) + { + obj.reportCollisionVsWorld(0, penY, 0, penY/aY, t); + + return Phaser.Physics.Ninja.AABB.COL_OTHER; + } + else + { + obj.reportCollisionVsWorld(x,y,x/lenP, y/lenP, t); + + return Phaser.Physics.Ninja.AABB.COL_AXIS; + } + } + else + { + if (aY < lenN) + { + obj.reportCollisionVsWorld(0, penY, 0, penY/aY, t); + + return Phaser.Physics.Ninja.AABB.COL_OTHER; + } + else + { + obj.reportCollisionVsWorld(sx,sy,t.sx,t.sy,t); + + return Phaser.Physics.Ninja.AABB.COL_OTHER; + } + } + } + } + + //if we've reached this point, no collision has occured + return Phaser.Physics.Ninja.AABB.COL_NONE; + }, + + /** + * Resolves 22 Degree tile collision. + * + * @method Phaser.Physics.Ninja.AABB#projAABB_22DegB + * @param {number} x - Penetration depth on the x axis. + * @param {number} y - Penetration depth on the y axis. + * @param {Phaser.Physics.Ninja.AABB} obj - The AABB involved in the collision. + * @param {Phaser.Physics.Ninja.Tile} t - The Tile involved in the collision. + * @return {number} The result of the collision. + */ + projAABB_22DegB: function (x, y, obj, t) { + + var signx = t.signx; + var signy = t.signy; + + var ox = (obj.pos.x - (signx*obj.xw)) - (t.pos.x - (signx*t.xw));//this gives is the coordinates of the innermost + var oy = (obj.pos.y - (signy*obj.yw)) - (t.pos.y + (signy*t.yw));//point on the AABB, relative to a point on the slope + + var sx = t.sx;//get slope unit normal + var sy = t.sy; + + //if the dotprod of (ox,oy) and (sx,sy) is negative, the corner is in the slope + //and we need toproject it out by the magnitude of the projection of (ox,oy) onto (sx,sy) + var dp = (ox*sx) + (oy*sy); + + if (dp < 0) + { + //collision; project delta onto slope and use this to displace the object + sx *= -dp;//(sx,sy) is now the projection vector + sy *= -dp; + + var lenN = Math.sqrt(sx*sx + sy*sy); + var lenP = Math.sqrt(x*x + y*y); + + if (lenP < lenN) + { + obj.reportCollisionVsWorld(x,y,x/lenP, y/lenP, t); + + return Phaser.Physics.Ninja.AABB.COL_AXIS; + } + else + { + obj.reportCollisionVsWorld(sx,sy,t.sx,t.sy,t); + + return Phaser.Physics.Ninja.AABB.COL_OTHER; + } + + } + + return Phaser.Physics.Ninja.AABB.COL_NONE; + + }, + + /** + * Resolves 67 Degree tile collision. + * + * @method Phaser.Physics.Ninja.AABB#projAABB_67DegS + * @param {number} x - Penetration depth on the x axis. + * @param {number} y - Penetration depth on the y axis. + * @param {Phaser.Physics.Ninja.AABB} obj - The AABB involved in the collision. + * @param {Phaser.Physics.Ninja.Tile} t - The Tile involved in the collision. + * @return {number} The result of the collision. + */ + projAABB_67DegS: function (x, y, obj, t) { + + var signx = t.signx; + var signy = t.signy; + + var px = obj.pos.x - (signx*obj.xw); + var penX = t.pos.x - px; + + if (0 < (penX*signx)) + { + var ox = (obj.pos.x - (signx*obj.xw)) - (t.pos.x - (signx*t.xw));//this gives is the coordinates of the innermost + var oy = (obj.pos.y - (signy*obj.yw)) - (t.pos.y + (signy*t.yw));//point on the AABB, relative to a point on the slope + + var sx = t.sx;//get slope unit normal + var sy = t.sy; + + //if the dotprod of (ox,oy) and (sx,sy) is negative, the corner is in the slope + //and we need to project it out by the magnitude of the projection of (ox,oy) onto (sx,sy) + var dp = (ox*sx) + (oy*sy); + + if (dp < 0) + { + //collision; project delta onto slope and use this to displace the object + sx *= -dp;//(sx,sy) is now the projection vector + sy *= -dp; + + var lenN = Math.sqrt(sx*sx + sy*sy); + var lenP = Math.sqrt(x*x + y*y); + + var aX = Math.abs(penX); + + if (lenP < lenN) + { + if (aX < lenP) + { + obj.reportCollisionVsWorld(penX, 0, penX/aX, 0, t); + + return Phaser.Physics.Ninja.AABB.COL_OTHER; + } + else + { + obj.reportCollisionVsWorld(x,y,x/lenP, y/lenP, t); + + return Phaser.Physics.Ninja.AABB.COL_AXIS; + } + } + else + { + if (aX < lenN) + { + obj.reportCollisionVsWorld(penX, 0, penX/aX, 0, t); + + return Phaser.Physics.Ninja.AABB.COL_OTHER; + } + else + { + obj.reportCollisionVsWorld(sx,sy,t.sx,t.sy,t); + + return Phaser.Physics.Ninja.AABB.COL_OTHER; + } + } + } + } + + //if we've reached this point, no collision has occured + return Phaser.Physics.Ninja.AABB.COL_NONE; + + }, + + /** + * Resolves 67 Degree tile collision. + * + * @method Phaser.Physics.Ninja.AABB#projAABB_67DegB + * @param {number} x - Penetration depth on the x axis. + * @param {number} y - Penetration depth on the y axis. + * @param {Phaser.Physics.Ninja.AABB} obj - The AABB involved in the collision. + * @param {Phaser.Physics.Ninja.Tile} t - The Tile involved in the collision. + * @return {number} The result of the collision. + */ + projAABB_67DegB: function (x, y, obj, t) { + + var signx = t.signx; + var signy = t.signy; + + var ox = (obj.pos.x - (signx*obj.xw)) - (t.pos.x + (signx*t.xw));//this gives is the coordinates of the innermost + var oy = (obj.pos.y - (signy*obj.yw)) - (t.pos.y - (signy*t.yw));//point on the AABB, relative to a point on the slope + + var sx = t.sx;//get slope unit normal + var sy = t.sy; + + //if the dotprod of (ox,oy) and (sx,sy) is negative, the corner is in the slope + //and we need toproject it out by the magnitude of the projection of (ox,oy) onto (sx,sy) + var dp = (ox*sx) + (oy*sy); + + if (dp < 0) + { + //collision; project delta onto slope and use this to displace the object + sx *= -dp;//(sx,sy) is now the projection vector + sy *= -dp; + + var lenN = Math.sqrt(sx*sx + sy*sy); + var lenP = Math.sqrt(x*x + y*y); + + if (lenP < lenN) + { + obj.reportCollisionVsWorld(x,y,x/lenP, y/lenP, t); + + return Phaser.Physics.Ninja.AABB.COL_AXIS; + } + else + { + obj.reportCollisionVsWorld(sx,sy,t.sx,t.sy,t); + + return Phaser.Physics.Ninja.AABB.COL_OTHER; + } + } + + return Phaser.Physics.Ninja.AABB.COL_NONE; + }, + + /** + * Resolves Convex tile collision. + * + * @method Phaser.Physics.Ninja.AABB#projAABB_Convex + * @param {number} x - Penetration depth on the x axis. + * @param {number} y - Penetration depth on the y axis. + * @param {Phaser.Physics.Ninja.AABB} obj - The AABB involved in the collision. + * @param {Phaser.Physics.Ninja.Tile} t - The Tile involved in the collision. + * @return {number} The result of the collision. + */ + projAABB_Convex: function (x, y, obj, t) { + + //if distance from "innermost" corner of AABB is less than than tile radius, + //collision is occuring and we need to project + + var signx = t.signx; + var signy = t.signy; + + var ox = (obj.pos.x - (signx * obj.xw)) - (t.pos.x - (signx * t.xw));//(ox,oy) is the vector from the circle center to + var oy = (obj.pos.y - (signy * obj.yw)) - (t.pos.y - (signy * t.yw));//the AABB + var len = Math.sqrt(ox * ox + oy * oy); + + var twid = t.xw * 2; + var rad = Math.sqrt(twid * twid + 0);//this gives us the radius of a circle centered on the tile's corner and extending to the opposite edge of the tile; + //note that this should be precomputed at compile-time since it's constant + + var pen = rad - len; + + if (((signx * ox) < 0) || ((signy * oy) < 0)) + { + //the test corner is "outside" the 1/4 of the circle we're interested in + var lenP = Math.sqrt(x * x + y * y); + obj.reportCollisionVsWorld(x, y, x / lenP, y / lenP, t); + + return Phaser.Physics.Ninja.AABB.COL_AXIS;//we need to report + } + else if (0 < pen) + { + //project along corner->circle vector + ox /= len; + oy /= len; + obj.reportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return Phaser.Physics.Ninja.AABB.COL_OTHER; + } + + return Phaser.Physics.Ninja.AABB.COL_NONE; + + }, + + /** + * Resolves Concave tile collision. + * + * @method Phaser.Physics.Ninja.AABB#projAABB_Concave + * @param {number} x - Penetration depth on the x axis. + * @param {number} y - Penetration depth on the y axis. + * @param {Phaser.Physics.Ninja.AABB} obj - The AABB involved in the collision. + * @param {Phaser.Physics.Ninja.Tile} t - The Tile involved in the collision. + * @return {number} The result of the collision. + */ + projAABB_Concave: function (x, y, obj, t) { + + //if distance from "innermost" corner of AABB is further than tile radius, + //collision is occuring and we need to project + + var signx = t.signx; + var signy = t.signy; + + var ox = (t.pos.x + (signx * t.xw)) - (obj.pos.x - (signx * obj.xw));//(ox,oy) is the vector form the innermost AABB corner to the + var oy = (t.pos.y + (signy * t.yw)) - (obj.pos.y - (signy * obj.yw));//circle's center + + var twid = t.xw * 2; + var rad = Math.sqrt(twid * twid + 0);//this gives us the radius of a circle centered on the tile's corner and extending to the opposite edge of the tile; + //note that this should be precomputed at compile-time since it's constant + + var len = Math.sqrt(ox * ox + oy * oy); + var pen = len - rad; + + if (0 < pen) + { + //collision; we need to either project along the axes, or project along corner->circlecenter vector + + var lenP = Math.sqrt(x * x + y * y); + + if (lenP < pen) + { + //it's shorter to move along axis directions + obj.reportCollisionVsWorld(x, y, x / lenP, y / lenP, t); + + return Phaser.Physics.Ninja.AABB.COL_AXIS; + } + else + { + //project along corner->circle vector + ox /= len;//len should never be 0, since if it IS 0, rad should be > than len + oy /= len;//and we should never reach here + + obj.reportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return Phaser.Physics.Ninja.AABB.COL_OTHER; + } + + } + + return Phaser.Physics.Ninja.AABB.COL_NONE; + + } + +} diff --git a/src/physics/ninja/Body.js b/src/physics/ninja/Body.js new file mode 100644 index 00000000..a2eb4e3a --- /dev/null +++ b/src/physics/ninja/Body.js @@ -0,0 +1,411 @@ +/** +* @author Richard Davey +* @copyright 2014 Photon Storm Ltd. +* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License} +*/ + +/** +* The Physics Body is linked to a single Sprite. All physics operations should be performed against the body rather than +* the Sprite itself. For example you can set the velocity, bounce values etc all on the Body. +* +* @class Phaser.Physics.Ninja.Body +* @classdesc Ninja Physics Body Constructor +* @constructor +* @param {Phaser.Physics.Ninja} system - The physics system this Body belongs to. +* @param {Phaser.Sprite} sprite - The Sprite object this physics body belongs to. +* @param {number} [type=1] - The type of Ninja shape to create. 1 = AABB, 2 = Circle or 3 = Tile. +* @param {number} [id=1] - If this body is using a Tile shape, you can set the Tile id here, i.e. Phaser.Physics.Ninja.Tile.SLOPE_45DEGpn, Phaser.Physics.Ninja.Tile.CONVEXpp, etc. +*/ +Phaser.Physics.Ninja.Body = function (system, sprite, type, id) { + + if (typeof type === 'undefined') { type = 1; } + if (typeof id === 'undefined') { id = 1; } + + /** + * @property {Phaser.Sprite} sprite - Reference to the parent Sprite. + */ + this.sprite = sprite; + + /** + * @property {Phaser.Game} game - Local reference to game. + */ + this.game = sprite.game; + + /** + * @property {number} type - The type of physics system this body belongs to. + */ + this.type = Phaser.Physics.NINJA; + + /** + * @property {Phaser.Physics.Ninja} system - The parent physics system. + */ + this.system = system; + + /** + * @property {Phaser.Physics.Ninja.AABB} aabb - The AABB object this body is using for collision. + */ + this.aabb = null; + + /** + * @property {Phaser.Physics.Ninja.Tile} tile - The Tile object this body is using for collision. + */ + this.tile = null; + + /** + * @property {Phaser.Physics.Ninja.Circle} circle - The Circle object this body is using for collision. + */ + this.circle = null; + + /** + * @property {object} shape - A local reference to the body shape. + */ + this.shape = null; + + if (type === 1) + { + this.aabb = new Phaser.Physics.Ninja.AABB(system, sprite.x, sprite.y, sprite.width, sprite.height); + this.shape = this.aabb; + } + else if (type === 2) + { + this.circle = new Phaser.Physics.Ninja.Circle(system, sprite.x, sprite.y, sprite.width, sprite.height); + this.shape = this.circle; + } + else if (type === 3) + { + this.tile = new Phaser.Physics.Ninja.Tile(system, sprite.x, sprite.y, sprite.width, sprite.height, id); + this.shape = this.tile; + } + + /** + * @property {Phaser.Point} velocity - The velocity in pixels per second sq. of the Body. + */ + this.velocity = new Phaser.Point(); + + /** + * @property {Phaser.Point} drag - The drag applied to the motion of the Body. + */ + this.drag = new Phaser.Point(); + + /** + * @property {Phaser.Point} bounce - The elasticitiy of the Body when colliding. bounce.x/y = 1 means full rebound, bounce.x/y = 0.5 means 50% rebound velocity. + */ + this.bounce = new Phaser.Point(); + + /** + * @property {boolean} skipQuadTree - If the Body is an irregular shape you can set this to true to avoid it being added to any QuadTrees. + * @default + */ + this.skipQuadTree = false; + + /** + * @property {number} facing - A const reference to the direction the Body is traveling or facing. + * @default + */ + this.facing = Phaser.NONE; + + /** + * @property {boolean} immovable - An immovable Body will not receive any impacts from other bodies. + * @default + */ + this.immovable = false; + + /** + * A Body can be set to collide against the World bounds automatically and rebound back into the World if this is set to true. Otherwise it will leave the World. + * @property {boolean} collideWorldBounds - Should the Body collide with the World bounds? + */ + this.collideWorldBounds = true; + + /** + * This object is populated with boolean values when the Body collides with another. + * touching.up = true means the collision happened to the top of this Body for example. + * @property {object} touching - An object containing touching results. + */ + this.touching = { none: true, up: false, down: false, left: false, right: false }; + + /** + * This object is populated with previous touching values from the bodies previous collision. + * @property {object} wasTouching - An object containing previous touching results. + */ + this.wasTouching = { none: true, up: false, down: false, left: false, right: false }; + + this.maxSpeed = 8; + +}; + +Phaser.Physics.Ninja.Body.prototype = { + + /** + * Internal method. + * + * @method Phaser.Physics.Ninja.Body#updateBounds + * @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); + } + + }, + + /** + * Internal method. + * + * @method Phaser.Physics.Ninja.Body#preUpdate + * @protected + */ + preUpdate: function () { + + // Store and reset collision flags + this.wasTouching.none = this.touching.none; + this.wasTouching.up = this.touching.up; + this.wasTouching.down = this.touching.down; + this.wasTouching.left = this.touching.left; + this.wasTouching.right = this.touching.right; + + this.touching.none = true; + this.touching.up = false; + this.touching.down = false; + this.touching.left = false; + this.touching.right = false; + + this.shape.integrate(); + + if (this.collideWorldBounds) + { + this.shape.collideWorldBounds(); + } + + this.speed = Math.sqrt(this.shape.velocity.x * this.shape.velocity.x + this.shape.velocity.y * this.shape.velocity.y); + this.angle = Math.atan2(this.shape.velocity.y, this.shape.velocity.x); + + }, + + /** + * Internal method. + * + * @method Phaser.Physics.Ninja.Body#postUpdate + * @protected + */ + postUpdate: function () { + + this.sprite.x = this.shape.pos.x; + this.sprite.y = this.shape.pos.y; + + }, + + /** + * Stops all movement of this body. + * + * @method Phaser.Physics.Ninja.Body#setZeroVelocity + */ + setZeroVelocity: function () { + + this.shape.oldpos.x = this.shape.pos.x; + this.shape.oldpos.y = this.shape.pos.y; + + }, + + /** + * Moves the Body forwards based on its current angle and the given speed. + * The speed is represented in pixels per second. So a value of 100 would move 100 pixels in 1 second (1000ms). + * + * @method Phaser.Physics.Body#moveTo + * @param {number} speed - The speed at which it should move forwards. + * @param {number} angle - The angle in which it should move, given in degrees. + */ + moveTo: function (speed, angle) { + + var magnitude = speed * this.game.time.physicsElapsed; + var angle = this.game.math.degToRad(angle); + + this.shape.pos.x = this.shape.oldpos.x + (magnitude * Math.cos(angle)); + this.shape.pos.y = this.shape.oldpos.y + (magnitude * Math.sin(angle)); + + }, + + /** + * Moves the Body backwards based on its current angle and the given speed. + * The speed is represented in pixels per second. So a value of 100 would move 100 pixels in 1 second (1000ms). + * + * @method Phaser.Physics.Body#moveBackward + * @param {number} speed - The speed at which it should move backwards. + * @param {number} angle - The angle in which it should move, given in degrees. + */ + moveFrom: function (speed, angle) { + + var magnitude = -speed * this.game.time.physicsElapsed; + var angle = this.game.math.degToRad(angle); + + this.shape.pos.x = this.shape.oldpos.x + (magnitude * Math.cos(angle)); + this.shape.pos.y = this.shape.oldpos.y + (magnitude * Math.sin(angle)); + + }, + + /** + * If this Body is dynamic then this will move it to the left by setting its x velocity to the given speed. + * The speed is represented in pixels per second. So a value of 100 would move 100 pixels in 1 second (1000ms). + * + * @method Phaser.Physics.Body#moveLeft + * @param {number} speed - The speed at which it should move to the left, in pixels per second. + */ + moveLeft: function (speed) { + + var fx = -speed * this.game.time.physicsElapsed; + + this.shape.pos.x = this.shape.oldpos.x + Math.min(this.maxSpeed, Math.max(-this.maxSpeed, this.shape.pos.x - this.shape.oldpos.x + fx)); + + }, + + /** + * If this Body is dynamic then this will move it to the right by setting its x velocity to the given speed. + * The speed is represented in pixels per second. So a value of 100 would move 100 pixels in 1 second (1000ms). + * + * @method Phaser.Physics.Body#moveRight + * @param {number} speed - The speed at which it should move to the right, in pixels per second. + */ + moveRight: function (speed) { + + var fx = speed * this.game.time.physicsElapsed; + + this.shape.pos.x = this.shape.oldpos.x + Math.min(this.maxSpeed, Math.max(-this.maxSpeed, this.shape.pos.x - this.shape.oldpos.x + fx)); + + }, + + /** + * If this Body is dynamic then this will move it up by setting its y velocity to the given speed. + * The speed is represented in pixels per second. So a value of 100 would move 100 pixels in 1 second (1000ms). + * + * @method Phaser.Physics.Body#moveUp + * @param {number} speed - The speed at which it should move up, in pixels per second. + */ + moveUp: function (speed) { + + var fx = -speed * this.game.time.physicsElapsed; + + this.shape.pos.y = this.shape.oldpos.y + Math.min(this.maxSpeed, Math.max(-this.maxSpeed, this.shape.pos.y - this.shape.oldpos.y + fx)); + + }, + + /** + * If this Body is dynamic then this will move it down by setting its y velocity to the given speed. + * The speed is represented in pixels per second. So a value of 100 would move 100 pixels in 1 second (1000ms). + * + * @method Phaser.Physics.Body#moveDown + * @param {number} speed - The speed at which it should move down, in pixels per second. + */ + moveDown: function (speed) { + + var fx = speed * this.game.time.physicsElapsed; + + this.shape.pos.y = this.shape.oldpos.y + Math.min(this.maxSpeed, Math.max(-this.maxSpeed, this.shape.pos.y - this.shape.oldpos.y + fx)); + + }, + + /** + * Resets all Body values (velocity, acceleration, rotation, etc) + * + * @method Phaser.Physics.Ninja.Body#reset + */ + reset: function () { + }, + +}; + +/** +* @name Phaser.Physics.Ninja.Body#x +* @property {number} x - The x position. +*/ +Object.defineProperty(Phaser.Physics.Ninja.Body.prototype, "x", { + + /** + * The x position. + * @method x + * @return {number} + */ + get: function () { + return this.shape.pos.x; + }, + + /** + * The x position. + * @method x + * @param {number} value + */ + set: function (value) { + this.shape.pos.x = value; + } + +}); + +/** +* @name Phaser.Physics.Ninja.Body#y +* @property {number} y - The y position. +*/ +Object.defineProperty(Phaser.Physics.Ninja.Body.prototype, "y", { + + /** + * The y position. + * @method y + * @return {number} + */ + get: function () { + return this.shape.pos.y; + }, + + /** + * The y position. + * @method y + * @param {number} value + */ + set: function (value) { + this.shape.pos.y = value; + } + +}); + +/** +* @name Phaser.Physics.Ninja.Body#bottom +* @property {number} bottom - The bottom value of this Body (same as Body.y + Body.height) +* @readonly +*/ +Object.defineProperty(Phaser.Physics.Ninja.Body.prototype, "bottom", { + + /** + * The sum of the y and height properties. + * @method bottom + * @return {number} + * @readonly + */ + get: function () { + return this.shape.pos.y + this.shape.height; + } + +}); + +/** +* @name Phaser.Physics.Ninja.Body#right +* @property {number} right - The right value of this Body (same as Body.x + Body.width) +* @readonly +*/ +Object.defineProperty(Phaser.Physics.Ninja.Body.prototype, "right", { + + /** + * The sum of the x and width properties. + * @method right + * @return {number} + * @readonly + */ + get: function () { + return this.shape.pos.x + this.shape.width; + } + +}); diff --git a/src/physics/ninja/Circle.js b/src/physics/ninja/Circle.js new file mode 100644 index 00000000..96aba471 --- /dev/null +++ b/src/physics/ninja/Circle.js @@ -0,0 +1,17 @@ +/** +* @author Richard Davey +* @copyright 2014 Photon Storm Ltd. +* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License} +*/ + +/** +* Ninja Physics Circle constructor. +* +* @class Phaser.Physics.Ninja.Circle +* @classdesc Arcade Physics Constructor +* @constructor +* @param {Phaser.Game} game reference to the current game instance. +*/ +Phaser.Physics.Ninja.Circle = function (system, x, y, width, height) { + +} diff --git a/src/physics/ninja/Tile.js b/src/physics/ninja/Tile.js new file mode 100644 index 00000000..4312115b --- /dev/null +++ b/src/physics/ninja/Tile.js @@ -0,0 +1,616 @@ +/** +* @author Richard Davey +* @copyright 2014 Photon Storm Ltd. +* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License} +*/ + +/** +* Ninja Physics Tile constructor. +* +* @class Phaser.Physics.Ninja.Tile +* @classdesc Arcade Physics Constructor +* @constructor +* @param {Phaser.Game} game reference to the current game instance. +*/ +Phaser.Physics.Ninja.Tile = function (system, x, y, width, height, type) { + + if (typeof type === 'undefined') { type = Phaser.Physics.Ninja.Tile.EMPTY; } + + this.system = system; + + this.id = type; + this.type = Phaser.Physics.Ninja.Tile.TYPE_EMPTY; + + this.pos = new Phaser.Point(x, y); + this.oldpos = new Phaser.Point(x, y); + + if (this.id > 1 && this.id < 30) + { + // Tile Types 2 to 29 require square tile dimensions, so use the width as the base + height = width; + } + + this.xw = Math.abs(width / 2); + this.yw = Math.abs(height / 2); + + this.width = width; + this.height = height; + + this.drag = 1; + this.friction = 0.05; + this.gravityScale = 1; + this.bounce = 0.3; + this.velocity = new Phaser.Point(); + + // This stores tile-specific collision information + this.signx = 0; + this.signy = 0; + this.sx = 0; + this.sy = 0; + + if (this.id > 0) + { + this.setType(this.id); + } + +}; + +Phaser.Physics.Ninja.Tile.prototype.constructor = Phaser.Physics.Ninja.Tile; + +Phaser.Physics.Ninja.Tile.prototype = { + + /** + * Updates this AABBs position. + * + * @method Phaser.Physics.Ninja.AABB#integrate + */ + integrate: function () { + + var px = this.pos.x; + var py = this.pos.y; + + 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.velocity.set(this.pos.x - px, this.pos.y - py); + this.oldpos.set(px, py); + + }, + + collideWorldBounds: function () { + }, + + //these functions are used to update the cell + //note: id is assumed to NOT be "empty" state.. + //if it IS the empty state, the tile clears itself + setType: function (id) { + + if (id === Phaser.Physics.Ninja.Tile.EMPTY) + { + this.clear(); + } + else + { + //set tile state to a non-emtpy value, and update it's edges and those of the neighbors + this.id = id; + this.updateType(); + } + + return this; + + }, + + clear: function () { + + this.id = Phaser.Physics.Ninja.Tile.EMPTY; + this.updateType(); + + }, + + render: function (context, key) { + + if (this.id === 0) + { + return; + } + + var image = this.system.game.cache.getImage(key); + var data = this.system.game.cache.getFrameData(key); + var frame = data._frames[this.id - 1]; + + context.drawImage(image, frame.x, frame.y, frame.width, frame.height, this.x, this.y, this.width, this.height); + + // context.beginPath(); + // context.strokeStyle = 'rgb(255,255,0)'; + // context.strokeRect(this.x, this.y, this.width, this.height); + // context.strokeRect(this.pos.x, this.pos.y, 2, 2); // center point + // context.closePath(); + + }, + + //this converts a tile from implicitly-defined (via id), to explicit (via properties) + updateType: function () { + + if (this.id === 0) + { + //EMPTY + this.type = Phaser.Physics.Ninja.Tile.TYPE_EMPTY; + this.signx = 0; + this.signy = 0; + this.sx = 0; + this.sy = 0; + + return true; + } + + //tile is non-empty; collide + if (this.id < Phaser.Physics.Ninja.Tile.TYPE_45DEG) + { + //FULL + this.type = Phaser.Physics.Ninja.Tile.TYPE_FULL; + this.signx = 0; + this.signy = 0; + this.sx = 0; + this.sy = 0; + } + else if (this.id < Phaser.Physics.Ninja.Tile.TYPE_CONCAVE) + { + // 45deg + this.type = Phaser.Physics.Ninja.Tile.TYPE_45DEG; + + if (this.id == Phaser.Physics.Ninja.Tile.SLOPE_45DEGpn) + { + this.signx = 1; + this.signy = -1; + this.sx = this.signx / Math.SQRT2;//get slope _unit_ normal + this.sy = this.signy / Math.SQRT2;//since normal is (1,-1), length is sqrt(1*1 + -1*-1) = sqrt(2) + } + else if (this.id == Phaser.Physics.Ninja.Tile.SLOPE_45DEGnn) + { + this.signx = -1; + this.signy = -1; + this.sx = this.signx / Math.SQRT2;//get slope _unit_ normal + this.sy = this.signy / Math.SQRT2;//since normal is (1,-1), length is sqrt(1*1 + -1*-1) = sqrt(2) + } + else if (this.id == Phaser.Physics.Ninja.Tile.SLOPE_45DEGnp) + { + this.signx = -1; + this.signy = 1; + this.sx = this.signx / Math.SQRT2;//get slope _unit_ normal + this.sy = this.signy / Math.SQRT2;//since normal is (1,-1), length is sqrt(1*1 + -1*-1) = sqrt(2) + } + else if (this.id == Phaser.Physics.Ninja.Tile.SLOPE_45DEGpp) + { + this.signx = 1; + this.signy = 1; + this.sx = this.signx / Math.SQRT2;//get slope _unit_ normal + this.sy = this.signy / Math.SQRT2;//since normal is (1,-1), length is sqrt(1*1 + -1*-1) = sqrt(2) + } + else + { + return false; + } + } + else if (this.id < Phaser.Physics.Ninja.Tile.TYPE_CONVEX) + { + // Concave + this.type = Phaser.Physics.Ninja.Tile.TYPE_CONCAVE; + + if (this.id == Phaser.Physics.Ninja.Tile.CONCAVEpn) + { + this.signx = 1; + this.signy = -1; + this.sx = 0; + this.sy = 0; + } + else if (this.id == Phaser.Physics.Ninja.Tile.CONCAVEnn) + { + this.signx = -1; + this.signy = -1; + this.sx = 0; + this.sy = 0; + } + else if (this.id == Phaser.Physics.Ninja.Tile.CONCAVEnp) + { + this.signx = -1; + this.signy = 1; + this.sx = 0; + this.sy = 0; + } + else if (this.id == Phaser.Physics.Ninja.Tile.CONCAVEpp) + { + this.signx = 1; + this.signy = 1; + this.sx = 0; + this.sy = 0; + } + else + { + return false; + } + } + else if (this.id < Phaser.Physics.Ninja.Tile.TYPE_22DEGs) + { + // Convex + this.type = Phaser.Physics.Ninja.Tile.TYPE_CONVEX; + + if (this.id == Phaser.Physics.Ninja.Tile.CONVEXpn) + { + this.signx = 1; + this.signy = -1; + this.sx = 0; + this.sy = 0; + } + else if (this.id == Phaser.Physics.Ninja.Tile.CONVEXnn) + { + this.signx = -1; + this.signy = -1; + this.sx = 0; + this.sy = 0; + } + else if (this.id == Phaser.Physics.Ninja.Tile.CONVEXnp) + { + this.signx = -1; + this.signy = 1; + this.sx = 0; + this.sy = 0; + } + else if (this.id == Phaser.Physics.Ninja.Tile.CONVEXpp) + { + this.signx = 1; + this.signy = 1; + this.sx = 0; + this.sy = 0; + } + else + { + return false; + } + } + else if (this.id < Phaser.Physics.Ninja.Tile.TYPE_22DEGb) + { + // 22deg small + this.type = Phaser.Physics.Ninja.Tile.TYPE_22DEGs; + + if (this.id == Phaser.Physics.Ninja.Tile.SLOPE_22DEGpnS) + { + this.signx = 1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } + else if (this.id == Phaser.Physics.Ninja.Tile.SLOPE_22DEGnnS) + { + this.signx = -1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } + else if (this.id == Phaser.Physics.Ninja.Tile.SLOPE_22DEGnpS) + { + this.signx = -1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } + else if (this.id == Phaser.Physics.Ninja.Tile.SLOPE_22DEGppS) + { + this.signx = 1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } + else + { + return false; + } + } + else if (this.id < Phaser.Physics.Ninja.Tile.TYPE_67DEGs) + { + // 22deg big + this.type = Phaser.Physics.Ninja.Tile.TYPE_22DEGb; + + if (this.id == Phaser.Physics.Ninja.Tile.SLOPE_22DEGpnB) + { + this.signx = 1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } + else if (this.id == Phaser.Physics.Ninja.Tile.SLOPE_22DEGnnB) + { + this.signx = -1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } + else if (this.id == Phaser.Physics.Ninja.Tile.SLOPE_22DEGnpB) + { + this.signx = -1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } + else if (this.id == Phaser.Physics.Ninja.Tile.SLOPE_22DEGppB) + { + this.signx = 1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } + else + { + return false; + } + } + else if (this.id < Phaser.Physics.Ninja.Tile.TYPE_67DEGb) + { + // 67deg small + this.type = Phaser.Physics.Ninja.Tile.TYPE_67DEGs; + + if (this.id == Phaser.Physics.Ninja.Tile.SLOPE_67DEGpnS) + { + this.signx = 1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } + else if (this.id == Phaser.Physics.Ninja.Tile.SLOPE_67DEGnnS) + { + this.signx = -1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } + else if (this.id == Phaser.Physics.Ninja.Tile.SLOPE_67DEGnpS) + { + this.signx = -1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } + else if (this.id == Phaser.Physics.Ninja.Tile.SLOPE_67DEGppS) + { + this.signx = 1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } + else + { + return false; + } + } + else if (this.id < Phaser.Physics.Ninja.Tile.TYPE_HALF) + { + // 67deg big + this.type = Phaser.Physics.Ninja.Tile.TYPE_67DEGb; + + if (this.id == Phaser.Physics.Ninja.Tile.SLOPE_67DEGpnB) + { + this.signx = 1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } + else if (this.id == Phaser.Physics.Ninja.Tile.SLOPE_67DEGnnB) + { + this.signx = -1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } + else if (this.id == Phaser.Physics.Ninja.Tile.SLOPE_67DEGnpB) + { + this.signx = -1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } + else if (this.id == Phaser.Physics.Ninja.Tile.SLOPE_67DEGppB) + { + this.signx = 1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } + else + { + return false; + } + } + else + { + // Half-full tile + this.type = Phaser.Physics.Ninja.Tile.TYPE_HALF; + + if (this.id == Phaser.Physics.Ninja.Tile.HALFd) + { + this.signx = 0; + this.signy = -1; + this.sx = this.signx; + this.sy = this.signy; + } + else if (this.id == Phaser.Physics.Ninja.Tile.HALFu) + { + this.signx = 0; + this.signy = 1; + this.sx = this.signx; + this.sy = this.signy; + } + else if (this.id == Phaser.Physics.Ninja.Tile.HALFl) + { + this.signx = 1; + this.signy = 0; + this.sx = this.signx; + this.sy = this.signy; + } + else if (this.id == Phaser.Physics.Ninja.Tile.HALFr) + { + this.signx = -1; + this.signy = 0; + this.sx = this.signx; + this.sy = this.signy; + } + else + { + return false; + } + } + } + +} + +/** +* @name Phaser.Physics.Ninja.Tile#x +* @property {number} x - The x position. +*/ +Object.defineProperty(Phaser.Physics.Ninja.Tile.prototype, "x", { + + /** + * The x position. + * @method x + * @return {number} + */ + get: function () { + return this.pos.x - this.xw; + }, + + /** + * The x position. + * @method x + * @param {number} value + */ + set: function (value) { + this.pos.x = value; + } + +}); + +/** +* @name Phaser.Physics.Ninja.Tile#y +* @property {number} y - The y position. +*/ +Object.defineProperty(Phaser.Physics.Ninja.Tile.prototype, "y", { + + /** + * The y position. + * @method y + * @return {number} + */ + get: function () { + return this.pos.y - this.yw; + }, + + /** + * The y position. + * @method y + * @param {number} value + */ + set: function (value) { + this.pos.y = value; + } + +}); + +/** +* @name Phaser.Physics.Ninja.Tile#bottom +* @property {number} bottom - The bottom value of this Body (same as Body.y + Body.height) +* @readonly +*/ +Object.defineProperty(Phaser.Physics.Ninja.Tile.prototype, "bottom", { + + /** + * The sum of the y and height properties. + * @method bottom + * @return {number} + * @readonly + */ + get: function () { + return this.pos.y + this.yw; + } + +}); + +/** +* @name Phaser.Physics.Ninja.Tile#right +* @property {number} right - The right value of this Body (same as Body.x + Body.width) +* @readonly +*/ +Object.defineProperty(Phaser.Physics.Ninja.Tile.prototype, "right", { + + /** + * The sum of the x and width properties. + * @method right + * @return {number} + * @readonly + */ + get: function () { + return this.pos.x + this.xw; + } + +}); + +// TILETYPE ENUMERATION +Phaser.Physics.Ninja.Tile.EMPTY = 0; +Phaser.Physics.Ninja.Tile.FULL = 1;//fullAABB tile +Phaser.Physics.Ninja.Tile.SLOPE_45DEGpn = 2;//45-degree triangle, whose normal is (+ve,-ve) +Phaser.Physics.Ninja.Tile.SLOPE_45DEGnn = 3;//(+ve,+ve) +Phaser.Physics.Ninja.Tile.SLOPE_45DEGnp = 4;//(-ve,+ve) +Phaser.Physics.Ninja.Tile.SLOPE_45DEGpp = 5;//(-ve,-ve) +Phaser.Physics.Ninja.Tile.CONCAVEpn = 6;//1/4-circle cutout +Phaser.Physics.Ninja.Tile.CONCAVEnn = 7; +Phaser.Physics.Ninja.Tile.CONCAVEnp = 8; +Phaser.Physics.Ninja.Tile.CONCAVEpp = 9; +Phaser.Physics.Ninja.Tile.CONVEXpn = 10;//1/4/circle +Phaser.Physics.Ninja.Tile.CONVEXnn = 11; +Phaser.Physics.Ninja.Tile.CONVEXnp = 12; +Phaser.Physics.Ninja.Tile.CONVEXpp = 13; +Phaser.Physics.Ninja.Tile.SLOPE_22DEGpnS = 14;//22.5 degree slope +Phaser.Physics.Ninja.Tile.SLOPE_22DEGnnS = 15; +Phaser.Physics.Ninja.Tile.SLOPE_22DEGnpS = 16; +Phaser.Physics.Ninja.Tile.SLOPE_22DEGppS = 17; +Phaser.Physics.Ninja.Tile.SLOPE_22DEGpnB = 18; +Phaser.Physics.Ninja.Tile.SLOPE_22DEGnnB = 19; +Phaser.Physics.Ninja.Tile.SLOPE_22DEGnpB = 20; +Phaser.Physics.Ninja.Tile.SLOPE_22DEGppB = 21; +Phaser.Physics.Ninja.Tile.SLOPE_67DEGpnS = 22;//67.5 degree slope +Phaser.Physics.Ninja.Tile.SLOPE_67DEGnnS = 23; +Phaser.Physics.Ninja.Tile.SLOPE_67DEGnpS = 24; +Phaser.Physics.Ninja.Tile.SLOPE_67DEGppS = 25; +Phaser.Physics.Ninja.Tile.SLOPE_67DEGpnB = 26; +Phaser.Physics.Ninja.Tile.SLOPE_67DEGnnB = 27; +Phaser.Physics.Ninja.Tile.SLOPE_67DEGnpB = 28; +Phaser.Physics.Ninja.Tile.SLOPE_67DEGppB = 29; +Phaser.Physics.Ninja.Tile.HALFd = 30;//half-full tiles +Phaser.Physics.Ninja.Tile.HALFr = 31; +Phaser.Physics.Ninja.Tile.HALFu = 32; +Phaser.Physics.Ninja.Tile.HALFl = 33; + +//collision shape "types" +Phaser.Physics.Ninja.Tile.TYPE_EMPTY = 0; +Phaser.Physics.Ninja.Tile.TYPE_FULL = 1; +Phaser.Physics.Ninja.Tile.TYPE_45DEG = 2; +Phaser.Physics.Ninja.Tile.TYPE_CONCAVE = 6; +Phaser.Physics.Ninja.Tile.TYPE_CONVEX = 10; +Phaser.Physics.Ninja.Tile.TYPE_22DEGs = 14; +Phaser.Physics.Ninja.Tile.TYPE_22DEGb = 18; +Phaser.Physics.Ninja.Tile.TYPE_67DEGs = 22; +Phaser.Physics.Ninja.Tile.TYPE_67DEGb = 26; +Phaser.Physics.Ninja.Tile.TYPE_HALF = 30; diff --git a/src/physics/ninja/World.js b/src/physics/ninja/World.js new file mode 100644 index 00000000..638f2090 --- /dev/null +++ b/src/physics/ninja/World.js @@ -0,0 +1,449 @@ +/** +* @author Richard Davey +* @copyright 2014 Photon Storm Ltd. +* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License} +*/ + +/** +* Ninja Physics constructor. +* +* @class Phaser.Physics.Ninja +* @classdesc Arcade Physics Constructor +* @constructor +* @param {Phaser.Game} game reference to the current game instance. +*/ +Phaser.Physics.Ninja = function (game) { + + /** + * @property {Phaser.Game} game - Local reference to game. + */ + this.game = game; + + this.time = this.game.time; + + /** + * @property {number} gravity - The World gravity setting. + */ + this.gravity = 0.2; + + /** + * @property {Phaser.Rectangle} bounds - The bounds inside of which the physics world exists. Defaults to match the world bounds. + */ + this.bounds = new Phaser.Rectangle(0, 0, game.world.width, game.world.height); + + /** + * @property {Phaser.QuadTree} quadTree - The world QuadTree. + */ + // this.quadTree = new Phaser.Physics.Ninja.QuadTree(this, this.game.world.bounds.x, this.game.world.bounds.y, this.game.world.bounds.width, this.game.world.bounds.height, this.maxObjects, this.maxLevels); + + /** + * @property {Array} _mapData - Internal cache var. + * @private + */ + this._mapData = []; + +}; + +Phaser.Physics.Ninja.prototype.constructor = Phaser.Physics.Ninja; + +Phaser.Physics.Ninja.prototype = { + + /** + * This will create a Ninja Physics AABB body on the given game object. Its dimensions will match the width and height of the object at the point it is created. + * A game object can only have 1 physics body active at any one time, and it can't be changed until the object is destroyed. + * + * @method Phaser.Physics.Ninja#enableAABB + * @param {object|array} object - The game object to create the physics body on. Can also be an array of objects, a body will be created on every object in the array. + */ + enableAABB: function (object) { + + this.enable(object, 1); + + }, + + /** + * This will create a Ninja Physics Circle body on the given game object. + * A game object can only have 1 physics body active at any one time, and it can't be changed until the object is destroyed. + * + * @method Phaser.Physics.Ninja#enableCircle + * @param {object|array} object - The game object to create the physics body on. Can also be an array of objects, a body will be created on every object in the array. + * @param {number} radius - The radius of the Circle. + */ + enableCircle: function (object, radius) { + + this.enable(object, 2, 0, radius); + + }, + + /** + * This will create a Ninja Physics Tile body on the given game object. There are 34 different types of tile you can create, including 45 degree slopes, + * convex and concave circles and more. The id parameter controls which Tile type is created, but you can also change it at run-time. + * Note that for all degree based tile types they need to have an equal width and height. If the given object doesn't have equal width and height it will use the width. + * A game object can only have 1 physics body active at any one time, and it can't be changed until the object is destroyed. + * + * @method Phaser.Physics.Ninja#enableTile + * @param {object|array} object - The game object to create the physics body on. Can also be an array of objects, a body will be created on every object in the array. + * @param {number} radius - The radius of the Circle. + */ + enableTile: function (object, id) { + + this.enable(object, 3, id); + + }, + + /** + * This will create a Ninja Physics Tile body on the given game object. There are 34 different types of tile you can create, including 45 degree slopes, + * convex and concave circles and more. The id parameter controls which Tile type is created, but you can also change it at run-time. + * Note that for all degree based tile types they need to have an equal width and height. If the given object doesn't have equal width and height it will use the width. + * A game object can only have 1 physics body active at any one time, and it can't be changed until the object is destroyed. + * + * @method Phaser.Physics.Ninja#enable + * @param {object|array} object - The game object to create the physics body on. Can also be an array of objects, a body will be created on every object in the array. + * @param {number} [type=1] - The type of Ninja shape to create. 1 = AABB, 2 = Circle or 3 = Tile. + * @param {number} [id=1] - If this body is using a Tile shape, you can set the Tile id here, i.e. Phaser.Physics.Ninja.Tile.SLOPE_45DEGpn, Phaser.Physics.Ninja.Tile.CONVEXpp, etc. + * @param {number} [radius=0] - If this body is using a Circle shape this controls the radius. + */ + enable: function (object, type, id, radius) { + + if (typeof type === 'undefined') { type = 1; } + if (typeof id === 'undefined') { id = 1; } + + var i = 1; + + if (Array.isArray(object)) + { + // Add to Group + i = object.length; + } + else + { + object = [object]; + } + + while (i--) + { + if (object[i].body === null) + { + object[i].body = new Phaser.Physics.Ninja.Body(this, object[i], type, id); + object[i].anchor.set(0.5); + } + } + + }, + + /** + * Called automatically by a Physics body, it updates all motion related values on the Body. + * + * @method Phaser.Physics.Ninja#updateMotion + * @param {Phaser.Physics.Ninja.Body} The Body object to be updated. + */ + update: function () { + }, + + /** + * Checks for overlaps between two game objects. The objects can be Sprites, Groups or Emitters. + * You can perform Sprite vs. Sprite, Sprite vs. Group and Group vs. Group overlap checks. + * Unlike collide the objects are NOT automatically separated or have any physics applied, they merely test for overlap results. + * The second parameter can be an array of objects, of differing types. + * + * @method Phaser.Physics.Arcade#overlap + * @param {Phaser.Sprite|Phaser.Group|Phaser.Particles.Emitter} object1 - The first object to check. Can be an instance of Phaser.Sprite, Phaser.Group or Phaser.Particles.Emitter. + * @param {Phaser.Sprite|Phaser.Group|Phaser.Particles.Emitter|array} object2 - The second object or array of objects to check. Can be Phaser.Sprite, Phaser.Group or Phaser.Particles.Emitter. + * @param {function} [overlapCallback=null] - An optional 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 specified them. + * @param {function} [processCallback=null] - A callback function that lets you perform additional checks against the two objects if they overlap. If this is set then overlapCallback will only be called if processCallback returns true. + * @param {object} [callbackContext] - The context in which to run the callbacks. + * @returns {boolean} True if an overlap occured otherwise false. + */ + overlap: function (object1, object2, overlapCallback, processCallback, callbackContext) { + + overlapCallback = overlapCallback || null; + processCallback = processCallback || null; + callbackContext = callbackContext || overlapCallback; + + this._result = false; + this._total = 0; + + if (Array.isArray(object2)) + { + for (var i = 0, len = object2.length; i < len; i++) + { + this.collideHandler(object1, object2[i], overlapCallback, processCallback, callbackContext, true); + } + } + else + { + this.collideHandler(object1, object2, overlapCallback, processCallback, callbackContext, true); + } + + return (this._total > 0); + + }, + + /** + * Checks for collision between two game objects. You can perform Sprite vs. Sprite, Sprite vs. Group, Group vs. Group, Sprite vs. Tilemap Layer or Group vs. Tilemap Layer collisions. + * The second parameter can be an array of objects, of differing types. + * The objects are also automatically separated. If you don't require separation then use ArcadePhysics.overlap instead. + * An optional processCallback can be provided. If given this function will be called when two sprites are found to be colliding. It is called before any separation takes place, + * giving you the chance to perform additional checks. If the function returns true then the collision and separation is carried out. If it returns false it is skipped. + * The collideCallback is an optional function that is only called if two sprites collide. If a processCallback has been set then it needs to return true for collideCallback to be called. + * + * @method Phaser.Physics.Ninja#collide + * @param {Phaser.Sprite|Phaser.Group|Phaser.Particles.Emitter|Phaser.Tilemap} object1 - The first object to check. Can be an instance of Phaser.Sprite, Phaser.Group, Phaser.Particles.Emitter, or Phaser.Tilemap. + * @param {Phaser.Sprite|Phaser.Group|Phaser.Particles.Emitter|Phaser.Tilemap|array} object2 - The second object or array of objects to check. Can be Phaser.Sprite, Phaser.Group, Phaser.Particles.Emitter or Phaser.Tilemap. + * @param {function} [collideCallback=null] - An optional callback function that is called if the objects collide. The two objects will be passed to this function in the same order in which you specified them. + * @param {function} [processCallback=null] - A callback function that lets you perform additional checks against the two objects if they overlap. If this is set then collision will only happen if processCallback returns true. The two objects will be passed to this function in the same order in which you specified them. + * @param {object} [callbackContext] - The context in which to run the callbacks. + * @returns {boolean} True if a collision occured otherwise false. + */ + collide: function (object1, object2, collideCallback, processCallback, callbackContext) { + + collideCallback = collideCallback || null; + processCallback = processCallback || null; + callbackContext = callbackContext || collideCallback; + + this._result = false; + this._total = 0; + + if (Array.isArray(object2)) + { + for (var i = 0, len = object2.length; i < len; i++) + { + this.collideHandler(object1, object2[i], collideCallback, processCallback, callbackContext, false); + } + } + else + { + this.collideHandler(object1, object2, collideCallback, processCallback, callbackContext, false); + } + + return (this._total > 0); + + }, + + /** + * Internal collision handler. + * + * @method Phaser.Physics.Arcade#collideHandler + * @private + * @param {Phaser.Sprite|Phaser.Group|Phaser.Particles.Emitter|Phaser.Tilemap} object1 - The first object to check. Can be an instance of Phaser.Sprite, Phaser.Group, Phaser.Particles.Emitter, or Phaser.Tilemap. + * @param {Phaser.Sprite|Phaser.Group|Phaser.Particles.Emitter|Phaser.Tilemap} object2 - The second object to check. Can be an instance of Phaser.Sprite, Phaser.Group, Phaser.Particles.Emitter or Phaser.Tilemap. Can also be an array of objects to check. + * @param {function} collideCallback - An optional callback function that is called if the objects collide. The two objects will be passed to this function in the same order in which you specified them. + * @param {function} processCallback - A callback function that lets you perform additional checks against the two objects if they overlap. If this is set then collision will only happen if processCallback returns true. The two objects will be passed to this function in the same order in which you specified them. + * @param {object} callbackContext - The context in which to run the callbacks. + * @param {boolean} overlapOnly - Just run an overlap or a full collision. + */ + collideHandler: function (object1, object2, collideCallback, processCallback, callbackContext, overlapOnly) { + + // Only collide valid objects + if (typeof object2 === 'undefined' && (object1.type === Phaser.GROUP || object1.type === Phaser.EMITTER)) + { + this.collideGroupVsSelf(object1, collideCallback, processCallback, callbackContext, overlapOnly); + return; + } + + if (object1 && object2 && object1.exists && object2.exists) + { + // SPRITES + if (object1.type == Phaser.SPRITE || object1.type == Phaser.TILESPRITE) + { + if (object2.type == Phaser.SPRITE || object2.type == Phaser.TILESPRITE) + { + this.collideSpriteVsSprite(object1, object2, collideCallback, processCallback, callbackContext, overlapOnly); + } + else if (object2.type == Phaser.GROUP || object2.type == Phaser.EMITTER) + { + this.collideSpriteVsGroup(object1, object2, collideCallback, processCallback, callbackContext, overlapOnly); + } + else if (object2.type == Phaser.TILEMAPLAYER) + { + this.collideSpriteVsTilemapLayer(object1, object2, collideCallback, processCallback, callbackContext); + } + } + // GROUPS + else if (object1.type == Phaser.GROUP) + { + if (object2.type == Phaser.SPRITE || object2.type == Phaser.TILESPRITE) + { + this.collideSpriteVsGroup(object2, object1, collideCallback, processCallback, callbackContext, overlapOnly); + } + else if (object2.type == Phaser.GROUP || object2.type == Phaser.EMITTER) + { + this.collideGroupVsGroup(object1, object2, collideCallback, processCallback, callbackContext, overlapOnly); + } + else if (object2.type == Phaser.TILEMAPLAYER) + { + this.collideGroupVsTilemapLayer(object1, object2, collideCallback, processCallback, callbackContext); + } + } + // TILEMAP LAYERS + else if (object1.type == Phaser.TILEMAPLAYER) + { + if (object2.type == Phaser.SPRITE || object2.type == Phaser.TILESPRITE) + { + this.collideSpriteVsTilemapLayer(object2, object1, collideCallback, processCallback, callbackContext); + } + else if (object2.type == Phaser.GROUP || object2.type == Phaser.EMITTER) + { + this.collideGroupVsTilemapLayer(object2, object1, collideCallback, processCallback, callbackContext); + } + } + // EMITTER + else if (object1.type == Phaser.EMITTER) + { + if (object2.type == Phaser.SPRITE || object2.type == Phaser.TILESPRITE) + { + this.collideSpriteVsGroup(object2, object1, collideCallback, processCallback, callbackContext, overlapOnly); + } + else if (object2.type == Phaser.GROUP || object2.type == Phaser.EMITTER) + { + this.collideGroupVsGroup(object1, object2, collideCallback, processCallback, callbackContext, overlapOnly); + } + else if (object2.type == Phaser.TILEMAPLAYER) + { + this.collideGroupVsTilemapLayer(object1, object2, collideCallback, processCallback, callbackContext); + } + } + } + + }, + + /** + * An internal function. Use Phaser.Physics.Arcade.collide instead. + * + * @method Phaser.Physics.Arcade#collideSpriteVsSprite + * @private + */ + collideSpriteVsSprite: function (sprite1, sprite2, collideCallback, processCallback, callbackContext, overlapOnly) { + + if (this.separate(sprite1.body, sprite2.body, processCallback, callbackContext, overlapOnly)) + { + if (collideCallback) + { + collideCallback.call(callbackContext, sprite1, sprite2); + } + + this._total++; + } + + }, + + /** + * An internal function. Use Phaser.Physics.Arcade.collide instead. + * + * @method Phaser.Physics.Arcade#collideSpriteVsGroup + * @private + */ + collideSpriteVsGroup: function (sprite, group, collideCallback, processCallback, callbackContext, overlapOnly) { + + if (group.length === 0) + { + return; + } + + // What is the sprite colliding with in the quadtree? + // this.quadTree.clear(); + + // this.quadTree = new Phaser.QuadTree(this.game.world.bounds.x, this.game.world.bounds.y, this.game.world.bounds.width, this.game.world.bounds.height, this.maxObjects, this.maxLevels); + + // this.quadTree.populate(group); + + // this._potentials = this.quadTree.retrieve(sprite); + + for (var i = 0, len = group.children.length; i < len; i++) + { + // We have our potential suspects, are they in this group? + if (group.children[i].exists && group.children[i].body && this.separate(sprite.body, group.children[i].body, processCallback, callbackContext, overlapOnly)) + { + if (collideCallback) + { + collideCallback.call(callbackContext, sprite, group.children[i]); + } + + this._total++; + } + } + + }, + + /** + * An internal function. Use Phaser.Physics.Arcade.collide instead. + * + * @method Phaser.Physics.Arcade#collideGroupVsSelf + * @private + */ + collideGroupVsSelf: function (group, collideCallback, processCallback, callbackContext, overlapOnly) { + + if (group.length === 0) + { + return; + } + + var len = group.children.length; + + for (var i = 0; i < len; i++) + { + for (var j = i + 1; j <= len; j++) + { + if (group.children[i] && group.children[j] && group.children[i].exists && group.children[j].exists) + { + this.collideSpriteVsSprite(group.children[i], group.children[j], collideCallback, processCallback, callbackContext, overlapOnly); + } + } + } + + }, + + /** + * An internal function. Use Phaser.Physics.Arcade.collide instead. + * + * @method Phaser.Physics.Arcade#collideGroupVsGroup + * @private + */ + collideGroupVsGroup: function (group1, group2, collideCallback, processCallback, callbackContext, overlapOnly) { + + if (group1.length === 0 || group2.length === 0) + { + return; + } + + for (var i = 0, len = group1.children.length; i < len; i++) + { + if (group1.children[i].exists) + { + this.collideSpriteVsGroup(group1.children[i], group2, collideCallback, processCallback, callbackContext, overlapOnly); + } + } + + }, + + /** + * The core separation function to separate two physics bodies. + * @method Phaser.Physics.Arcade#separate + * @param {Phaser.Physics.Arcade.Body} body1 - The Body object to separate. + * @param {Phaser.Physics.Arcade.Body} body2 - The Body object to separate. + * @param {function} [processCallback=null] - UN-USED: A callback function that lets you perform additional checks against the two objects if they overlap. If this function is set then the sprites will only be collided if it returns true. + * @param {object} [callbackContext] - UN-USED: The context in which to run the process callback. + * @returns {boolean} Returns true if the bodies collided, otherwise false. + */ + separate: function (body1, body2, processCallback, callbackContext, overlapOnly) { + + if (body1.type !== Phaser.Physics.NINJA || body2.type !== Phaser.Physics.NINJA) + { + return; + } + + if (body1.aabb && body2.tile) + { + return body1.aabb.collideAABBVsTile(body2.tile); + } + + if (body1.tile && body2.aabb) + { + return body2.aabb.collideAABBVsTile(body1.tile); + } + + // circles next + + } + +}; diff --git a/src/physics/p2/Body.js b/src/physics/p2/Body.js index 17a6cd66..a1f37893 100644 --- a/src/physics/p2/Body.js +++ b/src/physics/p2/Body.js @@ -37,6 +37,11 @@ Phaser.Physics.Body = function (game, sprite, x, y, mass) { */ this.sprite = sprite; + /** + * @property {number} type - The type of physics system this body belongs to. + */ + this.type = Phaser.Physics.P2; + /** * @property {Phaser.Point} offset - The offset of the Physics Body from the Sprite x/y position. */ diff --git a/src/tilemap/Tile.js b/src/tilemap/Tile.js index e4c5c4bf..f68c2eea 100644 --- a/src/tilemap/Tile.js +++ b/src/tilemap/Tile.js @@ -39,6 +39,16 @@ Phaser.Tile = function (layer, index, x, y, width, height) { */ this.y = y; + /** + * @property {number} x - The x map coordinate of this tile. + */ + this.worldX = x * width; + + /** + * @property {number} y - The y map coordinate of this tile. + */ + this.worldY = y * height; + /** * @property {number} width - The width of the tile in pixels. */ @@ -49,6 +59,16 @@ Phaser.Tile = function (layer, index, x, y, width, height) { */ this.height = height; + /** + * @property {number} width - The width of the tile in pixels. + */ + this.centerX = Math.abs(width / 2); + + /** + * @property {number} height - The height of the tile in pixels. + */ + this.centerY = Math.abs(height / 2); + /** * @property {number} alpha - The alpha value at which this tile is drawn to the canvas. */ @@ -135,6 +155,32 @@ Phaser.Tile = function (layer, index, x, y, width, height) { Phaser.Tile.prototype = { + intersects: function (x, y, right, bottom) { + + if (right <= this.worldX) + { + return false; + } + + if (bottom <= this.worldY) + { + return false; + } + + if (x >= this.worldX + this.width) + { + return false; + } + + if (y >= this.worldY + this.height) + { + return false; + } + + return true; + + }, + /** * Set a callback to be called when this tile is hit by an object. * The callback must true true for collision processing to take place. diff --git a/src/tilemap/TilemapLayer.js b/src/tilemap/TilemapLayer.js index f424e6b0..f37f5f72 100644 --- a/src/tilemap/TilemapLayer.js +++ b/src/tilemap/TilemapLayer.js @@ -454,6 +454,93 @@ Phaser.TilemapLayer.prototype.getTileXY = function (x, y, point) { } +Phaser.TilemapLayer.prototype.getIntersectingTiles = function (x, y, width, height) { + + // var tiles = this.getTiles(x, y, width,height, true); + var tiles = this.getTiles(x, y, width,height, false); + + var right = x + width; + var bottom = y + height; + + // We only want the ones that we actually intersect with + var i = tiles.length; + + while (i--) + { + if (tiles[i].intersects(x, y, right, bottom)) + { + // tiles[i].debug = true; + } + else + { + // console.log('sliced off tile', i); + // tiles.pop(); + } + } + + return tiles; + +} + +/* +Phaser.TilemapLayer.prototype.getTilesX = function (x, y, width, height, collides) { + + // Should we only get tiles that have at least one of their collision flags set? (true = yes, false = no just get them all) + if (typeof collides === 'undefined') { collides = false; } + + // adjust the x,y coordinates for scrollFactor + x = this._fixX(x); + y = this._fixY(y); + + if (width > this.layer.widthInPixels) + { + width = this.layer.widthInPixels; + } + + if (height > this.layer.heightInPixels) + { + height = this.layer.heightInPixels; + } + + // Convert the pixel values into tile coordinates + // this._tx = this.game.math.snapToFloor(x, this._cw) / this._cw; + // this._ty = this.game.math.snapToFloor(y, this._ch) / this._ch; + // this._tw = (this.game.math.snapToCeil(width, this._cw) + this._cw) / this._cw; + // this._th = (this.game.math.snapToCeil(height, this._ch) + this._ch) / this._ch; + + // var firstTileX = Math.max( Math.floor(res.pos.x / this.tilesize), 0 ); + // var lastTileX = Math.min( Math.ceil((res.pos.x + width) / this.tilesize), this.width ); + // var tileY = Math.floor( (res.pos.y + pxOffsetY) / this.tilesize ); + + this._tx = Math.max(Math.floor(x / this.tileWidth), 0); + this._tw = Math.min(Math.ceil((x + width) / this.tileWidth), this.width); + this._ty = Math.floor((y + px) / this.tileHeight); + + this._results.length = 0; + + for (var wy = this._ty; wy < this._ty + this._th; wy++) + { + for (var wx = this._tx; wx < this._tx + this._tw; wx++) + { + if (this.layer.data[wy] && this.layer.data[wy][wx]) + { + if (collides === false || (collides && this.layer.data[wy][wx].canCollide)) + { + this._results.push(this.layer.data[wy][wx]); + } + } + } + } + + // DEBUG ONLY - REMOVE + this.layer.dirty = true; + + return this._results; + +} +*/ + + /** * Get all tiles that exist within the given area, defined by the top-left corner, width and height. Values given are in pixels, not tiles. * @method Phaser.TilemapLayer#getTiles @@ -491,7 +578,8 @@ Phaser.TilemapLayer.prototype.getTiles = function (x, y, width, height, collides this._th = (this.game.math.snapToCeil(height, this._ch) + this._ch) / this._ch; // This should apply the layer x/y here - this._results.length = 0; + // this._results.length = 0; + this._results = []; for (var wy = this._ty; wy < this._ty + this._th; wy++) { @@ -501,6 +589,7 @@ Phaser.TilemapLayer.prototype.getTiles = function (x, y, width, height, collides { if (collides === false || (collides && this.layer.data[wy][wx].canCollide)) { + /* // Convert tile coordinates back to camera space for return var _wx = this._unfixX(wx * this._cw) / this._cw; var _wy = this._unfixY(wy * this._ch) / this._ch; @@ -513,11 +602,18 @@ Phaser.TilemapLayer.prototype.getTiles = function (x, y, width, height, collides tile: this.layer.data[wy][wx], layer: this.layer.data[wy][wx].layer }); + */ + + this._results.push(this.layer.data[wy][wx]); + } } } } + // DEBUG ONLY - REMOVE + this.layer.dirty = true; + return this._results; } diff --git a/src/time/Time.js b/src/time/Time.js index 79d57849..b489aa0a 100644 --- a/src/time/Time.js +++ b/src/time/Time.js @@ -227,6 +227,7 @@ Phaser.Time.prototype = { this.timeToCall = this.game.math.max(0, 16 - (time - this.lastTime)); this.elapsed = this.now - this.time; + this.physicsElapsed = this.elapsed / 1000; if (this.advancedTiming) { @@ -248,8 +249,6 @@ Phaser.Time.prototype = { this.time = this.now; this.lastTime = time + this.timeToCall; - this.physicsElapsed = 1.0 * (this.elapsed / 1000); - // Paused but still running? if (!this.game.paused) { diff --git a/src/utils/Debug.js b/src/utils/Debug.js index d1971365..7d078e6b 100644 --- a/src/utils/Debug.js +++ b/src/utils/Debug.js @@ -518,33 +518,35 @@ Phaser.Utils.Debug.prototype = { * @param {Phaser.Rectangle|Phaser.Circle|Phaser.Point|Phaser.Line} object - The geometry object to render. * @param {string} [color] - Color of the debug info to be rendered (format is css color string). * @param {boolean} [filled=true] - Render the objected as a filled (default, true) or a stroked (false) + * @param {number} [forceType=0] - Force rendering of a specific type. If 0 no type will be forced, otherwise 1 = Rectangle, 2 = Circle, 3 = Point and 4 = Line. */ - geom: function (object, color, filled) { + geom: function (object, color, filled, forceType) { if (typeof filled === 'undefined') { filled = true; } + if (typeof forceType === 'undefined') { forceType = 0; } - color = color || 'rgba(0,255,0,0.3)'; + color = color || 'rgba(0,255,0,0.4)'; this.start(); this.context.fillStyle = color; this.context.strokeStyle = color; - if (object instanceof Phaser.Rectangle) + if (object instanceof Phaser.Rectangle || forceType === 1) { if (filled) { - this.context.fillRect(object.x, object.y, object.width, object.height); + this.context.fillRect(object.x - this.game.camera.x, object.y - this.game.camera.y, object.width, object.height); } else { - this.context.strokeRect(object.x, object.y, object.width, object.height); + this.context.strokeRect(object.x - this.game.camera.x, object.y - this.game.camera.y, object.width, object.height); } } - else if (object instanceof Phaser.Circle) + else if (object instanceof Phaser.Circle || forceType === 2) { this.context.beginPath(); - this.context.arc(object.x, object.y, object.radius, 0, Math.PI * 2, false); + this.context.arc(object.x - this.game.camera.x, object.y - this.game.camera.y, object.radius, 0, Math.PI * 2, false); this.context.closePath(); if (filled) @@ -556,16 +558,16 @@ Phaser.Utils.Debug.prototype = { this.context.stroke(); } } - else if (object instanceof Phaser.Point) + else if (object instanceof Phaser.Point || forceType === 3) { - this.context.fillRect(object.x, object.y, 4, 4); + this.context.fillRect(object.x - this.game.camera.x, object.y - this.game.camera.y, 4, 4); } - else if (object instanceof Phaser.Line) + else if (object instanceof Phaser.Line || forceType === 4) { this.context.lineWidth = 1; this.context.beginPath(); - this.context.moveTo(object.start.x + 0.5, object.start.y + 0.5); - this.context.lineTo(object.end.x + 0.5, object.end.y + 0.5); + this.context.moveTo((object.start.x + 0.5) - this.game.camera.x, (object.start.y + 0.5) - this.game.camera.y); + this.context.lineTo((object.end.x + 0.5) - this.game.camera.x, (object.end.y + 0.5) - this.game.camera.y); this.context.closePath(); this.context.stroke(); }