由于问题与任何语言无关,我从Javascript的角度来回答。
我还实施了所谓的“垂直地板/天花板”技术。
但不是每个像素绘制像素ctx.drawImage()
I use putImageData
.
首先,我从要使用临时画布渲染的图块中获取数据:
var tempCanvas = document.createElement('canvas');
var tempCtx = tempCanvas.getContext('2d');
tempCanvas.width = 64;
tempCanvas.height = 64;
var wallsSprite = new Image();
wallsSprite.onload = function () {
tempCtx.drawImage(wallsSprite, 0, 128, 64, 64, 0, 0, 64, 64);
floorData = tempCtx.getImageData(0, 0, 64, 64);
tempCtx.drawImage(wallsSprite, 0, 192, 64, 64, 0, 0, 64, 64);
ceilData = tempCtx.getImageData(0, 0, 64, 64);
}
wallsSprite.src = "./walls_2.png";
我创建一个空的 imageData:
var floorSprite = this.ctx.createImageData(600, 400);
然后我进行“垂直地板/天花板”光线投射:
//we check if the wall reaches the bottom of the canvas
// this.wallToBorder = (400 - wallHeight) / 2;
if (this.wallToBorder > 0) {
// we calculate how many pixels we have from bottom of wall to border of canvas
var pixelsToBottom = Math.floor(this.wallToBorder);
//we calculate the distance between the first pixel at the bottom of the wall and the player eyes (canvas.height / 2)
var pixelRowHeight = 200 - pixelsToBottom;
// then we loop through every pixels until we reach the border of the canvas
for (let i = pixelRowHeight; i < 200; i += 1) {
// we calculate the straight distance between the player and the pixel
var directDistFloor = (this.screenDist * 200) / (Math.floor(i));
// we calculate it's real world distance with the angle relative to the player
var realDistance = (directDistFloor / Math.cos(this.angleR));
// we calculate it's real world coordinates with the player angle
this.floorPointx = this.player.x + Math.cos(this.angle) * realDistance / (this.screenDist / 100);
this.floorPointy = this.player.y + Math.sin(this.angle) * realDistance / (this.screenDist / 100);
// we map the texture
var textY = Math.floor(this.floorPointx % 64);
var textX = Math.floor(this.floorPointy % 64);
// we modify floorSprite array:
if (floorData && ceilData) {
floorSprite.data[(this.index * 4) + (i + 200) * 4 * 600] = floorData.data[textY * 4 * 64 + textX * 4]
floorSprite.data[(this.index * 4) + (i + 200) * 4 * 600 + 1] = floorData.data[textY * 4 * 64 + textX * 4 + 1]
floorSprite.data[(this.index * 4) + (i + 200) * 4 * 600 + 2] = floorData.data[textY * 4 * 64 + textX * 4 + 2]
floorSprite.data[(this.index * 4) + (i + 200) * 4 * 600 + 3] = 255;
floorSprite.data[(this.index * 4) + (200 - i) * 4 * 600] = ceilData.data[textY * 4 * 64 + textX * 4]
floorSprite.data[(this.index * 4) + (200 - i) * 4 * 600 + 1] = ceilData.data[textY * 4 * 64 + textX * 4 + 1]
floorSprite.data[(this.index * 4) + (200 - i) * 4 * 600 + 2] = ceilData.data[textY * 4 * 64 + textX * 4 + 2]
floorSprite.data[(this.index * 4) + (200 - i) * 4 * 600 + 3] = 255;
}
}
}
}
}
最后我们在渲染墙壁之前绘制地板和天花板:
this.ctx.putImageData(floorSprite, 0, 0);
结果非常快,因为:
- 我们不需要计算天花板纹理坐标,因为我们从地板坐标中推导出它们。
- 我们每个循环只绘制一次天花板/地板,而不是每个像素绘制一次。
- 仅重绘可见的像素,因此不会
wastes performance on filling up the whole screen with floor and ceiling, and after that it draws walls
.
也许可以通过混合来优化horizontal raysting
and putImageData
将游戏速度与墙壁/天花板渲染或不进行几乎相同。
这是结果 https://i.ibb.co/Sw4jPHK/ezgif-com-gif-maker.gif