Fixed Kenny tiles. Fixed Ninja Circle vs. World bounds. Added Ninja tilemap to Tiles conversion. Created test.

This commit is contained in:
photonstorm 2014-03-12 02:44:34 +00:00
parent 4910f27a88
commit a07cf894a0
9 changed files with 327 additions and 33 deletions

View file

@ -0,0 +1,66 @@
{ "height":11,
"layers":[
{
"data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 77, 95, 0, 0, 0, 0, 0, 0, 0, 0, 82, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 131, 36, 15, 20, 74, 95, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 77, 77, 15, 20, 20, 34, 74, 95, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 15, 20, 20, 20, 20, 20, 34, 34, 74, 95, 0, 89, 0, 137, 0, 0, 0, 0, 140, 0, 0, 0, 0, 36, 15, 20, 20, 20, 20, 20, 20, 34, 34, 34, 74, 32, 32, 32, 116, 135, 135, 135, 135, 119, 77, 77, 77, 77, 15, 20, 20, 20, 20, 20, 20, 20, 34, 34, 34, 34, 34, 34, 34, 34, 31, 31, 31, 31, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 34, 34, 34, 34, 34, 34, 34, 34, 31, 31, 31, 31, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20],
"height":11,
"name":"Tile Layer 1",
"opacity":1,
"type":"tilelayer",
"visible":true,
"width":25,
"x":0,
"y":0
}],
"orientation":"orthogonal",
"properties":
{
},
"tileheight":70,
"tilesets":[
{
"firstgid":1,
"image":"..\/tiles\/kenney.png",
"imageheight":490,
"imagewidth":1470,
"margin":0,
"name":"kenney",
"properties":
{
},
"spacing":0,
"tileheight":70,
"tileproperties":
{
"136":
{
"slope":"3"
},
"139":
{
"slope":"2"
},
"31":
{
"slope":"1"
},
"35":
{
"slope":"3"
},
"76":
{
"slope":"1"
},
"94":
{
"slope":"2"
}
},
"tilewidth":70
}],
"tilewidth":70,
"version":1,
"width":25
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

View file

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<map version="1.0" orientation="orthogonal" width="25" height="11" tilewidth="70" tileheight="70">
<tileset firstgid="1" name="kenney" tilewidth="70" tileheight="70">
<image source="../tiles/kenney.png" width="1470" height="490"/>
<tile id="31">
<properties>
<property name="slope" value="1"/>
</properties>
</tile>
<tile id="35">
<properties>
<property name="slope" value="3"/>
</properties>
</tile>
<tile id="76">
<properties>
<property name="slope" value="1"/>
</properties>
</tile>
<tile id="94">
<properties>
<property name="slope" value="2"/>
</properties>
</tile>
<tile id="136">
<properties>
<property name="slope" value="3"/>
</properties>
</tile>
<tile id="139">
<properties>
<property name="slope" value="2"/>
</properties>
</tile>
</tileset>
<layer name="Tile Layer 1" width="25" height="11">
<data encoding="base64">
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkAAAATQAAAF8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIMAAAAkAAAADwAAABQAAABKAAAAXwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJAAAAE0AAABNAAAADwAAABQAAAAUAAAAIgAAAEoAAABfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJAAAAA8AAAAUAAAAFAAAABQAAAAUAAAAFAAAACIAAAAiAAAASgAAAF8AAAAAAAAAWQAAAAAAAACJAAAAAAAAAAAAAAAAAAAAAAAAAIwAAAAAAAAAAAAAAAAAAAAAAAAAJAAAAA8AAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAiAAAAIgAAACIAAABKAAAAIAAAACAAAAAgAAAAdAAAAIcAAACHAAAAhwAAAIcAAAB3AAAATQAAAE0AAABNAAAATQAAAA8AAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAfAAAAHwAAAB8AAAAfAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAHwAAAB8AAAAfAAAAHwAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAA=
</data>
</layer>
</map>

View file

@ -0,0 +1,100 @@
// 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.tilemap('map', 'assets/tilemaps/maps/ninja-tilemap.json', null, Phaser.Tilemap.TILED_JSON);
game.load.image('ball', 'assets/sprites/shinyball.png');
game.load.image('sky', 'assets/skies/sky2.png');
game.load.image('kenney', 'assets/tilemaps/tiles/kenney.png');
}
var sprite1;
var cursors;
var map;
var layer;
var tiles;
function create() {
var sky = game.add.image(0, 0, 'sky');
sky.fixedToCamera = true;
// Activate the Ninja physics system
game.physics.startSystem(Phaser.Physics.NINJA);
map = game.add.tilemap('map');
map.addTilesetImage('kenney');
layer = map.createLayer('Tile Layer 1');
layer.resizeWorld();
var slopeMap = { '32': 1, '77': 1, '95': 2, '36': 3, '137': 3, '140': 2 };
tiles = game.physics.ninja.convertTilemap(map, layer, slopeMap);
sprite1 = game.add.sprite(50, 50, 'ball');
// game.physics.ninja.enableAABB(sprite1);
game.physics.ninja.enableCircle(sprite1, sprite1.width / 2);
// A little more bounce
sprite1.body.bounce = 0.5;
game.camera.follow(sprite1);
cursors = game.input.keyboard.createCursorKeys();
}
function update() {
for (var i = 0; i < tiles.length; i++)
{
sprite1.body.circle.collideCircleVsTile(tiles[i].tile);
// sprite1.body.aabb.collideAABBVsTile(tiles[i].tile);
}
// game.physics.ninja.collide(sprite1, tiles);
// 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);
}

