Универсальная функция JS по определению хитбоксов у HTML блоков

от автора

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

В HTML страницах изначально нет понятие хитбокса у блоков, поэтому в этом посте мы сами получим и обработаем их с помощью JS.

Для чего нам нужны хитбоксы?

Чаще всего для каких-нибудь игрушек или же симуляция, где важно определить касания блоков.

1 пример: block1 коснулся block2 и произошло какое-нибудь действие.

2 пример: player стоял на block1, как только он отошел с него, сразу же упал

Почему мы не можем просто сравнить координаты?

Мы можем сравнить координаты X=left и Y=top, но наше условие будет выполняться только, если block1 идеально встанет на место block2, что будет выглядеть странно, да и если вы делается какую-нибудь игру/симуляцию, это будет неправильным решением

Универсальность и размер нашей функции

Наша функция JS будет работать со всеми блоками, если мы дадим доступ к ним.

Размер функции будет зависеть от кол-ва видов ваших блоков, но по стандарту она занимает не больше 30 строк кода

Все действия будут проходить через 2 цикла for():

1 цикл: Для определения углов блоков.

2 цикл: Определения касания блоков между собой.

Логика получения координат:

Допустим у нас есть 3 блока разного размера и разных цветов (красный, зеленый, синий) с начальными позициями:

У каждого блока есть координата X и Y, которые находятся в левом углу самого блока:

Но у блоков помимо координат, есть высота и ширина (height, width):

Прибавляя эти значения между собой, мы уже можем получить все нужные нам координаты: x1, x2, y1, y2.

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

Для начала в .html файле напишем базовый код и наш главный блок:

<body>     <main class="mainBox">        </main>          <script src="script.js"></script> <!-- Заранее подключаем скрипт --> </body>

Внутри mainBox засунем наши 3 блока и дадим им классы

<main class="mainBox">     <div class="block red"></div>  <!-- Обращаться ко всем в JS будем через класс -->     <div class="block green"></div>     <div class="block blue"></div> </main>

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

Сразу же даем класс и onclick:

<main class="mainBox">     <div class="block red"></div>     <div class="block green"></div>     <div class="block blue"></div> </main> <button class="btn" onclick="getHitboxTouchResult()">Проверить</button>

Переходим в CSS и даем следующие стили:

body {     margin: 0; /* Чтобы по краям не было отступов */     background-color: rgb(60, 60, 60); /* Серый цвет приятнее глазу :D */ } .mainBox {     position: relative; /* Чтобы блоки могли использовать left, top в род. блоке */ } .block {     position: absolute; /* Возможность использования left, top */ }  /* Размеры и цвета доч. блоко */ .red {     background-color: rgba(255, 0, 0, 0.6);     width: 200px;     height: 200px; } .blue {     background-color: rgb(0, 0, 255, 0.6);     width: 250px;     height: 250px; } .green {     background-color: rgba(0, 255, 0, 0.6);     width: 100px;     height: 100px; }  .btn {     position: absolute; /* Крепим кнопку снизу справа */     right: 0;     bottom: 0;     width: 100px; /* Даем размер */     height: 40px; }

Получаем такой результат:

Все блоки слева сверху, давайте переместим их дав left, top параметры:

.red {     background-color: rgba(255, 0, 0, 0.6);     width: 200px;     height: 200px;     left: 920px;     top: 320px; } .blue {     background-color: rgb(0, 0, 255, 0.6);     width: 250px;     height: 250px;     left: 690px;     top: 190px; } .green {     background-color: rgba(0, 255, 0, 0.6);     width: 100px;     height: 100px;     left: 640px;     top: 420px; }

Теперь они у нас в таком положении по центру страницы:

Переходим в JS и обозначаем нашу главную функцию определения хитбоксов:

function setHitboxesAndCheck() {}

В нее записываем получения всех доч. блоков через класс block:

blocks = document.querySelectorAll(".block")

Все блоки у нас, теперь определим их координаты:

