So I've been writing a delphi conversion of this untextured raycaster. However, when checking for errors, I noticed that if you walk directly forwards a zero division error occurs. This makes sense as, when it does occur, the camera is inside of a wall so the perpendicular distance between the camera and that wall is zero. However, what doesn't make sense to me is that the check for being able to move forwards should stop the player from being able to enter a wall. What adds to this confusion is that the error only happens when walking forwards and not backwards (even though the test done for each is essentially the same). From debugging, I have been able to find that the error only occurs when the camera is at the starting rotation. I have attempted to fix the issue by setting the height of the line drawn to 480 (the screen size), and while that stops the program from crashing, you get this error, in which you can walk through a wall.
tldr, the raycaster is throwing a zero division error and I'm not sure how to fix it, please help.
Source code
edit: here is the required part of the source code:
procedure TForm1.ScreenRender();
var
Count : Integer;
CameraX : Double;
rayPosX : Double;
rayPosY : Double;
rayDirX : Double;
rayDirY : Double;
mapX : Integer;
mapY : Integer;
sideDistX : Double;
sideDistY : Double;
deltaDistX : Double;
deltaDistY : Double;
perpWallDist: Double;
stepX : Integer;
stepY : Integer;
hit : Integer;
side : Integer;
lineHeight : Integer;
drawStart : Integer;
drawEnd : Integer;
begin
for Count := 0 to 639 do
begin
CameraX := 2 * Count/639-1;
rayPosX := posX;
rayPosY := posY;
rayDirX := dirX + planeX * CameraX;
rayDirY := dirY + planeY * CameraX;
mapX := Trunc(rayPosX);
mapY := Trunc(rayPosY);
deltaDistX := sqrt(1 + (rayDirY * rayDirY) / (rayDirX * rayDirX));
deltaDistY := sqrt(1 + (rayDirX * rayDirX) / (rayDirY * rayDirY));
hit := 0;
if(rayDirX < 0) then
begin
stepX:=-1;
sideDistX := (rayPosX - mapX) * deltaDistX;
end
else
begin
stepX := 1;
sideDistX := (mapX + 1 - rayPosX) * deltaDistX;
end;
if(rayDirY < 0) then
begin
stepY:=-1;
sideDistY := (rayPosY - mapY) * deltaDistY ;
end
else
begin
stepY:=1;
sideDistY := (mapY + 1 - rayPosY) * deltaDistY;
end;
while(hit = 0) do
begin
if(sideDistX < sideDistY) then
begin
sideDistX := sideDistX + deltaDistX;
mapX := mapX + stepX;
side := 0;
end
else
begin
sideDistY := sideDistY + deltaDistY;
mapY := mapY + stepY;
side:= 1;
end;
if(MapData[mapX,mapY] > 0) then
hit := 1;
end;
if(side = 0) then
perpWallDist := (mapX - rayPosX + (1 - stepX) / 2) / rayDirX
else
perpWallDist := (mapY - rayPosY + (1 - stepY) / 2) / rayDirY;
//the error is here
//as the camera is in the wall and perpWallDist is 0, a zero division error is thrown
//if(perpWallDist > 0) then
lineHeight := Trunc(480 /perpWallDist);
//else
// lineHeight := 480;
drawStart := round(-lineHeight / 2 + 480 / 2);
if(drawStart < 0) then
drawStart := 0;
drawEnd := round(lineHeight / 2 + 480 / 2);
if(drawEnd >= 480) then
drawEnd := 480;
case MapData[mapX,mapY] of
1: image1.Canvas.Pen.Color := $ff0000;
2: image1.Canvas.Pen.Color := $0000ff;
3: image1.Canvas.Pen.Color := $00ff00;
4: image1.Canvas.Pen.Color := $ffffff;
end;
if(side = 1) then
image1.Canvas.Pen.Color := round( image1.Canvas.Pen.Color / 1.5);
image1.Canvas.MoveTo(Count,drawStart);
image1.Canvas.LineTo(Count,drawEnd);
end;
end;
procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word; Shift:
TShiftState);
const
moveSpeed = 0.5;
rotSpeed = 0.25;
var
oldDirX : Double;
oldPlaneX : Double;
begin
if(Key = $57) and (MovAllow = true) then
begin
if(MapData[trunc(posX + dirX * moveSpeed),trunc(posY)] = 0) then
posX := posX + (dirX * moveSpeed);
if(MapData[trunc(posX),trunc(posY + dirY * moveSpeed)] = 0) then
posY := posY + (dirY * moveSpeed);
end;
if(Key = $53) and (MovAllow = true) then
begin
if(MapData[trunc(posX - dirX * moveSpeed),trunc(posY)] = 0) then
posX := posX - (dirX * moveSpeed);
if(MapData[trunc(posX),trunc(posY - dirY * moveSpeed)] = 0) then
posY := posY - (dirY * moveSpeed);
end;
if(Key = $44) and (MovAllow = true) then
begin
oldDirX := dirX;
dirX := dirX * cos(-rotSpeed) - dirY * sin(-rotSpeed);
dirY := oldDirX * sin(-rotSpeed) + dirY * cos(-rotSpeed);
oldPlaneX := planeX;
planeX := planeX * cos(-rotSpeed) - planeY * sin(-rotSpeed);
planeY := oldPlaneX * sin(-rotSpeed) + planeY * cos(-rotSpeed);
end;
if(Key = $41) and (MovAllow = true) then
begin
oldDirX := dirX;
dirX := dirX * cos(rotSpeed) - dirY * sin(rotSpeed);
dirY := oldDirX * sin(rotSpeed) + dirY * cos(rotSpeed);
oldPlaneX := planeX;
planeX := planeX * cos(rotSpeed) - planeY * sin(rotSpeed);
planeY := oldPlaneX * sin(rotSpeed) + planeY * cos(rotSpeed);
end;
end;
edit 2: dirX and dirY only change when pressing 'A' or 'D' (key = $41 is A and key = $44 is D). They are changed using the rotation matrix. The code for them changing is just above this edit at the base of the code. From debugging, I've found that if you walk in a straight line they are not changed (this is expected). I also found that if you rotate the camera slightly in one direction and then rotate it back to what seems like the starting rotation, even though the dirX and dirY values are slightly different (instead of the initial values, they have dirX = -1 and dirY = 1.98408997564847E-0017) it still causes the same crash. I assume this is as they are almost the same values.