我正在研究光线投射器,并遵循了本教程:https://dev.opera.com/articles/3d-games-with-canvas-and-raycasting-part-1/
我的问题是关于一个Bug,该Bug会根据在画布上绘制的位置绘制/计算不同宽度的Walls(因此,射线角度对玩家的方向点(中心视图)有多大): 中间(1.)中绘制的墙较小,但屏幕左侧或右侧(2.)上的墙则绘制得较宽。当您查看图像时,它最容易理解。我想我只是对数学有误,可能是四舍五入到了我不应该发现的地方,但我还没有找到它,或者可以想到此错误的任何原因。它是使用JavaScript在HTML Canvas中制成的。
在我的第一个功能中,我为画布的每个x像素发出一束光线:
let resolution = Math.ceil(canvas.width / this.resolution); //canvas width = 1600, resolution = 1
let id = 0;
for (let x = 0; x < resolution; x++) {
let viewDist = (canvas.width / this.resolution) / Math.tan((this.fov / 2)); //fov 90 in rad
let rayx = (-resolution / 2 + x) * this.resolution;
let rayDist = Math.sqrt(rayx * rayx + viewDist * viewDist);
let rayAngle = Math.asin(rayx / rayDist);
let wall = this.castWall(this.pod * Math.PI / 180 + rayAngle);
this.drawWall(x, wall);
}
但是我不认为这里有什么问题。在第二个函数中,我向每条射线投射光并返回到碰撞墙的距离。我的积木/墙壁宽50。我的地图存储在2D数字Array-> this.map.grid中,this.map.width保持x方向上有多少块,this.map.height保持y方向上的计数。
castWall(angle) {
const PI2 = Math.PI * 2;
angle %= PI2;
if (angle < 0) {
angle += PI2;
}
let right = angle > PI2 * 0.75 || angle < PI2 * 0.25;
let up = angle < 0 || angle > Math.PI;
let sin = Math.sin(angle);
let cos = Math.cos(angle);
let dist = 0;
let textureX;
let texture;
let slope = sin / cos;
let dXVer = right ? 1 : -1;
let dYVer = dXVer * slope;
let px = this.x / 50;
let py = this.y / 50;
let x = right ? Math.ceil(px) : Math.floor(px);
let y = py + (x - px) * slope;
while (x >= 0 && x < this.map.width && y >= 0 && y < this.map.height) {
let wallX = Math.floor(x + (right ? 0 : -1));
let wallY = Math.floor(y);
if (this.map.grid[wallY][wallX] > 0) {
dist = Math.sqrt(Math.pow(x - px, 2) + Math.pow(y - py, 2));
texture = this.map.grid[wallY][wallX];
textureX = (y * 50) % 50;
if (right) {
textureX = 50 - textureX;
}
break;
}
x += dXVer;
y += dYVer;
}
slope = cos / sin;
let dYHor = up ? -1 : 1;
let dXHor = dYHor * slope;
y = up ? Math.floor(py) : Math.ceil(py);
x = px + (y - py) * slope;
while (x >= 0 && x < this.map.width && y >= 0 && y < this.map.height) {
let wallY = Math.floor(y + (up ? -1 : 0));
let wallX = Math.floor(x);
if (this.map.grid[wallY][wallX] > 0) {
let distHor = Math.sqrt(Math.pow(x - px, 2) + Math.pow(y - py, 2));
if (dist === 0 || distHor < dist) {
dist = distHor;
texture = this.map.grid[wallY][wallX];
textureX = (x * 50) % 50;
if (up) {
textureX = 50 - textureX;
}
}
break;
}
x += dXHor;
y += dYHor;
}
return {
distance: dist,
texture: texture,
textureX: textureX
};`
我还尝试了使用其他算法(Bresenham和DDA)进行射线广播,但是我从未真正使它们起作用。这是唯一对我有用的。如果您对代码有任何疑问,请随时提问。