В общем случае эта задача не такая простая. Можно найти библиотеки, обычно называющиеся «физическими движками», симулирующие взаимодействия между физическими объектами в двух или трёх измерениях. В этой главе мы поступим проще, так как нам нужно обрабатывать столкновения только прямоугольных объектов.
Перед тем, как сдвинуть игрока или блок лавы, мы проверяем, не приведёт ли нас движение внутрь непустой части фона. Если да – мы отменяем движение. Реакция на это будет зависеть от типа актёра – игрок останавливается, лава отскакивает.
Подход требует использования небольших отрезков времени, чтобы объекты останавливались до соприкосновения. Если взять слишком большие отрезки, игрок будет зависать над землёй. Можно было бы использовать более сложный вариант – вычислить место непосредственного соприкосновения и подвинуть актёра туда. Мы поступим проще, и скроем его проблемы, выбрав небольшие временные отрезки.
Метод сообщает, не пересекается ли прямоугольник (заданный позицией и размером) с каким-либо непустым пространством фоновой решётки.
>Level.prototype.obstacleAt = function(pos, size) {
> var xStart = Math.floor(pos.x);
> var xEnd = Math.ceil(pos.x + size.x);
> var yStart = Math.floor(pos.y);
> var yEnd = Math.ceil(pos.y + size.y);
> if (xStart < 0 || xEnd > this.width || yStart < 0)
> return "wall";
> if (yEnd > this.height)
> return "lava";
> for (var y = yStart; y < yEnd; y++) {
> for (var x = xStart; x < xEnd; x++) {
> var fieldType = this.grid[y][x];
> if (fieldType) return fieldType;
> }
> }
>};
Метод вычисляет занимаемые телом ячейки решётки, применяя >Math.floor
и >Math.ceil
на координатах тела. Помните, что размеры ячеек – 1×1 единиц. Округляя границы тела вверх и вниз, мы получаем промежуток из ячеек фона, которых касается тело.
Поиск столкновений на решётке
Если тело высовывается из уровня, мы всегда возвращаем >“wall”
для двух сторон и верха и >“lava”
для низа. Это обеспечит гибель игрока при выходе за пределы уровня. Когда тело внутри решётки, мы в цикле проходим блок квадратов решётки, найденный округлением координат, и возвращаем содержимое первого непустого квадратика.
Столкновения игрока с другими актёрами (монеты, движущаяся лава) обрабатываются после сдвига игрока. Когда движение приводит его к другому актёру, срабатывает соответствующий эффект (сбор монет или гибель).
Этот метод сканирует массив актёров, в поисках того, который накладывается на заданный аргумент:
>Level.prototype.actorAt = function(actor) {
> for (var i = 0; i < this.actors.length; i++) {