Урок по проверке столкновений на чистом JS

PHP/JS+HTML+CSS/Python/др.

Сообщение Урок по проверке столкновений на чистом JS
» 10 июн 2019, 14:32

Приветствую уважаемых программистов. В этом уроке мы рассмотрим алгоритм обработки столкновений на чистом JavaScript. Хоть алгоритм написан на JS, его можно реализовать на любом ЯП, так что полезно будет всем.

Как-то раз я решил сделать собственный игровой движок на JS. Сначала всё шло хорошо, но в один момент я понял что нужно сделать нормальную систему столкновений. Я начал гуглить и на своё удивление внятных ответов не нашёл.
Спрашивал на нескольких форумах (и в том числе на этом), но безуспешно. Тогда я понял что придется делать алгоритм самому...

Это было сложно, но я справился. И теперь хочу поделиться своими наработками, дабы вы не искали неделями решение.
Сразу скажу: этот алгоритм не идеален и я его ещё не оптимизировал, я уверен есть и лучше. Ну что же, не буду затягивать, НАЧНЁМ!


Часть 1

Пересечение прямых


Нам понадобится 2 файла:
index.html
script.js

Для начала сделаем стандартную HTML разметку и подключим скрипт.
Код: Выделить всё
<!DOCTYPE html>
<html>
<head>
   <meta charset="UTF-8"/>
   <title>Index</title>
   <script src="script.js"></script>
</head>
<body>
   <canvas id="cvs" style="border: 1px solid"></canvas>
   <h1 id="text">False</h1>
</body>
</html>


Все последующие действия мы будем выполнять в файле script.js

Сначала нам нужно написать код для создания и отрисовки линий

Код: Выделить всё

//Слушатель события загрузки, чтобы скрипт выполнялся после загрузки всех DOM-элементов

window.addEventListener('load', function () {
   
   //создание холста
   
   var cvs = document.getElementById('cvs');
   
   //контекст холста (инструменты для отрисовки)
   
   var ctx = cvs.getContext('2d');
   
   //размер холста
   
   cvs.width = 300;
   cvs.height = 300;
   
   //миссивы хранящие объекты
   
   var lines = [];
   var rects = [];
   
   //вектор
   
   function v2 (x, y) {
      var v = {};
      v.x = x;
      v.y = y;
      return v;
   }
   //создание объекта линии
   
   function createLine (x1, y1, x2, y2, color) {
      
      var o = {};
      o.x1 = x1;
      o.y1 = y1;
      o.x2 = x2;
      o.y2 = y2;
      o.color = color;
      
      lines.push(o);
      return o;
      
   }
   
   //игровой цикл (рендеринг и работа с объектами)
   
   function loop () {
      
      //перемещения конца линии за мышкой
      
      line2.x2 = mouse.x;
      line2.y2 = mouse.y;
      
      //очищение холста
      
      ctx.clearRect(0, 0, cvs.width, cvs.height);
      
      //цикл отрисовки линий
      
      for(var i in lines) {
         
         ctx.beginPath();
         ctx.moveTo(lines[i].x1, lines[i].y1);
         ctx.lineTo(lines[i].x2, lines[i].y2);
         
         ctx.strokeStyle = lines[i].color;
         ctx.stroke();
         ctx.closePath();
         
      }
      
      //выводим результат
      
      document.getElementById('text').innerHTML = false;
      
      //повторение цикла
      
      requestAnimationFrame(loop);
   }
   //вектор мышки
   
   var mouse = v2(0, 0);
   
   //слушатель события движения мышки
   
   cvs.addEventListener('mousemove', function (e) {
      mouse.x = e.offsetX;
      mouse.y = e.offsetY;
   });
   
   //создание двух линий
   
   var line1 = createLine(0, 0, 300, 150, 'red');
   var line2 = createLine(0, 300, 150, 150, 'green');
   
   //запуск цикла
   
   loop();
   
});


!!!ВНИМАНИЕ!!! Весь код нужно писать внутри слушателя window.addEventListener(function () { }).

В этом коде описаны две линии, конец одной из которых привязан к мышке. Их пересечение мы и будем проверять.

