Як я можу виявити напрямок зіткнень прямокутних об'єктів 2D?


11

Після цього питання мені потрібна ще допомога.

Як я можу дізнатись, з якої сторони прямокутника виникло зіткнення та реагувати відповідно?

прямокутники стикаються з усіх боків

Сині стрілки - це стежки, якими слідкували б деякі кругові об’єкти, якщо до та після зіткнення з полем.

Як я можу це обчислити?

Відповіді:


8

Оскільки це засноване на вашому іншому запитанні, я дам рішення, коли прямокутник вирівняний по осі.

Спочатку ви збираєте прямокутник поточного об'єкта з такими значеннями:

int boxLeft = box.X;
int boxRight = boxLeft + box.Width;
int boxTop = box.Y;
int boxBottom = boxTop + box.Height;

Далі ви повинні мати положення старого об'єкта (яке ви можете зберегти на кожному об'єкті або просто передати функції) для створення прямокутника старого об'єкта (коли він не стикався):

int oldBoxLeft = box.OldX;
int oldBoxRight = oldBoxLeft + box.Width;
int oldBoxTop = box.OldY;
int oldBoxBottom = oldBoxTop + box.Height;

Тепер, щоб знати, звідки відбулося зіткнення, ви повинні знайти сторону, де старе положення не було в зоні зіткнення та де його нове положення. Тому що, коли ви думаєте про це, саме це відбувається при зіткненні: сторона, яка не стикалася, входить в інший прямокутник.

Ось як ви могли це зробити (ці функції припускають зіткнення. Їх не слід викликати, якщо немає зіткнення):

 bool collidedFromLeft(Object otherObj)
{
    return oldBoxRight < otherObj.Left && // was not colliding
           boxRight >= otherObj.Left;
}

Змийте і повторіть.

bool collidedFromRight(Object otherObj)
{
    return oldBoxLeft >= otherObj.Right && // was not colliding
           boxLeft < otherObj.Right;
}

bool collidedFromTop(Object otherObj)
{
    return oldBoxBottom < otherObj.Top && // was not colliding
           boxBottom >= otherObj.Top;
}

bool collidedFromBottom(Object otherObj)
{
    return oldBoxTop >= otherObj.Bottom && // was not colliding
           boxTop < otherObj.Bottom;
}

Тепер для фактичного використання з відповіддю на інше запитання:

if (collidedFromTop(otherObj) || collidedFromBottom(otherObj))
    obj.Velocity.Y = -obj.Velocity.Y;
if (collidedFromLeft(otherObj) || collidedFromRight(otherObj))
    obj.Velocity.X = -obj.Velocity.X;

Знову ж таки, це може бути не найкращим рішенням, але саме так я зазвичай іду для виявлення зіткнень.


Ще раз ви мали рацію! ; D Дякую ... (наступного разу надсилай мені ще листівки твоєї дошки ... ^ ___ ^)
NemoStein

Ах, на жаль, я не знав, для чого я міг би його використати .. можливо, наступного разу!
Джессі Емонд

7

Оскільки питання частково ідентичне цьому питанню , я повторно використаю деякі частини своєї відповіді, щоб спробувати відповісти на ваше запитання.


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

Отже, ми розглянемо поле з обмеженням по осі (або орієнтоване обмежене поле ) та рухоме ціле .

  • Рамка для
    обмеження складається з 4 сторін, і кожну з них ми визначимо як: Side1 = [x1, y1, x2, y2] (дві точки [x1, y1] і [x2, y2])

  • Рухома Сутність визначається як вектор швидкості (позиція + швидкість):
    позиція [posX, posY] і швидкість [speedX, speedY] .


Ви можете визначити, на яку сторону AABB / OBB потрапив вектор, використовуючи наступний метод:

  • 1 / Знайдіть точки перетину між нескінченними лініями, що проходять через чотири сторони AABB, і нескінченною лінією, що проходить через положення сутності (перед зіткненням), які використовують вектор швидкості сутності як нахил. (Ви можете знайти точку зіткнення або невизначене число, яке відповідає паралелям або лініям, що перетинаються)

  • 2 / Після того, як ви дізнаєтесь точки перетину (якщо вони існують), ви можете шукати ті, що знаходяться в межах сегмента.

  • 3 / Нарешті, якщо в списку ще є кілька точок (вектор швидкості може проходити через декілька сторін), можна шукати найближчу точку від походження сутності, використовуючи величини вектора від перетину до походження сутності.

Потім можна визначити кут зіткнення за допомогою простого крапкового добутку.

  • 4 / Знайдіть кут між зіткненнями, використовуючи крапковий добуток сутності (можливо, куля?) З вектором попадання.

----------

