Вітаю! Ви пройшли через значну кількість коду з виявлення колізій. Але ці приклади призначені лише для простої демонстрації того, як працюють алгоритми. Об’єднання їх у більші проекти, ймовірно, означає переведення вашого коду на об’єктно-орієнтований підхід. (Чудовий вступ до об’єктно-орієнтованого програмування можна подивитись у книзі Деніела Шиффмана Природа коду).
Навіщо? Скажімо, у нас є круг та купа прямокутників, як у інтерактивному прикладі вище. Ми могли б зберігати окремі позиції, розміри та стан зіткнення для кожного з них, але це швидко призведе до безладдя. Натомість класи Circle і Rectangle нададуть нашому коду набагато більше зручності та гнучкості.
Почнімо зі створення класу Circle:
class Circle {
constructor(x, y, r) {
this.x = x; // x-координата
this.y = y; // y-координата
this.r = r; // радіус
}
update(x, y) {
this.x = x;
this.y = y;
}
// малювання круга
display() {
fill(0, 150);
noStroke();
ellipse(this.x, this.y, this.r * 2, this.r * 2);
}
}
Це було досить просто. Ми також можемо створити базовий клас Rectangle:
class Rectangle {
constructor(x, y, w, h) {
// xy-координати лівого кута
this.x = x;
this.y = y;
this.w = w; // ширина,
this.h = h; // висота,
this.isHit = false; // чи є зіткнення
}
// перевірка на зіткнення з кругом
// використовуючи функцію isCircleWithRectCollides, яку ми зробили на початку
checkCollisionWithCircle(c) {
this.isHit = isCircleWithRectCollides(c.x, c.y, c.r, this.x, this.y, this.w, this.h);
}
// малювання прямокутника
display() {
// при зіткненні змінюємо колір
if (this.isHit) {
fill(255, 150, 0);
} else {
fill(0, 150, 255);
}
noStroke();
rect(this.x, this.y, this.w, this.h);
}
}
Зауважте, що у класі Rectangle у нас є змінна під назвою isHit. У ній ми будемо зберігати поточний стан обʼєкта стосовно того, чи він має колізію з кругом, і відповідно змінювати колір його заливки. За замовчуванням значення встановлено на false.
У нас буде лише один обʼєкт типу Circle, але ми створимо масив об’єктів Rectangle. Ось так виглядатиме функція draw(), яка малюватиме наші фігури і оновлюватиме положення круга:
function draw() {
background(255);
// перебір усіх прямокутників
for (const rectangle of rects) {
rectangle.checkCollisionWithCircle(circle); // перевірка на колізію
rectangle.display(); // малювання прямокутника
}
// оновлення положення круга положенням курсора і його зображення
circle.update(mouseX, mouseY);
circle.display();
}
Отже, як ми перевіримо, чи коло прямокутник зіткнувя з кругом? Давайте створимо у класі Rectangle метод (внутрішню функцію) під назвою checkCollisionWithCircle(). Ми передамо у цей метод об’єкт Circle як аргумент, а потім виконаємо базовий тест на перевірку зіткнення круга з прямокутником.
function checkCollision(c) {
this.isHit = isCircleWithRectCollides(c.x,c.y,c.r, x,y,w,h);
}
Результат функції isCircleWithRectCollides() встановлює значення змінної isHit на true або false, що, у свою чергу, змінює колір заливки. Тепер ми просто додаємо виклик цьої перевірки у функцію draw() loop:
// перебір усіх прямокутників
for (const rectangle of rects) {
rectangle.checkCollisionWithCircle(circle); // перевірка на колізію
rectangle.display(); // малювання прямокутника
}
Дуже добре! Ось повний код:
// змінна для єдиного обʼєкту круга, що контролюватиметься курсором
let circle;
// масив прямокутників
let rects = new Array(8);
function setup() {
createCanvas(window.innerWidth, window.innerHeight);
// створення круга з радіусом у 30 пікселів
circle = new Circle(0, 0, 30);
// генерація прямокутників у випадкових місцях, але з привʼязкою до сітки у 50 пікселів
for (let i = 0; i < rects.length; i++) {
const x = round(random(50, width - 50) / 50) * 50;
const y = round(random(50, height - 50) / 50) * 50;
rects[i] = new Rectangle(x, y, 50, 50);
}
}
function draw() {
background(255);
// перебір усіх прямокутників
for (const rectangle of rects) {
rectangle.checkCollisionWithCircle(circle); // перевірка на колізію
rectangle.display(); // малювання прямокутника
}
// оновлення положення круга положенням курсора і його зображення
circle.update(mouseX, mouseY);
circle.display();
}
class Circle {
constructor(x, y, r) {
this.x = x; // x-координата
this.y = y; // y-координата
this.r = r; // радіус
}
update(x, y) {
this.x = x;
this.y = y;
}
// малювання круга
display() {
fill(0, 150);
noStroke();
ellipse(this.x, this.y, this.r * 2, this.r * 2);
}
}
class Rectangle {
constructor(x, y, w, h) {
this.x = x; // x-координата лівого кута
this.y = y; // y-координата лівого кута
this.w = w; // ширина
this.h = h; // висота
this.isHit = false; // чи є зіткнення
}
// перевірка на зіткнення з кругом
// використовуючи функцію isCircleWithRectCollides, яку ми зробили на початку
checkCollisionWithCircle(circleObj) {
this.isHit = isCircleWithRectCollides(circleObj.x, circleObj.y, circleObj.r, this.x, this.y, this.w, this.h);
}
// малювання прямокутника
display() {
// при зіткненні змінюємо колір
if (this.isHit) {
fill(255, 150, 0);
} else {
fill(0, 150, 255);
}
noStroke();
rect(this.x, this.y, this.w, this.h);
}
}
// перевірка на перетин між кругом і прямокутником
function isCircleWithRectCollides(cx, cy, radius, rx, ry, rw, rh) {
// тестові змінні точки, з якою буде відбуватися перевірка на перетин
let testX = cx;
let testY = cy;
// які координати квадрата знаходяться найближче до круга?
if (cx < rx) testX = rx; // compare to left edge
else if (cx > rx+rw) testX = rx+rw; // right edge
if (cy < ry) testY = ry; // top edge
else if (cy > ry+rh) testY = ry+rh; // bottom edge
// визначення відстані до найближчої точки ребра прямокутника, якщо круг за межами прямокутника
const distX = cx-testX;
const distY = cy-testY;
const distance = sqrt((distX*distX) + (distY*distY));
// якщо відстань менша за радіус круга це колізія!
return distance <= radius;
}
Зверніть увагу, що наш код трохи задовгий, бо включає усі потрібні класи, тому фактичний проєкт прикладу у репозиорії розбитий на окремі файли. За потреби ви можете обʼєднати усі допоміжні функції по виявленню колізій, які необхідні для вашого проєкту, в один файл під назвою collision-functions.
Ви можете побачити інший, більш складний приклад об’єктно-орієнтованої колізії у вступі. Він використовує класи для кругів, прямокутників і ліній.
Далі: Робота з перетвореннями матриці