Просто скопируйте этот код, т.к. этот урок посвящён только столкновениям объектов, а не отрисовке. (Если тема интересная, то я и о ней могу написать).

Алгоритм основан на линейной функции (y = k*x+b)
Линейная функция - это функция графиком которой является прямая.
У линейной функции есть два коэффициента K и B.

Коэффициенты можно получить путём несложных уравнений, где x1 и y1 это позиция первой точки линии, а x2 и y2 - второй точки линии.

k = (y2-y1)/(x2-x1)
b = y1-x1*k


Для того чтобы узнать пресекаются ли отрезки (линии) их нужно описать как прямую (бесконечную) при помощи линейной функции. Дальше при помощи двух уравнений нужно определить координаты точки пересечения прямых.

(b1 и k1 являются коэффициентами линейной функции первой прямой, а b2 и k2 - второй прямой)

x = (b2-b1)/(k1-k2)
y = k1*x+b1


Но есть один нюанс:
Если одна из прямых паралельна оси Y (вертикальная), то коэффициент К будет равен бесконечности.
В этом случае координаты точки пересечения будут следующими:

(если k1 = ∞)

x = x1;
y = k2*x+b2;



(если k2 = ∞)

x = x2;
y = k1*x+b1;


Теперь осталось проверить лежит ли точка пересечения на обоих отрезках, если - ДА, то линии пересекаются.
Для этого нам нужно сравнить длину отрезка с суммой расстояния от точки пересечения до первого конца отрезка и расстояния от точки пересечения до второго конца отрезка. То же сравнение нужно сделать для второго отрезка.
Если в обоих сравнениях длина отрезка больше или равняется сумме расстояний от точки пересечения до концов отрезка, то точка находится на обоих отрезках, и следовательно отрезки ПЕРЕСЕКАЮТСЯ!

Расстояние между точками или длина отрезка измеряется по теореме Пифагора a²+b²=c².

Demo.png


Алгоритм мы поняли, осталось написать код.
Сначала мы создадим метод для получения коэффициентов линейной функции, в который мы будем передавать объект линии.

Код: Выделить всё
   function getLineFunc (l) {
      
      var x1 = l.x1;
      var x2 = l.x2;
      var y1 = l.y1;
      var y2 = l.y2;
      
      //определение коэффициентов K и B
      
      var k = (y2-y1)/(x2-x1);
      var b = y1-x1*k;
      
      //Определение вертикальной линии
      
      if(k == 'Infinity' || k == '-Infinity') {
                        //результат в виде ассоциативного массива
         return {
            x: x1,
            type: 'vertical'
         }
      }

      //результат в виде ассоциативного массива
      
      return {
         x: x1,
         y: y1,
         k: k,
         b: b,
         type: 'normal'
      };
      
   }


Обратите внимание, что вертикальной прямой присваивается определённый тип, т.к. расчёты с вертикальными прямыми ведутся особым образом.

Первой проверкой будет проверка параллельности двух отрезков. Это будет отдельная функция, в которой мы просто сравниваем коэффициенты К двух линейных функций.

Код: Выделить всё
   function isParalel (l1, l2) {

      var res = (getLineFunc(l1).k == getLineFunc(l2).k);
      
      return res;
      
   }


Далее мы сделаем метод для определения координат точки пересечения.

Код: Выделить всё
   function getPoint (f1, f2) {
      //если прямые не паралельны оси Y
      if(f1.type == 'normal' && f2.type == 'normal' ) {
         var x = (f2.b-f1.b)/(f1.k-f2.k);
         var y = f1.k*x+f1.b;
      }
        //если первая прямая паралельна оси Y
      else if(f1.type == 'vertical') {
         var x = f1.x;
         var y = f2.k*x+f2.b;
      }
        //если вторая прямая паралельна оси Y
      else if(f2.type == 'vertical') {
         var x = f2.x;
         var y = f1.k*x+f1.b;
      }
      return v2(x, y);
      
   }


Следующей функцией будет определение расстояния между двумя точками. Функция принимает координати точек в виде двух векторов. (Расчёт по теореме Пифагора)