Детальніше:

  • 1 / Знайдіть перехрестя

    • a / Визначте нескінченні лінії (Ax + Bx = D), використовуючи їх параметричні форми (P (t) = Po + tD).

      Походження точки: Po = [posX, posY]
      Вектор напрямку: D = [speedX, speedY]

      A = Dy = швидкістьY
      B = -Dx = -speedX
      D = (Po.x * Dy) - (Po.y * Dx) = (posX speedY) - (posY speedX)

      Ax + By = D <====> (speedY x) + (-speedX y) = (posX speedY) - (posY speedX)

      Я використовував значення точки сутності для ілюстрації для методу, але це точно той же метод для визначення 4-х бічних нескінченних ліній обмежувального поля (Використовуйте Po = [x1, y1] і D = [x2-x1; y2-y1] замість цього).

    • b / Далі, щоб знайти перетин двох нескінченних прямих, ми можемо вирішити таку систему:

      A1x + B1x = D1 <== Лінія, що проходить через точку сутності з вектором швидкості у вигляді похилого.
      A2x + B2x = D2 <== Одна з ліній, що проходять через сторони AABB.

      який дає такі координати для перехоплення:

      Перехоплення x = (( B 2 * D 1) - ( B 1 * D 2)) / (( A 1 * B 2) - ( A 2 * B 1))
      Перехоплення y = (( A 1 * D 2) - ( A 2 * D 1)) / (( A 1 * B 2) - ( A 2 * B 1))

      Якщо знаменник ((A1 * B2) - (A2 * B1)) дорівнює нулю, то обидві лінії є паралельними або перекриваються, інакше слід знайти перетин.

  • 2 / Тест на межі сегмента. Оскільки це легко перевірити, немає потреби в більш детальних подробицях.

  • 3 / Пошук найближчої точки. Якщо в списку ще є кілька точок, ми можемо знайти, яка сторона є найближчою до точки початку сутності.

    • а / Визначте вектор, що йде від точки перетину до точки початку сутності

      V = Po - Int = [Po.x - Int.x; Po.y - Int.y]

    • б / Обчисліть векторну величину

      || V || = sqrt (V.x² + V.y²)

    • в / знайти найменшого.
  • 4 / Тепер, коли ви знаєте, в яку сторону буде потрапляти, ви можете визначити кут за допомогою крапкового добутку.

    • а / Нехай S = [x2-x1; y2-y1] - бічний вектор, який буде потрапляти, і E = [speedX; speedY] - вектор швидкості сутності.

      Використовуючи правило векторного крапкового продукту, ми це знаємо

      S · E = Sx Ex + Sy Ey
      і
      S · E = || S || || E || cos θ

      Тож ми можемо визначити θ, трохи обробивши це рівняння ...

      cos θ = (S · E) / (|| S || || E ||)

      θ = acos ((S · E) / (|| S || || E ||))

      з

      S · E = Sx * Ex + Sy * Ey
      || S || = sqrt (Sx² + Sy²)
      || E || = sqrt (Ex² + Ey²)


Примітка. Як я вже говорив в іншій темі питань, це, мабуть, не найефективніший і не найпростіший спосіб зробити це, це лише те, що приходить на думку, і якась частина математики може, можливо, допомогти.

Я не перевіряв конкретним прикладом OBB (я робив це з AABB), але він також повинен працювати.


6

Простий спосіб - вирішити зіткнення, а потім по черзі перекласти поле зіткнення рухомого об’єкта на один піксель у кожному напрямку та побачити, які з них призводять до зіткнення.

Якщо ви хочете зробити це "належним чином" та з обертовими фігурами зіткнення або довільними багатокутниками, пропоную прочитати теорему розділення осі. Наприклад, програмне забезпечення Metanet (люди, які створили гру N) має, наприклад, дивовижну статтю про SAT . Вони також обговорюють фізику, що займається.


2

Одним із способів було б обертати світ навколо прямокутника. "Світ" у цьому випадку - це лише ті предмети, які вам цікаві: прямокутник і кулька. Ви обертаєте прямокутник навколо його центру, поки його межі не вирівняються з осями x- / y, потім ви обертаєте кулю на стільки ж.

Тут важливим моментом є те, що ви обертаєте кульку навколо центру прямокутника, а не його власного.

Тоді ви можете легко перевірити на зіткнення так само, як і з будь-яким іншим прямокутником, що не повертається.


Іншим варіантом є трактування прямокутника як чотирьох чітких відрізків ліній та тест на зіткнення з кожним із них окремо. Це дозволяє протестувати на зіткнення і з’ясувати, в яку сторону зіткнувся одночасно.


1

Я використовував фіксовані кути у своїх розрахунках, але це має вам допомогти

void Bullet::Ricochet(C_Rect *r)
{
    C_Line Line;
    //the next two lines are because I detected 
    // a collision in my main loop so I need to take a step back.

    x = x + ceil(speed * ((double)fcos(itofix(angle)) / 65536));
    y = y + ceil(speed * ((double)fsin(itofix(angle)) / 65536));
    C_Point Prev(x,y);

    //the following checks our position to all the lines will give us
    // an answer which line we will hit due to no lines
    // with angles > 90 lines of a rect always shield the other lines.

    Line = r->Get_Closest_Line(Prev);    
    int langle = 0;
    if(!Line.Is_Horizontal())   //we need to rotate the line to a horizontal position
    {
        langle = Line.Get_Point1().Find_Fixed_Angle(Line.Get_Point2());
        angle = angle - langle;  //to give us the new angle of approach
    }
    //at this point the line is horizontal and the bullet is ready to be fixed.
    angle = 256 - angle;
    angle += langle;
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.