Почти рабочий рейкастинг

374
24 октября 2017, 03:50

Здравствуйте! Сейчас пытаюсь написать 3d бродилку, руководствуясь этой статьей.

Что есть на данный момент:

function rect(x, y, width, height, color) { 
  var ret = document.createElementNS("http://www.w3.org/2000/svg", "rect"); 
  ret.setAttribute('x', x); 
  ret.setAttribute('y', y); 
  ret.setAttribute('width', width); 
  ret.setAttribute('height', height); 
  ret.setAttribute('fill', color.replace(' ', '')); 
  return ret; 
} 
 
function circ(x, y, r, color) { 
  var ret = document.createElementNS("http://www.w3.org/2000/svg", "circle"); 
  ret.setAttribute('cx', x); 
  ret.setAttribute('cy', y); 
  ret.setAttribute('r', r); 
  ret.setAttribute('fill', color.replace(' ', '')); 
  return ret; 
} 
 
function line(x1, y1, x2, y2, color) { 
  var ret = document.createElementNS("http://www.w3.org/2000/svg", "line"); 
  ret.setAttribute('x1', x1); 
  ret.setAttribute('y1', y1); 
  ret.setAttribute('x2', x2); 
  ret.setAttribute('y2', y2); 
  ret.setAttribute('stroke', color.replace(' ', '')); 
  return ret; 
} 
 
// для того, чтобы можно было отличать стены по цветам. 
function wallColor(x, y) { 
  var colors = ['Cyan', 'Dark Blue', 'Dark Cyan', 'Dark Goldenrod', 'Dark Gray', 'Dark Green', 'Dark Khaki', 'Dark Magenta', 'Dark Orange', 'Dark Orchid', 'Dark Red', 'Dark Salmon', 'Dark Turquoise', 'Dark Violet', 'Deep Pink', 'Dim Gray', 'Dodger Blue', 'Firebrick', 'Forest Green', 'Fuchsia', 'Gainsboro', 'Gold', 'Goldenrod', 'Gray', 'Gray', 'Green', 'Green', 'Green Yellow', 'Hot Pink', 'Indian Red', 'Indigo', 'Khaki', 'Lawn Green', 'Light Blue', 'Light Coral', 'Light Cyan', 'Light Goldenrod', 'Light Gray', 'Light Green', 'Light Pink', 'Light Salmon', , 'Light Sky Blue', 'Light Slate Gray', 'Light Yellow', 'Lime', 'Lime Green', 'Linen', 'Magenta', 'Maroon', 'Maroon', 'Medium Aquamarine', 'Medium Blue', 'Medium Orchid', 'Medium Purple', 'Medium Sea Green', 'Medium Slate Blue', 'Medium Spring Green', 'Medium Turquoise', 'Medium Violet Red', 'Midnight Blue', 'Mint Cream', 'Misty Rose', 'Moccasin', 'Navy', 'Old Lace', 'Olive', 'Olive Drab', 'Orange', 'Orange Red', 'Orchid', 'Pale Goldenrod', 'Pale Green', 'Pale Turquoise', 'Papaya Whip', 'Peach Puff', 'Peru', 'Pink', 'Plum', 'Powder Blue', 'Purple', 'Purple', 'Red', 'Rosy Brown', 'Royal Blue', 'Saddle Brown', 'Salmon', 'Sandy Brown', 'Sea Green', 'Seashell', 'Sienna', 'Silver', 'Sky Blue', 'Slate Blue', 'Slate Gray', 'Snow', 'Spring Green', 'Steel Blue', 'Tan', 'Teal', 'Thistle', 'Tomato', 'Turquoise', 'Violet', 'Wheat', 'Yellow', 'Yellow Green'] 
 
  if (x < 0 || y < 0 || x >= maze.size || y >= maze.size) 
    return 'black'; 
 
  return colors[maze.blocks[x][y]]; 
} 
 
function pointInWall(x, y) { 
  var xr = Math.floor(x / minimap.K); 
  var yr = Math.floor(y / minimap.K); 
 
  if (xr < 0 || yr < 0 || xr >= maze.size || yr >= maze.size) 
    return false; 
 
  return maze.field[xr][yr] == 1; 
} 
 
 
var player = { 
  x: 5, 
  y: 5, 
  angle: 0 
}; 
var camera = { 
  dist: 100, 
  angle: Math.PI / 4, 
  res: 0.01 
}; 
 
var maze = { 
  size: 10, 
  field: [ 
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
    [0, 1, 1, 0, 1, 1, 0, 1, 1, 0], 
    [0, 1, 1, 0, 1, 1, 0, 1, 1, 0], 
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
    [0, 1, 1, 0, 1, 1, 0, 1, 1, 0], 
    [0, 1, 1, 0, 1, 1, 0, 1, 1, 0], 
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
    [0, 1, 1, 0, 1, 1, 0, 1, 1, 0], 
    [0, 1, 1, 0, 1, 1, 0, 1, 1, 0], 
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] 
  ], 
  blocks: [ 
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
    [0, 1, 2, 0, 5, 6, 0, 9, 10, 0], 
    [0, 4, 3, 0, 8, 7, 0, 11, 12, 0], 
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
    [0, 29, 31, 0, 33, 34, 0, 14, 13, 0], 
    [0, 30, 32, 0, 36, 35, 0, 15, 16, 0], 
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
    [0, 25, 26, 0, 24, 21, 0, 20, 17, 0], 
    [0, 28, 27, 0, 23, 22, 0, 19, 18, 0], 
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] 
  ] 
} 
 
