基于网格的环境中的2D光线投射

时间:2013-11-14 04:47:53

标签: javascript raycasting pixi.js

Hello 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);

1 个答案:

答案 0 :(得分: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,如果存在,形成你的线 沿着这条路走下去,还有很大的优化空间。

<强>更新

不想全部写完,但这里有一篇关于光线盒交叉点优化的好文章,特别是如果框或矩形与轴对齐:http://www.scratchapixel.com/lessons/3d-basic-lessons/lesson-7-intersecting-simple-shapes/ray-box-intersection/