为什么我的光线投射引擎的鱼眼修正会导致凹壁?

问题描述 投票:0回答:1

我正在使用HTML Canvas构建光线投射引擎的开头,我很快遇到了鱼眼效果问题。有很多网站告诉你只需将光线的长度乘以它与玩家角度的余弦来校正它。

distance * Math.cos(angle)

但是,该修复仅适用于我的地图的y轴。在x轴上,它使得墙壁基本上与鱼眼效果相反。任何想法为什么会这样?

这是我的代码:

var c = document.getElementById('canvas');
var ctx = c.getContext('2d');

c.height = window.innerHeight;
c.width = window.innerWidth;

//Setup up map
var map = [];
for(var i = 0;i < 20;i++)
{
    map[i] = [];
}
map[0] = [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2];
map[1] = [2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2];
map[2] = [2, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 2];
map[3] = [2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2];
map[4] = [2, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 2];
map[5] = [2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2];
map[6] = [2, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 2];
map[7] = [2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2];
map[8] = [2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2];
map[9] = [2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2];
map[10] = [2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2];
map[11] = [2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2];
map[12] = [2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2];
map[13] = [2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2];
map[14] = [2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2];
map[15] = [2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2];
map[16] = [2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2];
map[17] = [2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2];
map[18] = [2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2];
map[19] = [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2];

var character = {
    x: c.width / 2,
    y: (c.height / 2) + 100,
    r: 25,
    angle: 0
};

//Define all rays
var rays = [];
for(var i = 0;i < 300;i++)
{
    rays[i] = {
        x: 0,
        y: 0,
        travelling: false,
        hit: false,
        type: 0
    };
}

//GET INPUT
var turningLeft = false,
    turningRight = false,
    movingUp = false,
    movingDown = false;

window.addEventListener('keydown', handleKeyDown, true);
window.addEventListener('keyup', handleKeyUp, true);
function handleKeyDown(e)
{
    switch(e.keyCode)
    {
        case 87: movingUp = true;
            break;
        case 83: movingDown = true;
            break;
        case 65: turningLeft = true;
            break;
        case 68: turningRight = true;
            break;
    }
}
function handleKeyUp(e)
{
    switch(e.keyCode)
    {
        case 87: movingUp = false;
            break;
        case 83: movingDown = false;
            break;
        case 65: turningLeft = false;
            break;
        case 68: turningRight = false;
            break;
    }
}

function gameLoop()
{
    update();
    render();
    
    window.requestAnimationFrame(gameLoop);
}

function update()
{
        
    //Allow movement
    if(movingUp)
    {
        if(!detectCharacterCollision(character.x + Math.cos(character.angle) * 2, character.y))
        {
            character.x += Math.cos(character.angle) * 2;
        }
        if(!detectCharacterCollision(character.x, character.y + Math.sin(character.angle) * 2))
        {
            character.y += Math.sin(character.angle) * 2;
        }
    }
    if(movingDown)
    {
        character.x -= Math.cos(character.angle);
        character.y -= Math.sin(character.angle);
    }
    if(turningLeft)
    {
        character.angle -= Math.PI / 180;
    }
    if(turningRight)
    {
        character.angle += Math.PI / 180;
    }
        
    //Cast ray
    for(var i = 0;i < rays.length;i++)
    {
        rays[i] = {
            x: character.x,
            y: character.y,
            travelling: true,
            hit: false,
            type: 0
        };
        
        //Until the ray hits a wall
        while(rays[i].travelling)
        {
            //Detect if ray has hit a wall
            var collision = detectRayCollision(rays[i].x, rays[i].y);
            //Collision has the type of wall which was collided with (0 for no wall)
            if(collision == 1)
            {
                rays[i].travelling = false;
                rays[i].hit = true;
                rays[i].type = 1;
            }
            else if(collision == 2)
            {
                rays[i].travelling = false;
                rays[i].hit = true;
                rays[i].type = 2;
            }
            else
            {
                //If nothing was hit, move ray is appropriate direction from player
                var angle = (i * ((Math.PI / 2) / rays.length)) - (Math.PI / 4);
                
                rays[i].x += Math.cos(character.angle + angle);
                rays[i].y += Math.sin(character.angle + angle);
            }
        }
    }
}

function detectRayCollision(x, y)
{
    return map[Math.trunc(y / (c.height / 20))][Math.trunc(x / (c.width / 20))];
}

function detectCharacterCollision(x, y)
{
    if(map[Math.trunc(y / (c.height / 20))][Math.trunc(x / (c.width / 20))] == 0)
    {
        return false;
    }
    else
    {
        return true;
    }
}

function getTime()
{
    var date = new Date();
    return date.getTime();
}

function render()
{
    ctx.clearRect(0, 0, c.width, c.height);
    
    //Skybox
    ctx.beginPath();
    ctx.rect(0, 0, c.width, c.height / 2);
    ctx.fillStyle = 'rgb(135,206,250)';
    ctx.fill();
    ctx.closePath();
    
    //Floor
    ctx.beginPath();
    ctx.rect(0, c.height / 2, c.width, c.height / 2);
    ctx.fillStyle = 'black';
    ctx.fill();
    ctx.closePath();
    
    for(var i = 0;i < rays.length;i++)
    {
        var dx = rays[i].x - character.x;
        var dy = rays[i].y - character.y;
        var angle = Math.atan2(dy, dx);
        var distance = Math.sqrt((dy * dy) + (dx * dx));
        var z = distance * Math.cos(angle);
        
        ctx.beginPath();
        
        //Set color (or texture) for wall
        if(rays[i].type == 1)
        {
            ctx.fillStyle = 'grey';
        }
        else if(rays[i].type == 2)
        {
            ctx.fillStyle = 'orange';
        }
        
        ctx.fillRect(i * (c.width / rays.length), (c.height / 2) - ((c.height / (z / 100)) / 2), c.width / rays.length + 1, c.height / (z / 100));
        
        ctx.closePath();
    }
}

window.requestAnimationFrame(gameLoop);
javascript html canvas raycasting
1个回答
0
投票

我已经回答了我自己的问题。我没有考虑到玩家目前面临的方向。所以在我的代码中,鱼眼校正的线应该是:

var z = distance * Math.cos(angle - character.angle);
© www.soinside.com 2019 - 2024. All rights reserved.