Raytracing on Tile maps, basic concepts

Example source code


<!DOCTYPE html>
<html>
<head>

<script type="text/javascript">
var ctx = null;
var tileW = 10, tileH = 10;
var gridW = 60, gridH = 40;
var circleX = 30, circleY = 20, radius = 15;
var mapData = new Array();
var rayData = {};

window.onload = function() {
	ctx = document.getElementById('game').getContext('2d');
	ctx.font = "bold 10pt sans-serif";
	
	// Create our "map" and set random 10 tiles as impassable (0)
	for(var i = 0; i < (gridW*gridH); i++) { mapData.push(1); }
	for(var i = 0; i < 10; i++)
	{
		// Choose a random point on the map
		var randPos = (Math.floor(Math.random()*20000)%(gridW*gridH));
		
		// Check it's not the centre of the circle
		if(randPos==((circleY*gridW)+circleX)) { continue; }
		
		// Set the value of this position to 0
		mapData[randPos] = 0;
	}
	
	rayData = updateRayData(circleX, circleY, radius);
	
	game.addEventListener('mouseup', function(e) {
		// Get the position of the mouse click on the page
		var mouseX = e.pageX;
		var mouseY = e.pageY;

		// Find the offset of the Canvas relative to the document top, left,
		// and modify the mouse position to account for this
		var p = game;
		do
		{
			mouseX-= p.offsetLeft;
			mouseY-= p.offsetTop;

			p = p.offsetParent;
		} while(p!=null);

		// fit the real mouse position to our grid
		mouseX = Math.floor(mouseX / tileW);
		mouseY = Math.floor(mouseY / tileH);
		
		// Set this blocking point to on or off, UNLESS this is the central point...
		if(mouseX!=circleX || mouseY!=circleY)
		{
			mapData[((mouseY*gridW)+mouseX)] = (mapData[((mouseY*gridW)+mouseX)]==0 ? 1 : 0);
		}
		
		rayData = updateRayData(circleX, circleY, radius);
	});
	
	requestAnimationFrame(drawGame);
};

function updateRayData(cx, cy, cr)
{
	var edges = pointsOnCircumference(cx, cy, cr);
	var points = {};
	
	for(x in edges)
	{
		var line = pointsOnLine(cx, cy, edges[x][0], edges[x][1]);
		
		for(l in line)
		{
			points[((line[l][1]*gridW)+line[l][0])] = [line[l][0], line[l][1]];
			if(mapData[((line[l][1]*gridW)+line[l][0])]==0) { break; }
		}
	}
	
	return points;
}

function pointsOnLine(x1, y1, x2, y2)
{
	line = new Array();

	var dx = Math.abs(x2 - x1);
	var dy = Math.abs(y2 - y1);
	var x = x1;
	var y = y1;
	var n = 1 + dx + dy;
	var xInc = (x1 < x2 ? 1 : -1);
	var yInc = (y1 < y2 ? 1 : -1);
	var error = dx - dy;

	dx *= 2;
	dy *= 2;

	while(n>0)
	{
		line.push([x, y]);

		if(error>0)
		{
			x+= xInc;
			error-= dy;
		}
		else
		{
			y+= yInc;
			error+= dx;
		}

		n-= 1;
	}

	return line;
}

function pointsOnCircumference(cx, cy, cr)
{
	var list = new Array();
	
	var x = cr;
	var y = 0;
	var o2 = Math.floor(1 - x);

	while(y <= x)
	{
		list.push([ x + cx,  y + cy]);
		list.push([ y + cx,  x + cy]);
		list.push([-x + cx,  y + cy]);
		list.push([-y + cx,  x + cy]);
		list.push([-x + cx, -y + cy]);
		list.push([-y + cx, -x + cy]);
		list.push([ x + cx, -y + cy]);
		list.push([ y + cx, -x + cy]);

		y+= 1;

		if(o2 <= 0) { o2+= (2 * y) + 1; }
		else
		{
			x-= 1;
			o2+= (2 * (y - x)) + 1;
		}
	}

	return list;
}

function drawGame()
{
	if(ctx==null) { return; }

	// Clear the Canvas
	ctx.fillStyle = "#ffffff";
	ctx.fillRect(0, 0, 600, 400);
	
	// Draw rays
	ctx.fillStyle = "#dddd00";
	for(r in rayData)
	{
		ctx.fillRect(rayData[r][0]*tileW, rayData[r][1]*tileH, tileW, tileH);
	}

	// Draw the grid
	ctx.fillStyle = "#000000";
	ctx.strokeStyle = "#999999";
	ctx.beginPath();
	for(y = 0; y < gridH; ++y)
	{
		for(x = 0; x < gridW; ++x)
		{
			// Draw a blocking point here?
			if(mapData[((y*gridW)+x)]==0) { ctx.fillRect((x*tileW), (y*tileH), tileW, tileH); }
			
			// Draw the grid lines
			ctx.rect((x*tileW), (y*tileH), tileW, tileH);
		}
	}
	ctx.closePath();
	ctx.stroke();
	
	// Draw circle centre
	ctx.fillStyle = "#ff0000";
	ctx.fillRect(circleX*tileW, circleY*tileH, tileW, tileH);
	
	// Ask for the next animation frame
	requestAnimationFrame(drawGame);
}
</script>

</head>
<body>

<p>Click on a grid square to add or remove an obstruction from the map.  Rays will automatically be recast and redrawn.  If you do not see a grid and the raycasting example below, please ensure you have Javascript enabled and that your browser supports the Canvas element.</p>
<canvas id="game" width="600" height="400"></canvas>

</body>
</html>

Page loaded in 0.023 second(s).