RayCasting функция не работает
У меня есть объект с данными об игроке и объект с информацией для raycasting'а:
const player = {
x: HALF_WIDTH,
y: HALF_HEIGHT,
angle: 0,
speed: 2,
};
const ray = {
FOV: Math.PI / 3,
HALF_FOV: this.FOV / 2,
NUM_RAYS: 80,
MAX_DEPTH: 400,
DELTA_ANGLE: this.FOV / this.NUM_RAYS,
};
Ещё у меня есть массив map, где хранятся начальные и конечные точки стен по x и у. И у меня есть функция, которая рисует на карте лучи, исходящие от игрока:
function rayCasting(player, ray) {
let curAngle = player.angle - ray.HALF_FOV;
let xo = player.x;
let yo = player.y;
ctx.strokeStyle = 'darkgray';
for (let rayNum = 0; rayNum < ray.NUM_RAYS; rayNum++) {
sinAngle = Math.sin(curAngle);
cosAngle = Math.cos(curAngle);
let lastX = lastY = 400;
for (let depth = 0; depth < ray.MAX_DEPTH; depth++) {
let x = (xo + depth) * cosAngle;
let y = (yo + depth) * sinAngle;
if (Math.floor(x) === map.x || Math.floor(y) === map.y || Math.floor(x) === map.lastX || Math.floor(y) === map.lastY) {
lastX = x;
lastY = y;
break;
}
}
ctx.beginPath();
ctx.moveTo(xo, yo);
ctx.lineTo(lastX, lastY);
ctx.stroke();
curAngle += ray.DELTA_ANGLE;
}
}
К сожалению, функция, которая отвечает за рисование лучей, работает некорректно. Где может быть ошибка и как исправить эту функцию?
Ответы (1 шт):
Три ошибки:
- Внутри объекта нельзя ссылаться на самого себя во время инициализации. В вашем коде
const ray = {
FOV: Math.PI / 3,
HALF_FOV: this.FOV / 2,
NUM_RAYS: 80,
MAX_DEPTH: 400,
DELTA_ANGLE: this.FOV / this.NUM_RAYS,
};
this - глобальный объект.
Подобный объект можно создавать через конструктор, где this будет ссылаться на созданный объект при вызове:
const ray = new Ray();
function Ray() {
this.FOV = Math.PI / 3;
this.HALF_FOV = this.FOV / 2;
this.NUM_RAYS = 80;
this.MAX_DEPTH = 400;
this.DELTA_ANGLE = this.FOV / this.NUM_RAYS;
}
Или так:
const ray = {
FOV: Math.PI / 3,
NUM_RAYS: 80,
MAX_DEPTH: 400,
};
ray.HALF_FOV = ray.FOV / 2;
ray.DELTA_ANGLE = ray.FOV / ray.NUM_RAYS;
let lastX = lastY = 400;— здесь lastY не объявляется. В режиме "use strict" выдало бы ошибку, если lastY где-то заранее не объявлен. Должно бытьlet lastX = 400, lastY = 400;Не предоставили воспроизводимый пример, пришлось додумывать!))
let ctx = canvas.getContext("2d");
const map = { x: 0, lastX: 200, _x: [0, 200], y: 0, lastY: 100, _y: [0, 100] };
const player = { x: 50, y: 50, angle: 0, speed: 2 };
const ray = new Ray();
rayCasting(player, ray);
/***/
function rayCasting(player, ray) {
let curAngle = player.angle - ray.HALF_FOV;
let xo = player.x;
let yo = player.y;
/***/
for (let rayNum = 0; rayNum < ray.NUM_RAYS; rayNum++) {
let sin = Math.sin(curAngle);
let cos = Math.cos(curAngle);
curAngle += ray.DELTA_ANGLE;
let lastX = 400, lastY = 400;
for (let depth = 0; depth < ray.MAX_DEPTH; depth++) {
let x = (xo + depth) * cos;
let y = (yo + depth) * sin;
if (map._x.includes(x|0) || map._y.includes(y|0)) {
lastX = x;
lastY = y;
break;
}
}
/***/
ctx.strokeStyle = 'darkgray';
ctx.beginPath();
ctx.moveTo(xo, yo);
ctx.lineTo(lastX, lastY);
ctx.stroke();
}
}
function Ray() {
this.FOV = Math.PI / 3;
this.HALF_FOV = this.FOV / 2;
this.NUM_RAYS = 80;
this.MAX_DEPTH = 400;
this.DELTA_ANGLE = this.FOV / this.NUM_RAYS;
}
<canvas id="canvas"></canvas>
По поводу цепочки проверок Math.floor(x) === map.x ... Если число положительное, Math.floor(x) можно заменить на x|0 (побитовое "или", ноль - ничего не делает, операция отбрасывает дробную часть).
А в сам объект map можно добавить дополнительные свойства - массивы, хранящие оба значения: [x, lastX] и [y, lastY], чтобы проверять наличие числа в массиве, переписав всю проверку так: map._x.includes(x|0) || map._y.includes(y|0). Это не то, что правильнее, просто длинный вариант не понравился, подумал, как бы можно было сократить)