TilemapLayer.getRayCastTiles will let you get all tiles that hit the given line for further processing.

Fixed Tilemap collision. Added new TILE_BIAS const to aid with fast/small sprites.
This commit is contained in:
photonstorm 2014-03-14 02:33:58 +00:00
parent f678d1fd31
commit 017a017b96
7 changed files with 208 additions and 18 deletions

View file

@ -153,6 +153,7 @@ New features:
* ArcadePhysics.Body has a new gravityScale property, which is a modifier multiplied against the world gravity value on a Body. * ArcadePhysics.Body has a new gravityScale property, which is a modifier multiplied against the world gravity value on a Body.
* Line.coordinatesOnLine will return all coordinates on the line using Bresenhams line algorithm. * Line.coordinatesOnLine will return all coordinates on the line using Bresenhams line algorithm.
* Line now has x, y, width, height, top, bottom, left and right properties, so you can effectively get its bounds. * Line now has x, y, width, height, top, bottom, left and right properties, so you can effectively get its bounds.
* TilemapLayer.getRayCastTiles will let you get all tiles that hit the given line for further processing.
Updates: Updates:

View file

@ -6,7 +6,7 @@ function create() {
var line = new Phaser.Line(100, 50, 10, 300); var line = new Phaser.Line(100, 50, 10, 300);
var coords = line.coordinatesOnLine(); var coords = line.coordinatesOnLine(8);
var bmd = game.add.bitmapData(800, 600); var bmd = game.add.bitmapData(800, 600);
bmd.context.fillStyle = '#ffffff'; bmd.context.fillStyle = '#ffffff';

View file

@ -0,0 +1,127 @@
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');
}
var map;
var layer;
var cursors;
var sprite;
var line;
var tileHits = [];
var plotting = false;
function create() {
line = new Phaser.Line();
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);
cursors = game.input.keyboard.createCursorKeys();
var help = game.add.text(10, 10, 'Arrows to move, click and drag to cast a ray', { font: '16px Arial', fill: '#ffffff' });
help.fixedToCamera = true;
game.input.onDown.add(startLine, this);
game.input.onUp.add(raycast, this);
}
function startLine(pointer) {
if (tileHits.length > 0)
{
for (var i = 0; i < tileHits.length; i++)
{
tileHits[i].debug = false;
}
layer.dirty = true;
}
line.start.set(pointer.worldX, pointer.worldY);
plotting = true;
}
function raycast(pointer) {
line.end.set(pointer.worldX, pointer.worldY);
tileHits = layer.getRayCastTiles(line, 4, false, false);
if (tileHits.length > 0)
{
// Just so we can visually see the tiles
for (var i = 0; i < tileHits.length; i++)
{
tileHits[i].debug = true;
}
layer.dirty = true;
}
plotting = false;
}
function update() {
if (plotting)
{
line.end.set(game.input.activePointer.worldX, game.input.activePointer.worldY);
}
game.physics.arcade.collide(sprite, layer);
sprite.body.velocity.x = 0;
sprite.body.velocity.y = 0;
if (cursors.up.isDown)
{
sprite.body.velocity.y = -200;
}
else if (cursors.down.isDown)
{
sprite.body.velocity.y = 200;
}
if (cursors.left.isDown)
{
sprite.body.velocity.x = -200;
}
else if (cursors.right.isDown)
{
sprite.body.velocity.x = 200;
}
}
function render() {
game.debug.geom(line);
}

View file

