Lua光线投射异常排查:新高效光线投射代码问题咨询
先把你的代码片段整理成更清晰的格式,方便分析:
-- 射线方向向量(角度r转弧度) vectorX, vectorY = math.cos(r * math.pi/180), math.sin(r * math.pi/180) -- 墙体线段的方向向量(v[1],v[2]是起点,v[3],v[4]是终点) wallDistanceX, wallDistanceY = v[3] - v[1], v[4] - v[2] -- 墙体的垂直向量 wallParallelX, wallParallelY = -wallDistanceY, wallDistanceX -- 相机到墙体起点的向量 rayX, rayY = v[1] - cam.x, v[2] - cam.y -- 计算射线参数t(Distance) Distance = (rayX * wallParallelX + rayY * wallParallelY) / (vectorX * wallParallelX + vectorY * wallParallelY) -- 不完整的交点范围判断 if (math.min(v[1],v[3]) <= (cam.x + Distance * vectorX) and math.max(v[1],v[3]) >= (cam.x + Dista...
接下来逐个分析可能引发异常的点,以及对应的修复方案:
1. 除数为零的致命错误
你的分母vectorX * wallParallelX + vectorY * wallParallelY是射线方向向量和墙体垂直向量的点积。如果射线和墙体完全平行,这个点积会等于0,直接触发Lua的attempt to divide by zero错误,导致程序崩溃。
修复方案:
计算前先判断分母是否接近0(考虑浮点精度误差,不要直接判断等于0),如果是则跳过当前墙体:
local denominator = vectorX * wallParallelX + vectorY * wallParallelY -- 用极小值规避浮点精度问题 if math.abs(denominator) < 1e-6 then -- 射线与墙体平行,无有效交点,跳过 goto continue end Distance = (rayX * wallParallelX + rayY * wallParallelY) / denominator
2. 变量拼写错误
最后一行的Dista是Distance的拼写错误,这会导致attempt to index global 'Dista' (a nil value)错误,直接中断代码执行。
修复方案:
把Dista改成完整的Distance即可。
3. 交点范围判断不完整
你只判断了交点的X坐标是否在墙体的X区间内,但完全忽略了Y坐标的范围。这会导致程序把墙体线段延长线上的点当成有效交点,比如射线穿过墙体端点的延长线时,会错误判定为碰撞。
修复方案:
补充Y轴的范围判断,同时优化区间判断的写法:
local intersectX = cam.x + Distance * vectorX local intersectY = cam.y + Distance * vectorY -- 检查交点是否在墙体线段的X和Y区间内 local inXRange = intersectX >= math.min(v[1], v[3]) and intersectX <= math.max(v[1], v[3]) local inYRange = intersectY >= math.min(v[2], v[4]) and intersectY <= math.max(v[2], v[4]) if inXRange and inYRange then -- 有效交点,继续后续处理 -- ... end
4. 忽略射线方向的有效性(负Distance)
如果计算出的Distance是负数,说明交点在相机的后方(射线起点的反方向),这时候这个交点是无效的,不能作为墙体碰撞点,否则会导致渲染出相机背后的墙体,引发视觉异常。
修复方案:
添加Distance的正负判断:
if Distance > 0 and inXRange and inYRange then -- 仅处理前方的有效交点 -- ... end
5. 向量逻辑的潜在问题
你的rayX, rayY是相机到墙体起点的向量,但在参数方程推导中,正确的射线参数方程应该是:射线点 = 相机位置 + t * 射线方向向量,墙体线段的参数方程是:墙体点 = v1 + s * 墙体方向向量。你的投影计算逻辑虽然能得到t,但如果墙体端点的选择逻辑有偏差,可能会出现计算误差。
优化建议:
改用标准的线段-射线交点算法,逻辑更清晰也更容易排查问题:
-- 标准线段-射线交点计算 local x1, y1 = cam.x, cam.y -- 射线起点 local x2, y2 = cam.x + vectorX, cam.y + vectorY -- 射线方向终点 local x3, y3 = v[1], v[2] -- 墙体起点 local x4, y4 = v[3], v[4] -- 墙体终点 local denom = (x1 - x2)*(y3 - y4) - (y1 - y2)*(x3 - x4) if math.abs(denom) < 1e-6 then goto continue -- 平行或重合,无有效交点 end local t = ((x1 - x3)*(y3 - y4) - (y1 - y3)*(x3 - x4)) / denom local u = -((x1 - x2)*(y1 - y3) - (y1 - y2)*(x1 - x3)) / denom -- t>0表示在射线前进方向,u在0-1之间表示在线段上 if t > 0 and u >= 0 and u <= 1 then local intersectX = x1 + t*(x2 - x1) local intersectY = y1 + t*(y2 - y1) -- 处理有效交点的逻辑 end
6. 角度输入的合法性
如果变量r不是有效的数字(比如NaN、nil),math.cos和math.sin会返回NaN,后续所有计算都会变成NaN,导致异常。
修复方案:
在计算方向向量前,先检查r的合法性:
if type(r) ~= "number" or r ~= r then -- r ~= r 用来判断NaN -- 设置默认角度,比如朝向正右 r = 0 end vectorX, vectorY = math.cos(r * math.pi/180), math.sin(r * math.pi/180)
内容的提问来源于stack exchange,提问作者crazicrafter1