Код: Выделить всё
   function getDistanse (v1, v2) {
      
      return Math.sqrt(Math.abs((v2.x-v1.x)*(v2.x-v1.x)+(v2.y-v1.y)*(v2.y-v1.y)));
      
   }


Теперь мы сделаем функцию, которая проверяет лежит ли точка на двух отрезках. Принимает 2 линии в виде объектов и точку в виде вектора.

Код: Выделить всё
   function pointOnLine (l1, l2, p) {

      //первый отрезок
      var a = getDistanse(v2(l1.x1, l1.y1), v2(l1.x2, l1.y2));
      var b = getDistanse(p, v2(l1.x1, l1.y1));
      var c = getDistanse(p, v2(l1.x2, l1.y2));
               
                //второй отрезок
      var d = getDistanse(v2(l2.x1, l2.y1), v2(l2.x2, l2.y2));
      var e = getDistanse(p, v2(l2.x1, l2.y1));
      var f = getDistanse(p, v2(l2.x2, l2.y2));
      
      //погрешность 0.1 пикселя
      
      var fix = 0.1;
      
      var res = (a >= b+c-fix && d >= e+f-fix);
      return res;
      
   }


Обратите внимание на погрешность в 0.1 пикселя, без неё могут возникнуть ошибки в вычислениях.

Осталось объединить всё воедино. Следующая функция проверяет прямые на параллельность, потом если они не параллельны определяет точку пересечения и затем проверяет находится ли она на отрезках.

Код: Выделить всё
   function lineCollision(l1, l2) {
      
      if(isParalel(l1, l2))
         return false;
      
      var f1 = getLineFunc(l1);
      var f2 = getLineFunc(l2);
      var p = getPoint(f1, f2);
      
      return pointOnLine(l1, l2, p);
      
   }


Осталось вывести результат. В строчке кода где указано:


document.getElementById('text').innerHTML = false;


Вместо false вставляем lineCollision(line1, line2)

ВСЁ! Первая часть готова. Мы сделали проверку пересечения отрезков




Если что-то не работает или вы не разобрались, вот исходник:

Lines.zip
(2.4 КБ) Скачиваний: 3
Сколько языков программирования ты знаешь — столько раз ты ПРОГРАММИСТ!

 Мои проекты
Rect JS Engine: viewtopic.php?f=116&t=14949
INI Editor Pro: viewtopic.php?f=14&t=13354
Симулятор Бомжа: viewtopic.php?f=116&t=13457
Mineсraft 2D JS: viewtopic.php?f=116&t=13642
Змейка на JS: viewtopic.php?f=116&t=13990
Project Sinus JS: viewtopic.php?f=116&t=13837

Black Square

Моя Музыка
Аватара пользователя

Участник
Сообщений: 188
Я тут с 03 фев 2017
Откуда: Киев
Двиг: C2, СC, JS
Лицензия: Business
Skype: sviatkoslav@gmail.co
Репутация 10 [ ? ]

Сообщение Урок по проверке столкновений на чистом JS
» 10 июн 2019, 14:34

Делать проверку пересечения линий мы уже научились, теперь пора применить эти алгоритмы для более сложной задачи, а именно столкновения многоугольников.

Часть 2


Для лучшего понимания в уроке вместо сложных фигур мы используем квадраты.

Давайте сделаем всё для отрисовки и работы с прямоугольниками.