@ -131,11 +131,13 @@ Phaser.Line.prototype = {
* The start and end points are rounded before this runs as the algorithm works on integers. * The start and end points are rounded before this runs as the algorithm works on integers.
* *
* @method Phaser.Line#coordinatesOnLine * @method Phaser.Line#coordinatesOnLine
* @param {number} [stepRate=1] - How many steps will we return? 1 = every coordinate on the line, 2 = every other coordinate, etc.
* @param {array} [results] - The array to store the results in. If not provided a new one will be generated. * @param {array} [results] - The array to store the results in. If not provided a new one will be generated.
* @return {array} An array of coordinates. * @return {array} An array of coordinates.
*/ */
coordinatesOnLine: function (results) { coordinatesOnLine: function (stepRate, results) {
if (typeof stepRate === 'undefined') { stepRate = 1; }
if (typeof results === 'undefined') { results = []; } if (typeof results === 'undefined') { results = []; }
var x1 = Math.round(this.start.x); var x1 = Math.round(this.start.x);
@ -151,6 +153,8 @@ Phaser.Line.prototype = {
results.push([x1, y1]); results.push([x1, y1]);
var i = 1;
while (!((x1 == x2) && (y1 == y2))) while (!((x1 == x2) && (y1 == y2)))
{ {
var e2 = err << 1; var e2 = err << 1;
@ -167,7 +171,13 @@ Phaser.Line.prototype = {
y1 += sy; y1 += sy;
} }
results.push([x1, y1]); if (i % stepRate === 0)
{
results.push([x1, y1]);
}
i++;
} }
return results; return results;

View file

@ -44,6 +44,11 @@ Phaser.Physics.Arcade = function (game) {
*/ */
this.OVERLAP_BIAS = 4; this.OVERLAP_BIAS = 4;
/**
* @property {number} TILE_BIAS - A value added to the delta values during collision with tiles. Adjust this if you get tunnelling.
*/
this.TILE_BIAS = 16;
/** /**
* @property {Phaser.QuadTree} quadTree - The world QuadTree. * @property {Phaser.QuadTree} quadTree - The world QuadTree.
*/ */
@ -1052,7 +1057,7 @@ Phaser.Physics.Arcade.prototype = {
{ {
ox = body.x - tile.right; ox = body.x - tile.right;
if (ox < -this.OVERLAP_BIAS) if (ox < -this.TILE_BIAS)
{ {
ox = 0; ox = 0;
} }
@ -1065,7 +1070,7 @@ Phaser.Physics.Arcade.prototype = {
{ {
ox = body.right - tile.left; ox = body.right - tile.left;
if (ox > this.OVERLAP_BIAS) if (ox > this.TILE_BIAS)
{ {
ox = 0; ox = 0;
} }
@ -1101,7 +1106,7 @@ Phaser.Physics.Arcade.prototype = {
{ {
oy = body.y - tile.bottom; oy = body.y - tile.bottom;
if (oy < -this.OVERLAP_BIAS) if (oy < -this.TILE_BIAS)
{ {
oy = 0; oy = 0;
} }
@ -1114,7 +1119,7 @@ Phaser.Physics.Arcade.prototype = {
{ {
oy = body.bottom - tile.top; oy = body.bottom - tile.top;
if (oy > this.OVERLAP_BIAS) if (oy > this.TILE_BIAS)
{ {
oy = 0; oy = 0;
} }

View file

@ -144,6 +144,20 @@ Phaser.Tile = function (layer, index, x, y, width, height) {
Phaser.Tile.prototype = { Phaser.Tile.prototype = {
/**
* Check if the given x and y world coordinates are within this Tile.
*
* @method Phaser.Tile#containsPoint
* @param {number} x - The x coordinate to test.
* @param {number} y - The y coordinate to test.
* @return {boolean} True if the coordinates are within this Tile, otherwise false.
*/
containsPoint: function (x, y) {
return !(x < this.worldX || y < this.worldY || x > this.right || y > this.bottom);
},
/** /**
* Check for intersection with this tile. * Check for intersection with this tile.
* *

View file

@ -151,6 +151,12 @@ Phaser.TilemapLayer = function (game, tilemap, index, width, height) {
*/ */
this.dirty = true; this.dirty = true;
/**
* @property {number} rayStepRate - When ray-casting against tiles this is the number of steps it will jump. For larger tile sizes you can increase this to improve performance.
* @default
*/
this.rayStepRate = 4;
/** /**
* @property {number} _cw - Local collision var. * @property {number} _cw - Local collision var.
* @private * @private
@ -457,17 +463,46 @@ Phaser.TilemapLayer.prototype.getTileXY = function (x, y, point) {
/** /**
* Gets all tiles that intersect with the given line. * Gets all tiles that intersect with the given line.
* *
* @method Phaser.TilemapLayer#getIntersectingTiles * @method Phaser.TilemapLayer#getRayCastTiles
* @memberof Phaser.TilemapLayer * @memberof Phaser.TilemapLayer
* @param {Phaser.Line} line - The line used to determine which tiles to return. * @param {Phaser.Line} line - The line used to determine which tiles to return.
* @param {number} [stepRate] - How many steps through the ray will we check? If undefined or null it uses TilemapLayer.rayStepRate.
* @param {boolean} [collides=false] - If true only return tiles that collide on one or more faces.
* @param {boolean} [interestingFace=false] - If true only return tiles that have interesting faces.
* @return {array<Phaser.Tile>} An array of Phaser.Tiles. * @return {array<Phaser.Tile>} An array of Phaser.Tiles.
*/ */
Phaser.TilemapLayer.prototype.getIntersectingTiles = function (line) { Phaser.TilemapLayer.prototype.getRayCastTiles = function (line, stepRate, collides, interestingFace) {
var tiles = this.getTiles(x, y, width, height, false); if (typeof stepRate === 'undefined' || stepRate === null) { stepRate = this.rayStepRate; }
if (typeof collides === 'undefined') { collides = false; }
if (typeof interestingFace === 'undefined') { interestingFace = false; }
// First get all tiles that touch the bounds of the line
var tiles = this.getTiles(line.x, line.y, line.width, line.height, collides, interestingFace);
return tiles; if (tiles.length === 0)
{
return [];
}
// Now we only want the tiles that intersect with the points on this line
var coords = line.coordinatesOnLine(stepRate);
var total = coords.length;
var results = [];
for (var i = 0; i < tiles.length; i++)
{
for (var t = 0; t < total; t++)
{
if (tiles[i].containsPoint(coords[t][0], coords[t][1]))
{
results.push(tiles[i]);
break;
}
}
}
return results;
} }
@ -589,8 +624,6 @@ Phaser.TilemapLayer.prototype.render = function () {
var tile; var tile;
var set; var set;
// var ox = 0;
// var oy = 0;
if (this.debug) if (this.debug)
{ {
@ -618,11 +651,11 @@ Phaser.TilemapLayer.prototype.render = function () {
set.draw(this.context, Math.floor(this._tx), Math.floor(this._ty), tile.index); set.draw(this.context, Math.floor(this._tx), Math.floor(this._ty), tile.index);
// if (tile.debug) if (tile.debug)
// { {
// this.context.fillStyle = 'rgba(0, 255, 0, 0.4)'; this.context.fillStyle = 'rgba(0, 255, 0, 0.4)';
// this.context.fillRect(Math.floor(this._tx), Math.floor(this._ty), this.map.tileWidth, this.map.tileHeight); this.context.fillRect(Math.floor(this._tx), Math.floor(this._ty), this.map.tileWidth, this.map.tileHeight);
// } }
} }
this._tx += this.map.tileWidth; this._tx += this.map.tileWidth;