所以我一直在编写一个delphi转换的this无纹理raycaster。但是,在检查错误时,我注意到如果您直接向前走,则会发生零分割错误。这是有道理的,因为当它确实发生时,摄像机位于墙内,因此摄像机与该墙之间的垂直距离为零。然而,对我来说没有意义的是,能够向前移动的检查应该阻止玩家进入墙壁。更令人困惑的是,错误只发生在前进而不是后退时(即使为每个步骤完成的测试基本相同)。从调试开始,我发现错误只发生在摄像机处于起始旋转状态时。我试图通过将绘制的线的高度设置为480(屏幕大小)来解决问题,并且在停止程序崩溃的同时,你会得到这个error,你可以穿过墙壁。 tldr,raycaster抛出零分错误,我不知道如何解决它,请帮忙。 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;
编辑2:dirX和dirY仅在按下'A'或'D'时改变(键= $ 41是A,键= $ 44是D)。它们使用旋转矩阵进行更改。它们的代码改变就在代码底部的编辑之上。从调试开始,我发现如果你走直线就不会改变(这是预期的)。我还发现,如果你在一个方向稍微旋转相机,然后将其旋转回看起来像起始旋转,即使dirX和dirY值略有不同(而不是初始值,它们有dirX = -1和dirY = 1.98408997564847E-0017)它仍会导致同样的崩溃。我认为这是因为它们几乎是相同的值。
只需在调试器中运行程序,Delphi就会告诉您错误发生在哪一行。
即使没有它,您也可以检查所有划分的地方是否安全。
看看这一行:
deltaDistX := sqrt(1 + (rayDirY * rayDirY) / (rayDirX * rayDirX));
当rayDir为0时会发生什么?
perpWallDist := (mapX - rayPosX + (1 - stepX) / 2) / rayDirX
当rayDir为0时会发生什么?
lineHeight := Trunc(480 /perpWallDist);
当perpWallDist为0时会发生什么?
更新为什么perpWallDist可以为0?好吧,在设置它的地方放置一个断点,并查看用于设置变量的值。
perpWallDist := (mapX - rayPosX + (1 - stepX) / 2) / rayDirX
其余的我无法帮助你。这只是一个简单的调试问题。如果你在某个地方遇到了这个过程,那就创建一个关于你遇到的问题的单独问题。