Код: Выделить всё
window.addEventListener('load', function () {

   //создание холста

   var cvs = document.getElementById('cvs');

   //контекст холста (инструменты для отрисовки)

   var ctx = cvs.getContext('2d');

   //размер холста

   cvs.width = 300;
   cvs.height = 300;

   //миссивы хранящие объекты

   var lines = [];
   var rects = [];

   //вектор

   function v2 (x, y) {
      var v = {};
      v.x = x;
      v.y = y;
      return v;
   }

   //создание объекта линии

   function createLine (x1, y1, x2, y2, color) {
      
      var o = {};
      o.x1 = x1;
      o.y1 = y1;
      o.x2 = x2;
      o.y2 = y2;
      o.color = color;
      
      lines.push(o);
      return o;
      
   }

   //создание объекта прямоугольника

   function createRect (x, y, width, height, angle, color) {
      
      var o = {};
      o.x = x;
      o.y = y;
      o.width = width;
      o.height = height;
      o.angle = angle;
      o.color = color;
      o.center = v2(width/2, height/2);
      
      rects.push(o);
      return o;
      
   }
   
   //игровой цикл (рендеринг и работа с объектами)

   function loop () {

      //перемещение прямоугольника за мышькой

      rect2.x = mouse.x-rect2.width/2;
      rect2.y = mouse.y-rect2.height/2;

      //вращение прямоугольника

      rect2.angle ++;
      
      //очищение холста

      ctx.clearRect(0, 0, cvs.width, cvs.height);

      //цикл отрисовки линий

      for(var i in lines) {
         
         ctx.beginPath();
         ctx.moveTo(lines[i].x1, lines[i].y1);
         ctx.lineTo(lines[i].x2, lines[i].y2);
         
         ctx.strokeStyle = lines[i].color;
         ctx.stroke();
         ctx.closePath();
         
      }

      //цикл отрисовки прямоугольников

      for(var i in rects) {
         
         ctx.save();
         
         var d = v2(rects[i].x+rects[i].width/2, rects[i].y+rects[i].height/2);
         
         ctx.translate(d.x, d.y);
         ctx.rotate(rects[i].angle*Math.PI/180);
         ctx.translate(-d.x, -d.y);
         ctx.fillStyle = rects[i].color;
         ctx.fillRect(rects[i].x, rects[i].y, rects[i].width, rects[i].height);
         ctx.restore();
         
      }

      //выводим результат

      document.getElementById('text').innerHTML = false;
      //повторение цикла
      requestAnimationFrame(loop);
   }

   //вектор мышки

   var mouse = v2(0, 0);

   //слушатель события движения мышки

   cvs.addEventListener('mousemove', function (e) {
      mouse.x = e.offsetX;
      mouse.y = e.offsetY;
   });

   //создание двух прямоугольнков

   var rect1 = createRect(100, 100, 100, 100, 23, 'red');
   var rect2 = createRect(20, 20, 30, 30, 0, 'green');
   
   //запуск цикла

   loop();
   
   
   /* ДВИЖОК КОЛЛИЗИЙ */
   
   //создание вспомогательных линий

   function line (x1, y1, x2, y2) {
      return {
         x1: x1,
         y1: y1,
         x2: x2,
         y2: y2,
      }
   }

   //получение коэффициентов линейной функции

   function getLineFunc (l) {
      
      var x1 = l.x1;
      var x2 = l.x2;
      var y1 = l.y1;
      var y2 = l.y2;
      
      //определение коэффициэнтов K и B

      var k = (y2-y1)/(x2-x1);
      var b = y1-x1*k;
      
      //Определение вертикальной линии

      if(k == 'Infinity' || k == '-Infinity') {
         return {
            x: x1,
            type: 'vertical'
         }
      }

      //результат в виде ассоциативного массива

      return {
         x: x1,
         y: y1,
         k: k,
         b: b,
         type: 'normal'
      };
      
   }
   
   //проверка прямых на паралельность при помощи сравнивания коэффициэнта K

   function isParalel (l1, l2) {

      var res = (getLineFunc(l1).k == getLineFunc(l2).k);
      
      return res;
      
   }
   
   //получение точки пересечения прямых

   function getPoint (f1, f2) {
      
      if(f1.type == 'normal' && f2.type == 'normal' ) {
         var x = (f2.b-f1.b)/(f1.k-f2.k);
         var y = f1.k*x+f1.b;
      }
      else if(f1.type == 'vertical') {
         var x = f1.x;
         var y = f2.k*x+f2.b;
      }
      else if(f2.type == 'vertical') {
         var x = f2.x;
         var y = f1.k*x+f1.b;
      }
      return v2(x, y);
      
   }
   
   //получение расстояния между точками

   function getDistanse (v1, v2) {
      
      return Math.sqrt(Math.abs((v2.x-v1.x)*(v2.x-v1.x)+(v2.y-v1.y)*(v2.y-v1.y)));
      
   }
   
   //проверяем лежит ли прямая на двух линиях

   function pointOnLine (l1, l2, p) {
      
      var a = getDistanse(v2(l1.x1, l1.y1), v2(l1.x2, l1.y2));
      var b = getDistanse(p, v2(l1.x1, l1.y1));
      var c = getDistanse(p, v2(l1.x2, l1.y2));
      var d = getDistanse(v2(l2.x1, l2.y1), v2(l2.x2, l2.y2));
      var e = getDistanse(p, v2(l2.x1, l2.y1));
      var f = getDistanse(p, v2(l2.x2, l2.y2));
      
      //погрешность 0.1 пикселя

      var fix = 0.1;
      
      var res = (a >= b+c-fix && d >= e+f-fix);
      return res;
      
   }
   
   //проверка пересечения двух линий

   function lineCollision(l1, l2) {
      
      if(isParalel(l1, l2))
         return false;
      
      var f1 = getLineFunc(l1);
      var f2 = getLineFunc(l2);
      var p = getPoint(f1, f2);
      
      return pointOnLine(l1, l2, p);
      
   }
});


