Skip to content

Commit

Permalink
Handle perfect tile raycast (#60)
Browse files Browse the repository at this point in the history
Use DDA instead of the Bresenham to get more accurate raycast.
  • Loading branch information
ClementCariou authored Jan 4, 2024
1 parent a2f02a1 commit 0916323
Showing 1 changed file with 41 additions and 21 deletions.
62 changes: 41 additions & 21 deletions src/engineTileLayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,37 +72,57 @@ function tileCollisionTest(pos, size=vec2(), object)
}
}

/** Return the center of tile if any that is hit (this does not return the exact hit point)
/** Return the center of tile if any that is hit
* @param {Vector2} posStart
* @param {Vector2} posEnd
* @param {EngineObject} [object]
* @return {Vector2}
* @memberof TileCollision */
function tileCollisionRaycast(posStart, posEnd, object)
{
// test if a ray collides with tiles from start to end
// todo: a way to get the exact hit point, it must still register as inside the hit tile
const posDelta = (posEnd = posEnd.floor()).subtract(posStart = posStart.floor());
const dx = abs(posDelta.x), dy = -abs(posDelta.y);
const sx = sign(posDelta.x), sy = sign(posDelta.y);
// Test if a ray collides with tiles from start to end using DDA
const flooredPosStart = posStart.floor();
const delta = posEnd.subtract(posStart);
const totalLength = delta.length();
const normalizedDelta = delta.normalize();
// Length between 2 intersections for each dimension
const unit = vec2(abs(1 / normalizedDelta.x), abs(1 / normalizedDelta.y));
// Initiate the iteration variables
let xIntersection =
delta.x < 0
? (posStart.x - flooredPosStart.x) * unit.x
: (flooredPosStart.x + 1 - posStart.x) * unit.x;
let yIntersection =
delta.y < 0
? (posStart.y - flooredPosStart.y) * unit.y
: (flooredPosStart.y + 1 - posStart.y) * unit.y;
let pos = flooredPosStart;

for (let x = posStart.x, y = posStart.y, e = dx + dy;;)
{
const tileData = getTileCollisionData(vec2(x,y));
if (tileData && (object ? object.collideWithTileRaycast(tileData, new Vector2(x, y)) : tileData > 0))
{
debugRaycast && debugLine(posStart, posEnd, '#f00',.02, 1);
debugRaycast && debugPoint(new Vector2(x+.5, y+.5), '#ff0', 1);
return new Vector2(x+.5, y+.5);
while (xIntersection < totalLength || yIntersection < totalLength) {
// Calculate the coordinates of the next tile to check
if (xIntersection > yIntersection) {
pos.y += sign(delta.y);
yIntersection += unit.y;
} else {
pos.x += sign(delta.x);
xIntersection += unit.x;
}
// Check for tile collision
const tileData = getTileCollisionData(pos);
if (
tileData &&
(object
? object.collideWithTileRaycast(tileData, pos)
: tileData > 0)
) {
const centeredPos = pos.add(vec2(0.5));
debugRaycast && debugLine(posStart, posEnd, "#f00", 0.02, 1);
debugRaycast && debugPoint(centeredPos, "#ff0", 1);
return centeredPos;
}

// update Bresenham line drawing algorithm
if (x == posEnd.x & y == posEnd.y) break;
const e2 = 2*e;
if (e2 >= dy) e += dy, x += sx;
if (e2 <= dx) e += dx, y += sy;
}
debugRaycast && debugLine(posStart, posEnd, '#00f',.02, 1);

debugRaycast && debugLine(posStart, posEnd, "#00f", 0.02, 1);
}

///////////////////////////////////////////////////////////////////////////////
Expand Down

0 comments on commit 0916323

Please sign in to comment.