您好stackoverflow社区。最近,我一直在努力使适当的光线投射系统正常工作。目前,我一直在2D地图和播放器表示方面完全以2D进行工作。但是在正确创建基于网格的环境时遇到了问题。我认为问题在于我投射光线的方式根本不是基于网格的。我有一个问题here的示例。如您所见,射线显得断断续续且畸形。谁能给我一些有关基于网格的系统如何工作的见解?任何帮助表示赞赏,谢谢。

这是完整的源代码(我正在使用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);

最佳答案

您的问题是将地图视为矩阵,而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(如果存在)形成您的线。
当您沿着这条道路前进时,有很多优化的空间。

更新资料

不想全部写下来,但是这里是有关射线盒相交优化的好文章,尤其是当盒子或矩形与轴对齐时:http://www.scratchapixel.com/lessons/3d-basic-lessons/lesson-7-intersecting-simple-shapes/ray-box-intersection/

关于javascript - 基于网格的环境中的2D射线广播,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/19969744/

10-14 09:53