Просто скопируйте код в файл script.js и если вы не прочитали первую часть, прочитайте сперва ёё.

Как всегда сначала алгоритм.
Итак, первая задача, которая перед нами стоит, - это построить модель из полигонов (сторон) и углов, т.е. меш.

Координаты всех точек (углов) должны вводиться при создании меша, а остальное сделает алгоритм. Перед алгоритмом стоит 2 задачи:

* рассчитать координаты точек с учётом угла наклона объекта и центра вращения
* провести вспомогательные линии между точками по периметру объекта

Координаты точек получаются при помощи следующих формул, где a1 - угол наклона объекта в градусах, x1 и y1 - координаты точки относительно левого верхнего угла объекта, x0 и y0 - координаты центра вращения относительно левого верхнего угла объекта, а x2 и y2 - координаты точки относительно левого верхнего угла объекта с учётом наклона.

a2 = (a1+180)*π/180

x2 = (x0-x1) * cos(a2) - (y0-y1) * sin(a2) + x0
y2 = (x0-x1) * sin(a2) + (y0-y1) * cos(a2) + y0


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

Приступим к коду.

Код: Выделить всё
   function mesh (pos, p, a = 0, c) {
      
      var o = {};
      
      var x = pos.x;
      var y = pos.y;
      o.lines = [];
      o.points = p;
      o._points = [];
      o.c = c;
      o.x = x;
      o.y = y;
      o.a = 0;
      //расчёт расположения углов при вращении
      for(var i in o.points) {
         var px = o.points[i].x;
         var py = o.points[i].y;
         
         var a2 = (a+180)*Math.PI/180;
      
         x2 = (c.x-px) * Math.cos(a2) - (c.y-py) * Math.sin(a2) + c.x;
         y2 = (c.x-px) * Math.sin(a2) + (c.y-py) * Math.cos(a2) + c.y;
         
         o._points[i] = v2(x2, y2);
      }
      
      var _p = o._points;
      //создание полигонов (линий) между углами
      for(var i = 0; i < _p.length; i ++) {   
         
         var point1 = _p[i];
         var point2 = _p[i+1];
         if(i == _p.length-1) point2 = _p[0];
         o.lines.push(line(point1.x+x, point1.y+y, point2.x+x, point2.y+y));
         
      }
      
      return o;
      
   }


Эта функция принимает 4 параметра:
* позиция левого верхнего угла объекта
* массив точек в виде векторов
* наклон объекта
* координаты центра вращения относительно левого верхнего угла объекта (в виде вектора)

Теперь в цикл loop, после строчки

rect2.angle ++;

вставьте код создания мешей для обоих прямоугольников:

var mesh1 = mesh(v2(rect1.x, rect1.y), [v2(0, 0), v2(rect1.width, 0), v2(rect1.width, rect1.height), v2(0, rect1.height)], rect1.angle, rect1.center);

var mesh2 = mesh(v2(rect2.x, rect2.y), [v2(0, 0), v2(rect2.width, 0), v2(rect2.width, rect2.height), v2(0, rect2.height)], rect2.angle, rect2.center);



Теперь приступим к самому алгоритму проверки столкновений.

