Skip to content

Commit 017a017

Browse files
committed
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.
1 parent f678d1f commit 017a017

7 files changed

Lines changed: 208 additions & 18 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ New features:
153153
* ArcadePhysics.Body has a new gravityScale property, which is a modifier multiplied against the world gravity value on a Body.
154154
* Line.coordinatesOnLine will return all coordinates on the line using Bresenhams line algorithm.
155155
* Line now has x, y, width, height, top, bottom, left and right properties, so you can effectively get its bounds.
156+
* TilemapLayer.getRayCastTiles will let you get all tiles that hit the given line for further processing.
156157

157158

158159
Updates:

examples/wip/line points.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ function create() {
66

77
var line = new Phaser.Line(100, 50, 10, 300);
88

9-
var coords = line.coordinatesOnLine();
9+
var coords = line.coordinatesOnLine(8);
1010

1111
var bmd = game.add.bitmapData(800, 600);
1212
bmd.context.fillStyle = '#ffffff';

examples/wip/tilemap raycast.js

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
2+
var game = new Phaser.Game(800, 600, Phaser.CANVAS, 'phaser-example', { preload: preload, create: create, update: update, render: render });
3+
4+
function preload() {
5+
6+
game.load.tilemap('map', 'assets/tilemaps/maps/collision_test.json', null, Phaser.Tilemap.TILED_JSON);
7+
game.load.image('ground_1x1', 'assets/tilemaps/tiles/ground_1x1.png');
8+
game.load.image('phaser', 'assets/sprites/phaser-dude.png');
9+
10+
}
11+
12+
var map;
13+
var layer;
14+
var cursors;
15+
var sprite;
16+
var line;
17+
var tileHits = [];
18+
var plotting = false;
19+
20+
function create() {
21+
22+
line = new Phaser.Line();
23+
24+
map = game.add.tilemap('map');
25+
26+
map.addTilesetImage('ground_1x1');
27+
28+
layer = map.createLayer('Tile Layer 1');
29+
30+
layer.resizeWorld();
31+
32+
map.setCollisionBetween(1, 12);
33+
34+
layer.debug = true;
35+
36+
sprite = game.add.sprite(260, 70, 'phaser');
37+
38+
game.physics.enable(sprite);
39+
40+
game.camera.follow(sprite);
41+
42+
cursors = game.input.keyboard.createCursorKeys();
43+
44+
var help = game.add.text(10, 10, 'Arrows to move, click and drag to cast a ray', { font: '16px Arial', fill: '#ffffff' });
45+
help.fixedToCamera = true;
46+
47+
game.input.onDown.add(startLine, this);
48+
game.input.onUp.add(raycast, this);
49+
50+
}
51+
52+
function startLine(pointer) {
53+
54+
if (tileHits.length > 0)
55+
{
56+
for (var i = 0; i < tileHits.length; i++)
57+
{
58+
tileHits[i].debug = false;
59+
}
60+
61+
layer.dirty = true;
62+
}
63+
64+
line.start.set(pointer.worldX, pointer.worldY);
65+
66+
plotting = true;
67+
68+
}
69+
70+
function raycast(pointer) {
71+
72+
line.end.set(pointer.worldX, pointer.worldY);
73+
74+
tileHits = layer.getRayCastTiles(line, 4, false, false);
75+
76+
if (tileHits.length > 0)
77+
{
78+
// Just so we can visually see the tiles
79+
for (var i = 0; i < tileHits.length; i++)
80+
{
81+
tileHits[i].debug = true;
82+
}
83+
84+
layer.dirty = true;
85+
}
86+
87+
plotting = false;
88+
89+
}
90+
91+
function update() {
92+
93+
if (plotting)
94+
{
95+
line.end.set(game.input.activePointer.worldX, game.input.activePointer.worldY);
96+
}
97+
98+
game.physics.arcade.collide(sprite, layer);
99+
100+
sprite.body.velocity.x = 0;
101+
sprite.body.velocity.y = 0;
102+
103+
if (cursors.up.isDown)
104+
{
105+
sprite.body.velocity.y = -200;
106+
}
107+
else if (cursors.down.isDown)
108+
{
109+
sprite.body.velocity.y = 200;
110+
}
111+
112+
if (cursors.left.isDown)
113+
{
114+
sprite.body.velocity.x = -200;
115+
}
116+
else if (cursors.right.isDown)
117+
{
118+
sprite.body.velocity.x = 200;
119+
}
120+
121+
}
122+
123+
function render() {
124+
125+
game.debug.geom(line);
126+
127+
}

src/geom/Line.js

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,11 +131,13 @@ Phaser.Line.prototype = {
131131
* The start and end points are rounded before this runs as the algorithm works on integers.
132132
*
133133
* @method Phaser.Line#coordinatesOnLine
134+
* @param {number} [stepRate=1] - How many steps will we return? 1 = every coordinate on the line, 2 = every other coordinate, etc.
134135
* @param {array} [results] - The array to store the results in. If not provided a new one will be generated.
135136
* @return {array} An array of coordinates.
136137
*/
137-
coordinatesOnLine: function (results) {
138+
coordinatesOnLine: function (stepRate, results) {
138139

140+
if (typeof stepRate === 'undefined') { stepRate = 1; }
139141
if (typeof results === 'undefined') { results = []; }
140142

141143
var x1 = Math.round(this.start.x);
@@ -151,6 +153,8 @@ Phaser.Line.prototype = {
151153

152154
results.push([x1, y1]);
153155

156+
var i = 1;
157+
154158
while (!((x1 == x2) && (y1 == y2)))
155159
{
156160
var e2 = err << 1;
@@ -167,7 +171,13 @@ Phaser.Line.prototype = {
167171
y1 += sy;
168172
}
169173

170-
results.push([x1, y1]);
174+
if (i % stepRate === 0)
175+
{
176+
results.push([x1, y1]);
177+
}
178+
179+
i++;
180+
171181
}
172182

173183
return results;

src/physics/arcade/World.js

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,11 @@ Phaser.Physics.Arcade = function (game) {
4444
*/
4545
this.OVERLAP_BIAS = 4;
4646

47+
/**
48+
* @property {number} TILE_BIAS - A value added to the delta values during collision with tiles. Adjust this if you get tunnelling.
49+
*/
50+
this.TILE_BIAS = 16;
51+
4752
/**
4853
* @property {Phaser.QuadTree} quadTree - The world QuadTree.
4954
*/
@@ -1052,7 +1057,7 @@ Phaser.Physics.Arcade.prototype = {
10521057
{
10531058
ox = body.x - tile.right;
10541059

1055-
if (ox < -this.OVERLAP_BIAS)
1060+
if (ox < -this.TILE_BIAS)
10561061
{
10571062
ox = 0;
10581063
}
@@ -1065,7 +1070,7 @@ Phaser.Physics.Arcade.prototype = {
10651070
{
10661071
ox = body.right - tile.left;
10671072

1068-
if (ox > this.OVERLAP_BIAS)
1073+
if (ox > this.TILE_BIAS)
10691074
{
10701075
ox = 0;
10711076
}
@@ -1101,7 +1106,7 @@ Phaser.Physics.Arcade.prototype = {
11011106
{
11021107
oy = body.y - tile.bottom;
11031108

1104-
if (oy < -this.OVERLAP_BIAS)
1109+
if (oy < -this.TILE_BIAS)
11051110
{
11061111
oy = 0;
11071112
}
@@ -1114,7 +1119,7 @@ Phaser.Physics.Arcade.prototype = {
11141119
{
11151120
oy = body.bottom - tile.top;
11161121

1117-
if (oy > this.OVERLAP_BIAS)
1122+
if (oy > this.TILE_BIAS)
11181123
{
11191124
oy = 0;
11201125
}

src/tilemap/Tile.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,20 @@ Phaser.Tile = function (layer, index, x, y, width, height) {
144144

145145
Phaser.Tile.prototype = {
146146

147+
/**
148+
* Check if the given x and y world coordinates are within this Tile.
149+
*
150+
* @method Phaser.Tile#containsPoint
151+
* @param {number} x - The x coordinate to test.
152+
* @param {number} y - The y coordinate to test.
153+
* @return {boolean} True if the coordinates are within this Tile, otherwise false.
154+
*/
155+
containsPoint: function (x, y) {
156+
157+
return !(x < this.worldX || y < this.worldY || x > this.right || y > this.bottom);
158+
159+
},
160+
147161
/**
148162
* Check for intersection with this tile.
149163
*

src/tilemap/TilemapLayer.js

Lines changed: 44 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,12 @@ Phaser.TilemapLayer = function (game, tilemap, index, width, height) {
151151
*/
152152
this.dirty = true;
153153

154+
/**
155+
* @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.
156+
* @default
157+
*/
158+
this.rayStepRate = 4;
159+
154160
/**
155161
* @property {number} _cw - Local collision var.
156162
* @private
@@ -457,17 +463,46 @@ Phaser.TilemapLayer.prototype.getTileXY = function (x, y, point) {
457463
/**
458464
* Gets all tiles that intersect with the given line.
459465
*
460-
* @method Phaser.TilemapLayer#getIntersectingTiles
466+
* @method Phaser.TilemapLayer#getRayCastTiles
461467
* @memberof Phaser.TilemapLayer
462468
* @param {Phaser.Line} line - The line used to determine which tiles to return.
469+
* @param {number} [stepRate] - How many steps through the ray will we check? If undefined or null it uses TilemapLayer.rayStepRate.
470+
* @param {boolean} [collides=false] - If true only return tiles that collide on one or more faces.
471+
* @param {boolean} [interestingFace=false] - If true only return tiles that have interesting faces.
463472
* @return {array<Phaser.Tile>} An array of Phaser.Tiles.
464473
*/
465-
Phaser.TilemapLayer.prototype.getIntersectingTiles = function (line) {
474+
Phaser.TilemapLayer.prototype.getRayCastTiles = function (line, stepRate, collides, interestingFace) {
466475

467-
var tiles = this.getTiles(x, y, width, height, false);
476+
if (typeof stepRate === 'undefined' || stepRate === null) { stepRate = this.rayStepRate; }
477+
if (typeof collides === 'undefined') { collides = false; }
478+
if (typeof interestingFace === 'undefined') { interestingFace = false; }
479+
480+
// First get all tiles that touch the bounds of the line
481+
var tiles = this.getTiles(line.x, line.y, line.width, line.height, collides, interestingFace);
468482

483+
if (tiles.length === 0)
484+
{
485+
return [];
486+
}
469487

470-
return tiles;
488+
// Now we only want the tiles that intersect with the points on this line
489+
var coords = line.coordinatesOnLine(stepRate);
490+
var total = coords.length;
491+
var results = [];
492+
493+
for (var i = 0; i < tiles.length; i++)
494+
{
495+
for (var t = 0; t < total; t++)
496+
{
497+
if (tiles[i].containsPoint(coords[t][0], coords[t][1]))
498+
{
499+
results.push(tiles[i]);
500+
break;
501+
}
502+
}
503+
}
504+
505+
return results;
471506

472507
}
473508

@@ -589,8 +624,6 @@ Phaser.TilemapLayer.prototype.render = function () {
589624

590625
var tile;
591626
var set;
592-
// var ox = 0;
593-
// var oy = 0;
594627

595628
if (this.debug)
596629
{
@@ -618,11 +651,11 @@ Phaser.TilemapLayer.prototype.render = function () {
618651

619652
set.draw(this.context, Math.floor(this._tx), Math.floor(this._ty), tile.index);
620653

621-
// if (tile.debug)
622-
// {
623-
// this.context.fillStyle = 'rgba(0, 255, 0, 0.4)';
624-
// this.context.fillRect(Math.floor(this._tx), Math.floor(this._ty), this.map.tileWidth, this.map.tileHeight);
625-
// }
654+
if (tile.debug)
655+
{
656+
this.context.fillStyle = 'rgba(0, 255, 0, 0.4)';
657+
this.context.fillRect(Math.floor(this._tx), Math.floor(this._ty), this.map.tileWidth, this.map.tileHeight);
658+
}
626659
}
627660

628661
this._tx += this.map.tileWidth;

0 commit comments

Comments
 (0)