View file

@ -16,8 +16,14 @@
* @param {number} [type=1] - The type of Ninja shape to create. 1 = AABB, 2 = Circle or 3 = Tile. * @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} [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=16] - If this body is using a Circle shape this controls the radius. * @param {number} [radius=16] - If this body is using a Circle shape this controls the radius.
* @param {number} [x=0] - The x coordinate of this Body. This is only used if a sprite is not provided.
* @param {number} [y=0] - The y coordinate of this Body. This is only used if a sprite is not provided.
* @param {number} [width=0] - The width of this Body. This is only used if a sprite is not provided.
* @param {number} [height=0] - The height of this Body. This is only used if a sprite is not provided.
*/ */
Phaser.Physics.Ninja.Body = function (system, sprite, type, id, radius) { Phaser.Physics.Ninja.Body = function (system, sprite, type, id, radius, x, y, width, height) {
sprite = sprite || null;
if (typeof type === 'undefined') { type = 1; } if (typeof type === 'undefined') { type = 1; }
if (typeof id === 'undefined') { id = 1; } if (typeof id === 'undefined') { id = 1; }
@ -31,7 +37,7 @@ Phaser.Physics.Ninja.Body = function (system, sprite, type, id, radius) {
/** /**
* @property {Phaser.Game} game - Local reference to game. * @property {Phaser.Game} game - Local reference to game.
*/ */
this.game = sprite.game; this.game = system.game;
/** /**
* @property {number} type - The type of physics system this body belongs to. * @property {number} type - The type of physics system this body belongs to.
@ -131,32 +137,37 @@ Phaser.Physics.Ninja.Body = function (system, sprite, type, id, radius) {
*/ */
this.maxSpeed = 8; this.maxSpeed = 8;
var sx = sprite.x; if (sprite)
var sy = sprite.y;
if (sprite.anchor.x === 0)
{ {
sx += (sprite.width * 0.5); x = sprite.x;
} y = sprite.y;
width = sprite.width;
height = sprite.height;
if (sprite.anchor.y === 0) if (sprite.anchor.x === 0)
{ {
sy += (sprite.height * 0.5); x += (sprite.width * 0.5);
}
if (sprite.anchor.y === 0)
{
y += (sprite.height * 0.5);
}
} }
if (type === 1) if (type === 1)
{ {
this.aabb = new Phaser.Physics.Ninja.AABB(this, sx, sy, sprite.width, sprite.height); this.aabb = new Phaser.Physics.Ninja.AABB(this, x, y, width, height);
this.shape = this.aabb; this.shape = this.aabb;
} }
else if (type === 2) else if (type === 2)
{ {
this.circle = new Phaser.Physics.Ninja.Circle(this, sx, sy, radius); this.circle = new Phaser.Physics.Ninja.Circle(this, x, y, radius);
this.shape = this.circle; this.shape = this.circle;
} }
else if (type === 3) else if (type === 3)
{ {
this.tile = new Phaser.Physics.Ninja.Tile(this, sx, sy, sprite.width, sprite.height, id); this.tile = new Phaser.Physics.Ninja.Tile(this, x, y, width, height, id);
this.shape = this.tile; this.shape = this.tile;
} }
@ -202,16 +213,19 @@ Phaser.Physics.Ninja.Body.prototype = {
*/ */
postUpdate: function () { postUpdate: function () {
if (this.sprite.type === Phaser.TILESPRITE) if (this.sprite)
{ {
// TileSprites don't use their anchor property, so we need to adjust the coordinates if (this.sprite.type === Phaser.TILESPRITE)
this.sprite.x = this.shape.pos.x - this.shape.xw; {
this.sprite.y = this.shape.pos.y - this.shape.yw; // TileSprites don't use their anchor property, so we need to adjust the coordinates
} this.sprite.x = this.shape.pos.x - this.shape.xw;
else this.sprite.y = this.shape.pos.y - this.shape.yw;
{ }
this.sprite.x = this.shape.pos.x; else
this.sprite.y = this.shape.pos.y; {
this.sprite.x = this.shape.pos.x;
this.sprite.y = this.shape.pos.y;
}
} }
if (this.velocity.x < 0) if (this.velocity.x < 0)

View file

@ -199,7 +199,7 @@ Phaser.Physics.Ninja.Circle.prototype = {
*/ */
collideWorldBounds: function () { collideWorldBounds: function () {
var dx = this.system.bounds.x - (this.pos.x - this.xw); var dx = this.system.bounds.x - (this.pos.x - this.radius);
if (0 < dx) if (0 < dx)
{ {
@ -207,7 +207,7 @@ Phaser.Physics.Ninja.Circle.prototype = {
} }
else else
{ {
dx = (this.pos.x + this.xw) - this.system.bounds.width; dx = (this.pos.x + this.radius) - this.system.bounds.width;
if (0 < dx) if (0 < dx)
{ {
@ -215,7 +215,7 @@ Phaser.Physics.Ninja.Circle.prototype = {
} }
} }
var dy = this.system.bounds.y - (this.pos.y - this.yw); var dy = this.system.bounds.y - (this.pos.y - this.radius);
if (0 < dy) if (0 < dy)
{ {
@ -223,7 +223,7 @@ Phaser.Physics.Ninja.Circle.prototype = {
} }
else else
{ {
dy = (this.pos.y + this.yw) - this.system.bounds.height; dy = (this.pos.y + this.radius) - this.system.bounds.height;
if (0 < dy) if (0 < dy)
{ {

View file

@ -300,6 +300,18 @@ Phaser.Physics.Ninja.Tile.prototype = {
}, },
/**
* Destroys this Tiles reference to Body and System.
*
* @method Phaser.Physics.Ninja.Tile#destroy
*/
destroy: function () {
this.body = null;
this.system = null;
},
/** /**
* This converts a tile from implicitly-defined (via id), to explicit (via properties). * This converts a tile from implicitly-defined (via id), to explicit (via properties).
* Don't call directly, instead of setType. * Don't call directly, instead of setType.

View file

@ -220,13 +220,74 @@ Phaser.Physics.Ninja.prototype = {
}, },
/** /**
* Called automatically by a Physics body, it updates all motion related values on the Body. * Clears all physics bodies from the given TilemapLayer that were created with `World.convertTilemap`.
* *
* @method Phaser.Physics.Ninja#updateMotion * @method Phaser.Physics.Ninja#clearTilemapLayerBodies
* @param {Phaser.Physics.Ninja.Body} The Body object to be updated. * @param {Phaser.Tilemap} map - The Tilemap to get the map data from.
update: function () { * @param {number|string|Phaser.TilemapLayer} [layer] - The layer to operate on. If not given will default to map.currentLayer.
},
*/ */
clearTilemapLayerBodies: function (map, layer) {
layer = map.getLayer(layer);
var i = map.layers[layer].bodies.length;
while (i--)
{
map.layers[layer].bodies[i].destroy();
}
map.layers[layer].bodies.length = [];
},
/**
* Goes through all tiles in the given Tilemap and TilemapLayer and converts those set to collide into physics tiles.
* Only call this *after* you have specified all of the tiles you wish to collide with calls like Tilemap.setCollisionBetween, etc.
* Every time you call this method it will destroy any previously created bodies and remove them from the world.
* Therefore understand it's a very expensive operation and not to be done in a core game update loop.
*
* In Ninja the Tiles have an ID from 0 to 33, where 0 is 'empty', 1 is a full tile, 2 is a 45-degree slope, etc. You can find the ID
* list either at the very bottom of `Tile.js`, or in a handy visual reference in the `resources/Ninja Physics Debug Tiles` folder in the repository.
* The slopeMap parameter is an array that controls how the indexes of the tiles in your tilemap data will map to the Ninja Tile IDs.
* For example if you had 6 tiles in your tileset: Imagine the first 4 should be converted into fully solid Tiles and the other 2 are 45-degree slopes.
* Your slopeMap array would look like this: `[ 1, 1, 1, 1, 2, 3 ]`.
* Where each element of the array is a tile in your tilemap and the resulting Ninja Tile it should create.
*
* @method Phaser.Physics.Ninja#convertTilemap
* @param {Phaser.Tilemap} map - The Tilemap to get the map data from.
* @param {number|string|Phaser.TilemapLayer} [layer] - The layer to operate on. If not given will default to map.currentLayer.
* @param {object} [slopeMap] - The tilemap index to Tile ID map.
* @return {array} An array of the Phaser.Physics.Ninja.Tile objects that were created.
*/
convertTilemap: function (map, layer, slopeMap) {
layer = map.getLayer(layer);
if (typeof addToWorld === 'undefined') { addToWorld = true; }
if (typeof optimize === 'undefined') { optimize = true; }
// If the bodies array is already populated we need to nuke it
this.clearTilemapLayerBodies(map, layer);
for (var y = 0, h = map.layers[layer].height; y < h; y++)
{
for (var x = 0, w = map.layers[layer].width; x < w; x++)
{
var tile = map.layers[layer].data[y][x];
if (tile && slopeMap.hasOwnProperty(tile.index))
{
var body = new Phaser.Physics.Ninja.Body(this, null, 3, slopeMap[tile.index], 0, tile.worldX + tile.centerX, tile.worldY + tile.centerY, tile.width, tile.height);
map.layers[layer].bodies.push(body);
}
}
}
return map.layers[layer].bodies;
},
/** /**
* Checks for overlaps between two game objects. The objects can be Sprites, Groups or Emitters. * Checks for overlaps between two game objects. The objects can be Sprites, Groups or Emitters.

View file

@ -1027,7 +1027,7 @@ Phaser.Physics.P2.prototype = {
/** /**
* Clears all physics bodies from the given TilemapLayer that were created with `World.convertTilemap`. * Clears all physics bodies from the given TilemapLayer that were created with `World.convertTilemap`.
* *
* @method Phaser.Tilemap#clearTilemapLayerBodies * @method Phaser.Physics.P2#clearTilemapLayerBodies
* @param {Phaser.Tilemap} map - The Tilemap to get the map data from. * @param {Phaser.Tilemap} map - The Tilemap to get the map data from.
* @param {number|string|Phaser.TilemapLayer} [layer] - The layer to operate on. If not given will default to map.currentLayer. * @param {number|string|Phaser.TilemapLayer} [layer] - The layer to operate on. If not given will default to map.currentLayer.
*/ */