Для проверки столкновения объектов необходимо проверить пересечение их полигонов, а если они не пересекаются проверить находится ли хоть одна точка объекта внутри другого. Если хоть один из пунктов соблюдается, то объекты соприкасаются.

Итак, наш алгоритм будет в два этапа:
* проверка пересечений полигонов
* проверка наличия хотя бы одной из точек объекта внутри другого

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

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

Demo2.png


Теперь нам нужно для каждой точки (угла) одного объекта создать 4 вспомогательные линии от самой точки до следующих координат:

1) x = x левой крайней точки другого объекта * -2, y = y начальной точки
2) x = x начальной точки, y = y верхней крайней точки другого объекта * -2
3) x = x правой крайней точки другого объекта * 2, y = y начальной точки
4) x = x начальной точки, y = y нижней крайней точки другого объекта * 2

Затем мы проверяем количество пересечений каждой вспомогательной линии с полигонами другого объекта. Если количество пересечений чётно - точка находится снаружи, а если нечётно - внутри.

Тот же цикл нужно провести и для второго объекта.
Если ни одна точка не находится внутри другого объекта, то мы можем с уверенностью сказать что объекты не соприкасаются.

А если хоть одна точка находится внутри другого объекта, то объекты точно соприкасаются.

Теперь, когда мы понимаем алгоритм, пора приступать к написанию кода.

Сделаем функцию принимающую меш и возвращающую его крайние точки в виде ассоциативного массива.
* x1 - левая крайняя точка
* x2 - правая крайняя точка
* y1 - верхняя крайняя точка
* y2 - нижняя крайняя точка

Код: Выделить всё
   function getExtremePoints (m) {
      
      var o = {};
      o.x1 = m._points[0].x+m.x;
      o.y1 = m._points[0].y+m.y;
      o.x2 = m._points[0].x+m.x;
      o.y2 = m._points[0].y+m.y;
      
      for(var i in m._points) {
         var p = m._points[i];
         if(p.x+m.x < o.x1) o.x1 = p.x+m.x;
         if(p.y+m.x < o.y1) o.y1 = p.y+m.y;
      }
      
      for(var i in m._points) {
         var p = m._points[i];
         if(p.x+m.x > o.x2) o.x2 = p.x+m.x;
         if(p.y+m.x > o.y2) o.y2 = p.y+m.y;
      }
      
      return o;
      
   }


Дальше нужно сделать функцию первой стадии проверки, которая принимает 2 меша и проверяет пересечение их полигонов.

Код: Выделить всё
   function stage1 (m1, m2) {
      for(var i in m1.lines) {         
         for(var j in m2.lines) {
            
            if(lineCollision(m1.lines[i], m2.lines[j]))
               return true;
         }
      }
      
   }


Теперь необходимо сделать функцию второй стадии проверки, которая принимает 2 меша и проверяет наличие точек одного меша внутри другого.

Код: Выделить всё
   function stage2 (m1, m2) {
      
      //получаем крайние точки

      var e1 = getExtremePoints(m1);
      var e2 = getExtremePoints(m2);

      for(var i in m1._points) {
         
         var p = m1._points[i];

         //создаем вспомогательные линии

         var l1 = line(p.x+m1.x, p.y+m1.y, e2.x2+m2.x*2, p.y+m1.y);
         var l2 = line(p.x+m1.x, p.y+m1.y, e2.x1+m2.x*-2, p.y+m1.y);
         var l3 = line(p.x+m1.x, p.y+m1.y, p.x+m1.x, e2.y2+m2.y*2);
         var l4 = line(p.x+m1.x, p.y+m1.y, p.x+m1.x, e2.y1+m2.y*-2);
         
         
         var r = 0;
         var l = 0;
         var d = 0;
         var u = 0;

         //считаем количество пересечений вспомогательной линии с линиями другой маски

         for(var j in m2.lines) {
            var l0 = m2.lines[j];
            if(lineCollision(l1, l0))
               r++
            if(lineCollision(l2, l0))
               l++
            if(lineCollision(l3, l0))
               d++
            if(lineCollision(l4, l0))
               u++
         }

         //прверяем количество пересечений на парность

         if(r % 2 != 0 && l % 2 != 0 && d % 2 != 0 && u % 2 != 0)
            return true;
         
      }
      
      for(var i in m2._points) {

         //создаем вспомогательные линии

         var p = m2._points[i];
         var l1 = line(p.x+m2.x, p.y+m2.y, e1.x2+m1.x*2, p.y+m2.y);
         var l2 = line(p.x+m2.x, p.y+m2.y, e1.x1+m1.x*-2, p.y+m2.y);
         var l3 = line(p.x+m2.x, p.y+m2.y, p.x+m2.x, e1.y2+m1.y*2);
         var l4 = line(p.x+m2.x, p.y+m2.y, p.x+m2.x, e1.y1+m1.y*-2);
         
         var r = 0;
         var l = 0;
         var d = 0;
         var u = 0;

         //считаем количество пересечений вспомогательной линии с линиями другой маски

         for(var j in m1.lines) {
            var l0 = m1.lines[j];
            if(lineCollision(l1, l0))
               r++
            if(lineCollision(l2, l0))
               l++
            if(lineCollision(l3, l0))
               d++
            if(lineCollision(l4, l0))
               u++
         }

         //прверяем количество пересечений на парность

         if(r % 2 != 0 && l % 2 != 0 && d % 2 != 0 && u % 2 != 0)
            return true;
         
      }
      
      return false;
      
   }