minimap.K = 20; // масштаб карты 
 
function key(k) { 
  var xold = player.x; 
  var yold = player.y; 
 
  var cos = Math.cos(player.angle); 
  var sin = Math.sin(player.angle); 
 
  if (k == 'W') { 
    player.x += cos; 
    player.y += sin; 
  } 
 
  if (k == 'S') { 
    player.x -= cos; 
    player.y -= sin; 
  } 
 
  if (k == 'D') { 
    player.angle += 0.1; 
  } 
 
  if (k == 'A') { 
    player.angle -= 0.1; 
  } 
 
  var xr = Math.floor(player.x / minimap.K); 
  var yr = Math.floor(player.y / minimap.K); 
 
  if (maze.field[xr][yr] == 1 || player.x < 0 || player.y < 0 || player.x > maze.size * minimap.K || player.y > maze.size * minimap.K) { 
    player.x = xold; 
    player.y = yold; 
  } 
 
  draw(); 
} 
 
 
 
// рисует и сцену и карту 
function draw() { 
  while (minimap.firstChild) 
    minimap.removeChild(minimap.firstChild); 
 
  while (canv.firstChild) 
    canv.removeChild(canv.firstChild); 
 
  var rays = []; 
 
  for (var a = player.angle - camera.angle; a < player.angle + camera.angle; a += camera.res) { 
    var x = player.x; 
    var y = player.y; 
 
    var xs = Math.cos(a); 
    var ys = Math.sin(a); 
 
    var color = 'white'; 
 
    // есди луч врезается в стену, то  
 
    while (Math.hypot(x - player.x, y - player.y) < camera.dist) { 
      x += xs; 
      y += ys; 
 
      if (pointInWall(x, y)) { 
        var xr = Math.floor(x / minimap.K); 
        var yr = Math.floor(y / minimap.K); 
 
        color = wallColor(xr, yr); 
        break; 
      } 
    } 
 
    rays.push({ 
      angle: a, 
      length: Math.hypot(x - player.x, y - player.y), 
      color: color 
    }); 
  } 
 
  var width = canv.getBoundingClientRect().width; 
  var height = canv.getBoundingClientRect().height; 
  var y0 = canv.getBoundingClientRect().height / 2; 
  var step = rays.length / width; 
 
  for (var x = 0; x < width; x++) { 
    var i = Math.floor(x * step); 
    var y = height / rays[i].length / Math.cos(rays[i].angle); 
 
    canv.appendChild(line(x, y0 + y, x, y0 - y, rays[i].color)); 
  } 
 
  for (var x = 0; x < maze.size; x++) 
    for (var y = 0; y < maze.size; y++) 
      if (maze.field[x][y] != 0) 
        minimap.appendChild(rect(x * minimap.K, y * minimap.K, minimap.K, minimap.K, wallColor(x, y))); 
      else 
        minimap.appendChild(rect(x * minimap.K, y * minimap.K, minimap.K, minimap.K, 'white')); 
 
  minimap.appendChild(circ(player.x, player.y, minimap.K / 10, 'red')); 
 
  for (var i = 0; i < rays.length; i++) 
    minimap.appendChild(line(player.x, player.y, 
      player.x + Math.cos(rays[i].angle) * rays[i].length, player.y + Math.sin(rays[i].angle) * rays[i].length, 'red')) 
}
svg { 
  height: 100%; 
} 
 
#minimap { 
  width: 50%; 
  height: 100%; 
} 
 
#canv { 
  width: 50%; 
}
<body onload='draw()' onkeydown='key(String.fromCharCode(event.keyCode))'> 
  <table> 
    <tr> 
      <svg id='canv'> 
 
		</svg> 
      <svg id='minimap'> 
 
		</svg> 
    </tr> 
  </table> 
</body>

Управление: W\S - вперед\назад, A\D повернуться влево\вправо.

Как видите, в некоторых местах картинка очень похожа на правильную, а в некоторых присутствуют ужасные искажения.

Я, скорее всего, что-то не учитываю и недопонимаю, но что именно - непонятно. Статья очень куцая, а прогуглить почему-то не получается.

Не могли бы вы помочь?

READ ALSO
Кастомная сортировка

Кастомная сортировка

Привет, Я писал тестовый фильтр который должен возвращать мне данные в ng-repeat

309
JS - Эмуляция кнопки &ldquo;назад&rdquo; в javascript

JS - Эмуляция кнопки “назад” в javascript

Хочу написать кнопку, при нажатии на которую будет эмулироваться кнопка "назад", как в браузереЕсть такая функция в javascript?

265
JQuery - Как создать переменную только для context?

JQuery - Как создать переменную только для context?

Вот, например, есть такой код:

307
Удаление с массива, добавление в массив. (React / JS)

Удаление с массива, добавление в массив. (React / JS)

Добрый день, есть массив данных, мне нужно с этих данных вывести на FE name но при этом отправить на BE codeОдинаковые code на BE может уйти несколько:

581