Reputation: 633
Hello stackoverflow community. Recently, I've been working on getting a proper raycasting system working. Currently, I've been working entirely in 2D, with a 2D map and player representation. I'm having issues however properly creating a grid-based environment. I think the issue is the way I'm casting rays isn't grid-based at all. I have an example of my issue here. As you can see, the rays appear choppy and malformed. Can anyone give me some insight on how a grid-based system would work? Any help is appreciated, thanks.
Here's the full source code(I'm using PIXI.js for my rendering):
var world = [
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
[1,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,1],
[1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
[1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1],
[1,0,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
[1,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
[1,0,1,0,0,1,0,0,0,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,1,1,1],
[1,0,1,1,1,1,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1],
[1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1],
[1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,1,1,1,1,1],
[1,0,0,0,0,0,0,0,0,1,1,1,1,0,1,1,0,0,0,0,0,0,0,0,0,0,1],
[1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1],
[1,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,1],
[1,0,1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,1],
[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,1],
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
];
var width = world[0].length;
var height = world.length;
var scale = 8;
var posX = 1;
var posY = 1;
var yaw = 0;
var m = 0;
var renderer = new PIXI.WebGLRenderer(width * scale,height * scale);
//var renderer = new PIXI.WebGLRenderer(320,200);
document.body.appendChild(renderer.view);
var stage = new PIXI.Stage(0xFFFFFF);
var graphics = new PIXI.Graphics();
stage.addChild(graphics);
function drawMap()
{
for(var x = 0;x < width;x++)
{
for(var y = 0;y < height;y++)
{
if(world[y][x])
{
graphics.beginFill(0xCCCCCC);
graphics.drawRect(x * scale, y * scale, scale, scale);
graphics.endFill();
}
}
}
}
function drawPlayer()
{
graphics.beginFill(0x000000);
graphics.drawRect(posX * scale, posY * scale, 4, 4);
graphics.endFill();
graphics.lineStyle(1,0x000000);
graphics.moveTo(posX * scale + 2, posY * scale + 2);
graphics.lineTo(posX * scale + Math.cos(yaw) * 20 + 2, posY * scale + Math.sin(yaw) * 20 + 2);
}
function move()
{
var newX = posX + Math.cos(yaw) * m * 0.3;
var newY = posY + Math.sin(yaw) * m * 0.3;
m = 0;
if(isColliding(newX,newY))
{
return;
}
posX = newX;
posY = newY;
}
function isColliding(x,y)
{
if(world[Math.floor(y)][Math.floor(x)])
{
return true;
}
return false;
}
function castRays()
{
var rayYaw = 0;
var rayX = posX;
var rayY = posY;
var dist = 0;
for(var x = -160;x < 160;x++)
{
rayYaw = x * 0.1875;
while(!isColliding(rayX,rayY))
{
rayX += Math.cos((rayYaw) * (Math.PI / 180) + yaw);
rayY += Math.sin((rayYaw) * (Math.PI / 180) + yaw);
if(rayX < 0 || rayX >= width || rayY < 0 || rayY >= height)
{
break;
}
}
dist = Math.sqrt(Math.pow(posX - rayX,2) + Math.pow(posY - rayY,2));
graphics.lineStyle(1,0x00FFCC);
graphics.moveTo(posX * scale + 2, posY * scale + 2);
graphics.lineTo(rayX * scale + Math.cos((rayYaw) * (Math.PI / 180)) + 2, rayY * scale + Math.sin((rayYaw) * (Math.PI / 180)) + 2);
//drawLine(x + 160,dist);
rayX = posX;
rayY = posY;
}
}
function drawLine(x,d)
{
var slice = (32 * d / 160);
var start = (100 - (slice/2));
graphics.lineStyle(1,0xCCCCCC);
graphics.moveTo(x,start);
graphics.lineTo(x,slice);
}
function main()
{
drawMap();
move();
drawPlayer();
castRays();
renderer.render(stage);
graphics.clear();
}
document.onkeydown = checkKey;
function checkKey(e) {
e = e || window.event;
if (e.keyCode == '38')
{
// up arrow
m = 1;
}
else if (e.keyCode == '40')
{
// down arrow
m = -1;
}
else if (e.keyCode == '37')
{
// left arrow
yaw -= 0.1;
}
else if (e.keyCode == '39')
{
// right arrow
yaw += 0.1;
}
}
setInterval(main,1000/30);
Upvotes: 2
Views: 4846
Reputation: 9706
Your problem is that you treat your map as a matrix and isColliding() is oversimplified. You really should treat each 1 in the matrix as a square to determine if and where exactly your ray is hitting it.
I would completely re-write the castRays to more traditional raytracing approach:
for each point on your "screen":
define vector V from point of view P through this point on screen
for each line S + L*y forming sides of each square, find if your vector intersects it:
P + V*k = S + L*y when k>0 and y is in 0..1
shortest k, if exists, forms your line.
There is plenty of room for optimizations as you go along this path.
Update
Did not want to write it all up, but here is a good article on ray-box intersection optimization, especially if the box or rectangle is aligned to the axis: http://www.scratchapixel.com/lessons/3d-basic-lessons/lesson-7-intersecting-simple-shapes/ray-box-intersection/
Upvotes: 3