Теперь нужно лишь объединить обе стадии в одну функцию

Код: Выделить всё
   function meshCollision (m1, m2) {
      
      if(stage1(m1, m2))
         return true;
      else if(stage2(m1, m2))
         return true;
      else
         return false;
      
   }


Осталось только вывести результат. В строчке кода где указано:

document.getElementById('text').innerHTML = false

Вместо false нужно вставить meshCollision(mesh1, mesh2).

Готово! Поздравляю, вы прошли Урок по проверке столкновений на чистом JavaScript!




Если вы не разобрались или у вас что-то не работает, вот исходник:

Rects.zip
(3.61 КБ) Скачиваний: 2
Сколько языков программирования ты знаешь — столько раз ты ПРОГРАММИСТ!

 Мои проекты
Rect JS Engine: viewtopic.php?f=116&t=14949
INI Editor Pro: viewtopic.php?f=14&t=13354
Симулятор Бомжа: viewtopic.php?f=116&t=13457
Mineсraft 2D JS: viewtopic.php?f=116&t=13642
Змейка на JS: viewtopic.php?f=116&t=13990
Project Sinus JS: viewtopic.php?f=116&t=13837

Black Square

Моя Музыка
Аватара пользователя

Участник
Сообщений: 188
Я тут с 03 фев 2017
Откуда: Киев
Двиг: C2, СC, JS
Лицензия: Business
Skype: sviatkoslav@gmail.co
Репутация 10 [ ? ]

Сообщение Урок по проверке столкновений на чистом JS
» 13 июн 2019, 20:50

В уроках я намеренно не использовал классы и конструкторы объектов JavaScript, дабы не запутывать новичков, ведь урок и так получился довольно сложным.

Прошу оставлять комментарии под уроком, т.к. я вложил много времени и усилий в этот урок и для меня отзывы очень важны.

А если этот урок кому-то помог, то поблагодарите ПЛЮСИКОМ +
Сколько языков программирования ты знаешь — столько раз ты ПРОГРАММИСТ!

 Мои проекты
Rect JS Engine: viewtopic.php?f=116&t=14949
INI Editor Pro: viewtopic.php?f=14&t=13354
Симулятор Бомжа: viewtopic.php?f=116&t=13457
Mineсraft 2D JS: viewtopic.php?f=116&t=13642
Змейка на JS: viewtopic.php?f=116&t=13990
Project Sinus JS: viewtopic.php?f=116&t=13837

Black Square

Моя Музыка
Аватара пользователя

Участник
Сообщений: 188
Я тут с 03 фев 2017
Откуда: Киев
Двиг: C2, СC, JS
Лицензия: Business
Skype: sviatkoslav@gmail.co
Репутация 10 [ ? ]



Вернуться в Web

Сейчас эту тему просматривают

Зарегистрированные пользователи: нет зарегистрированных пользователей

Наверх