2013-11-14 7 views
2

Привет, сообщество stackoverflow. Недавно я работал над созданием правильной системы raycasting. В настоящее время я полностью работаю в 2D, с 2D-картой и представлением игроков. У меня возникли проблемы, но правильно создаю среду с сеткой. Я думаю, что проблема заключается в том, как я использую лучи, не основанные на сетке. У меня есть пример моей проблемы here. Как вы можете видеть, лучи кажутся изменчивыми и искаженными. Может ли кто-нибудь дать мне некоторое представление о том, как будет работать система на основе сетки? Любая помощь приветствуется, спасибо.2D Raycasting в среде на основе сетки

Вот полный исходный код (я использую 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); 

ответ

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 

кратчайшего к, если существует, образует свою линию.
Существует множество возможностей для оптимизации, когда вы идете по этому пути.

Update

Не хотел писать все это, но вот хорошая статья по оптимизации пересечения лучей коробки, особенно если окно или прямоугольник выравнивается по оси: http://www.scratchapixel.com/lessons/3d-basic-lessons/lesson-7-intersecting-simple-shapes/ray-box-intersection/

 Смежные вопросы

  • Нет связанных вопросов^_^