for (var i = 0; i < Object.keys(blocks).length; i++) { // повторяем цикл пока i не достигнет количества всех блоков    }

Определим координаты X:

for (var i = 0; i < Object.keys(blocks).length; i++) {     // параметры offset могут записывать данные блока, в нашем случае мы используем offsetLeft и offsetWidth     // за счет того, что blocks является массивом, мы можем каждому блоку присвоить переменные     blocks[i].x1 = blocks[i].offsetLeft // первая точка X1     blocks[i].x2 = blocks[i].offsetLeft + blocks[i].offsetWidth // вторая точка X2 за счет прибавления ширины  }

Не забываем про Y:

for (var i = 0; i < Object.keys(blocks).length; i++) {     blocks[i].x1 = blocks[i].offsetLeft     blocks[i].x2 = blocks[i].offsetLeft + blocks[i].offsetWidth     blocks[i].y1 = blocks[i].offsetTop     blocks[i].y2 = blocks[i].offsetTop + blocks[i].offsetHeight // тут уже прибавляем высоту }

Координаты наших 4 точек уже готовы, выглядят они так:

Проверяем касания наших точек

Для начала поймем как мы будем это делать.

Представим 2 блока, у которых есть свои точки:

Мы видим, что blocks[0].y2 находится в blocks[1].

Вначале будем проверять координаты X:

if (blocks[1].x1 <= blocks[0].x2) { // Если координата x1 второго блока меньше или равна координате x2 второго блока      if (blocks[0].x2 >= blocks[1].x1) { // Если координата x1 первого блока больше или равна координате x2 второго блока         // В этих условия мы проверяем блоки именно касаются друг друга или просто находятся рядом         console.log(true) // Если условия верны и блоки касаются по горизонтали, то мы выводим true     } }

Подключим циклы для проверок всех блоков:

for (var i = 0; i < Object.keys(blocks).length; i++) {         for (var o = 0; o < Object.keys(blocks).length; o++) { // чтобы проверять блоки между собой             if (i != o) { // даем условия, чтобы блоки не сравнивали свои же координаты                 if (blocks[i].x1 <= blocks[o].x2) { // наши условия                     if (blocks[i].x2 >= blocks[o].x1) { // заменяем цифры на переменные                         console.log(true)                     }                 }             }         }     }

Создаем еще одну функцию к которой подключим кнопку:

function getHitboxTouchResult() { // onclick нашей кнопки     setHitboxesAndCheck() }

Мы видим, что в консоли (F12) выводится true если они касаются по координате X.

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

Для этого дадим еще пару условий по координатам:

if (blocks[i].x1 <= blocks[o].x2) {     if (blocks[i].x2 >= blocks[o].x1) {                if (blocks[i].y1 <= blocks[o].y1) { // тут тоже самое, но в первом условии проверяем левые верхние углы между собой             if (blocks[i].y2 >= blocks[o].y1) {                 console.log(true) // выводим true             }         }            } }

Теперь мы можем полностью определить хитбокс по горизонтали и вертикали.

Чуть изменим нашу функцию, чтобы мы могли использовать ее в сравнениях.

Заменим:

console.log(true)

На:

return true

Также заменим в функции кнопки, простой вызов на:

console.log(setHitboxesAndCheck())

Для использования нескольких видов блоков, нужно будет лишь их указывать в сравнениях.

Весь код:

HTML

<!DOCTYPE html> <html lang="en">  <head>     <meta charset="UTF-8">     <meta http-equiv="X-UA-Compatible" content="IE=edge">     <meta name="viewport" content="width=device-width, initial-scale=1.0">     <title>Hitboxes</title>     <link rel="stylesheet" href="style.css"> </head>  <body>     <main class="mainBox">         <div class="block red"></div>         <div class="block green"></div>         <div class="block blue"></div>     </main>     <button class="btn" onclick="getHitboxTouchResult()">Check</button>      <script src="script.js"></script> </body>  </html>

CSS

body {     margin: 0;     background-color: rgb(60, 60, 60); } .mainBox {     position: relative; } .block {     position: absolute; } .red {     background-color: rgba(255, 0, 0, 0.6);     width: 200px;     height: 200px;     left: 920px;     top: 320px; } .blue {     background-color: rgb(0, 0, 255, 0.6);     width: 250px;     height: 250px;     left: 690px;     top: 190px; } .green {     background-color: rgba(0, 255, 0, 0.6);     width: 100px;     height: 100px;     left: 640px;     top: 420px;     display: none; } .btn {     position: absolute;     right: 0;     bottom: 0;     width: 100px;     height: 40px; }

JS

function setHitboxesAndCheck() {     blocks = document.querySelectorAll(".block")     for (var i = 0; i < Object.keys(blocks).length; i++) {         blocks[i].x1 = blocks[i].offsetLeft         blocks[i].x2 = blocks[i].offsetLeft + blocks[i].offsetWidth         blocks[i].y1 = blocks[i].offsetTop         blocks[i].y2 = blocks[i].offsetTop + blocks[i].offsetHeight     }     for (var i = 0; i < Object.keys(blocks).length; i++) {         for (var o = 0; o < Object.keys(blocks).length; o++) {             if (i != o) {                 if (blocks[i].x1 <= blocks[o].x2) {                     if (blocks[i].x2 >= blocks[o].x1) {                         if(blocks[i].y1 <= blocks[o].y1) {                             if(blocks[i].y2 >= blocks[o].y1) {                                 return true                             }                         }                     }                 }             }         }     } } function getHitboxTouchResult() {     console.log(setHitboxesAndCheck()) }

Спасибо за прочтение данного поста.

Буду благодарен, если посетите мой телеграм канал: https://t.me/blg_projects


ссылка на оригинал статьи https://habr.com/ru/post/690370/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *