Я создаю начало механизма качания лучей, используя 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);