2

你好 stackoverflow 社区。最近,我一直在努力让适当的光线投射系统正常工作。目前,我完全在 2D 中工作,使用 2D 地图和玩家表示。我遇到了问题,但是正确地创建了基于网格的环境。我认为问题在于我投射光线的方式根本不是基于网格的。我在这里有一个我的问题的例子。如您所见,光线看起来波涛汹涌且畸形。谁能给我一些关于基于网格的系统如何工作的见解?任何帮助表示赞赏,谢谢。

这是完整的源代码(我使用 PIXI.js 进行渲染):

var world = [
        [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
        [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
        [1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
        [1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1],
        [1,0,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
        [1,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
        [1,0,1,0,0,1,0,0,0,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,1,1,1],
        [1,0,1,1,1,1,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1],
        [1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1],
        [1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,1,1,1,1,1],
        [1,0,0,0,0,0,0,0,0,1,1,1,1,0,1,1,0,0,0,0,0,0,0,0,0,0,1],
        [1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
        [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1],
        [1,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
        [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,1],
        [1,0,1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,1],
        [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,1],
        [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
];

var width = world[0].length;
var height = world.length;

var scale = 8;

var posX = 1;
var posY = 1;
var yaw = 0;
var m = 0;

var renderer = new PIXI.WebGLRenderer(width * scale,height * scale);
//var renderer = new PIXI.WebGLRenderer(320,200);

document.body.appendChild(renderer.view);

var stage = new PIXI.Stage(0xFFFFFF);
var graphics = new PIXI.Graphics();
stage.addChild(graphics);

function drawMap()
{
    for(var x = 0;x < width;x++)
    {
        for(var y = 0;y < height;y++)
        {
            if(world[y][x])
            {
                graphics.beginFill(0xCCCCCC);
                graphics.drawRect(x * scale, y * scale, scale, scale);
                graphics.endFill();
            }
        }
    }
}

function drawPlayer()
{
    graphics.beginFill(0x000000);
    graphics.drawRect(posX * scale, posY * scale, 4, 4);
    graphics.endFill();

    graphics.lineStyle(1,0x000000);
    graphics.moveTo(posX * scale + 2, posY * scale + 2);
    graphics.lineTo(posX * scale + Math.cos(yaw) * 20 + 2, posY * scale + Math.sin(yaw) * 20 + 2);
}

function move()
{
    var newX = posX + Math.cos(yaw) * m * 0.3;
    var newY = posY + Math.sin(yaw) * m * 0.3;

    m = 0;

    if(isColliding(newX,newY))
    {
        return;
    }

    posX = newX;
    posY = newY;
}

function isColliding(x,y)
{
    if(world[Math.floor(y)][Math.floor(x)])
    {
        return true;
    }

    return false;
}

function castRays()
{
    var rayYaw = 0;

    var rayX = posX;
    var rayY = posY;

    var dist = 0;

    for(var x = -160;x < 160;x++)
    {
        rayYaw = x * 0.1875;

        while(!isColliding(rayX,rayY))
        {
            rayX += Math.cos((rayYaw) * (Math.PI / 180) + yaw);
            rayY += Math.sin((rayYaw) * (Math.PI / 180) + yaw);

            if(rayX < 0 || rayX >= width || rayY < 0 || rayY >= height)
            {
                break;
            }
        }

        dist = Math.sqrt(Math.pow(posX - rayX,2) + Math.pow(posY - rayY,2));

        graphics.lineStyle(1,0x00FFCC);
        graphics.moveTo(posX * scale + 2, posY * scale + 2);
        graphics.lineTo(rayX * scale + Math.cos((rayYaw) * (Math.PI / 180)) + 2, rayY * scale + Math.sin((rayYaw) * (Math.PI / 180)) + 2);

        //drawLine(x + 160,dist);

        rayX = posX;
        rayY = posY;
    }
}

function drawLine(x,d)
{
    var slice = (32 * d / 160);

    var start = (100 - (slice/2));

    graphics.lineStyle(1,0xCCCCCC);
    graphics.moveTo(x,start);
    graphics.lineTo(x,slice);
}

function main()
{
    drawMap();
    move();
    drawPlayer();
    castRays();

    renderer.render(stage);
    graphics.clear();
}

document.onkeydown = checkKey;

function checkKey(e) {

    e = e || window.event;

    if (e.keyCode == '38') 
    {
        // up arrow
        m = 1;
    }
    else if (e.keyCode == '40') 
    {
        // down arrow
        m = -1;
    }
    else if (e.keyCode == '37')
    {
        // left arrow
        yaw -= 0.1;
    }
    else if (e.keyCode == '39')
    {
        // right arrow
        yaw += 0.1;
    }
}

setInterval(main,1000/30);
4

1 回答 1

3

您的问题是您将地图视为矩阵并且 isColliding() 过于简单。您确实应该将矩阵中的每个 1 视为一个正方形,以确定您的光线是否以及在何处击中它。

我会将 castRays 完全重写为更传统的光线追踪方法:

for each point on your "screen":
  define vector V from point of view P through this point on screen
  for each line S + L*y forming sides of each square, find if your vector intersects it:
      P + V*k = S + L*y when k>0 and y is in 0..1

最短的 k(如果存在)形成您的线。
当你沿着这条路走时,有很大的优化空间。

更新

不想全部写出来,但是这里有一篇关于 ray-box 相交优化的好文章,特别是如果盒子或矩形与轴对齐:http ://www.scratchapixel.com/lessons/3d-basic-课程/第 7 课-相交-简单形状/光线盒-相交/

于 2013-11-14T05:20:38.920 回答