Впровадження обгорткового дроту (як мотузка для черв’яків Ninja) у двигуні 2D фізики


34

Нещодавно я спробував фізику мотузки, і виявив, що «стандартне» рішення - виготовлення мотузки з ряду предметів, нанизаних на пружини або стики, - незадовільне. Особливо, коли розмах мотузки має відношення до ігрового процесу. Мені не дуже важливо вміння мотузки загортатись або провисати (це все одно може бути підробленим для візуальних зображень).

Для ігрового процесу важливим є вміння мотузки обертатися навколо навколишнього середовища, а потім згортатися. Він навіть не повинен поводитись як мотузка - це би зробив «дріт», складений з прямих сегментів. Ось ілюстрація:

Мотузка ніндзя, обертаючись навколо перешкод

Це дуже схоже на "Ninja Rope" з гри Worms.

Оскільки я використовую 2D двигун фізики - моє середовище складається з 2D опуклих багатокутників. (Зокрема, я використовую SAT у Farseer.)

Отже, моє запитання таке: як би ви реалізували ефект "обгортання"?

Здається досить очевидним, що провід буде складатися з ряду відрізків ліній, які "розщеплюються" та "з'єднуються". І останнім (активним) відрізком цієї лінії, куди приєднується рухомий об’єкт, буде суглоб фіксованої довжини.

Але для чого математика / алгоритм бере участь у визначенні, коли і де потрібно ділити сегмент активного рядка? А коли його потрібно з'єднати з попереднім сегментом?

(Раніше це питання також задавали питання про те, як це зробити для динамічного середовища. Я вирішив розділити це на інші питання.)

Відповіді:


18

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

Корисна схема ситуації з мотузкою ніндзя

Спосіб виявлення зіткнення полягає в тому, що для кореня поточного сегмента мотузки, O, кінцеве положення мотузки на попередньому кадрі, A, кінцеве положення мотузки на поточному кадрі, B і кожну кутову точку P у полігоналі рівня геометрії, ви обчислюєте (OA x OP), (OP x OB) і (OA x OB), де "x" являє собою взяття координати Z поперечного добутку між двома векторами. Якщо всі три результати мають однаковий знак, негативний чи позитивний, а довжина ОП менша за довжину відрізка мотузки, точка Р знаходиться в межах зони, охопленої гойдалкою, і ви повинні розділити мотузку. Якщо у вас є кілька кутових точок, що стикаються, вам потрібно скористатися першою, яка потрапила в мотузку, тобто ту, де кут між OA і OP є найменшим. Використовуйте крапковий виріб для визначення кута.

Що стосується об'єднання сегментів, зробіть порівняння між попереднім сегментом і дугою поточного сегмента. Якщо поточний сегмент перекинувся з лівого на правий бік або навпаки, вам слід приєднатись до сегментів.

Для математики для приєднання сегментів ми будемо використовувати точку кріплення попереднього сегмента мотузки, Q, а також ті, що були у нас для випадку розщеплення. Тож тепер ви хочете порівняти вектори QO, OA та OB. Якщо знак (QO x OA) відрізняється від знаку (QO x OB), мотузка перетнула зліва направо або навпаки, і вам слід приєднатись до сегментів. Це, звичайно, також може статися, якщо мотузка хитається на 180 градусів, тому, якщо ви хочете, щоб мотузка могла обернутися навколо однієї точки в просторі замість багатокутної форми, ви можете додати спеціальний чохол для цього.

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


1
Проблема такого підходу полягає в тому, що помилки точності з плаваючою комою дають можливість канату пройти "через" точку.
Ендрю Рассел

16

Минув час, коли я грав у Черв'яків, але з того, що я пам’ятаю - коли мотузка обертається навколо речей, є лише одна (пряма) секція мотузки, яка рухається в будь-який час. Решта мотузки стає статичною

Тому фактичної фізики займається дуже мало. Активну секцію можна моделювати як єдину жорстку пружину з масою на кінці

Цікавим бітом буде логіка розщеплення / приєднання неактивних ділянок мотузки до / з активної секції.

Я думаю, було б дві основні операції:

'Спліт' - Мотузка щось перетинала. Розділіть його на перехресті на неактивний відрізок та новий, коротший, активний відрізок

'Приєднатися' - активна мотузка перемістилася в положення, де найближчого перехрестя більше не існує (це може бути просто простий кут / крапка випробування виробу?). Приєднайтеся до 2 розділів, створивши новий, довший, активний розділ

У сцені, побудованій з 2D багатокутників, усі точки розщеплення повинні знаходитись у вершині на сітці зіткнення. Виявлення зіткнення може спростити вниз щось до рядків "Якщо мотузка під час цього оновлення проходить через вершину, розділіть / приєднайте мотузку до цієї вершини?


2
Цей хлопець виявився прямо на місці ... Насправді, це не весна "жорсткої" весни, ви лише обертаєте якусь пряму лінію ...
speeder

Ваша відповідь технічно правильна. Але я начебто припускав, що очевидно мати сегменти ліній, розділити їх і з'єднати їх. Мене цікавить власне алгоритм / математика для цього. Я зробив своє питання більш конкретним.
Ендрю Рассел

3

Перевірте, як було виконано мотузку ніндзя в Гусаносі :

  • Мотузка діє як частинка, поки не прикріпиться до чогось.
  • Після приєднання мотузка просто прикладає силу до хробака.
  • Приєднання до динамічних об'єктів (як і інших червів) все ще є TODO: у цьому коді.
  • Я не можу згадати, чи підтримується обгортання об'єктів / кутів ...

Відповідний уривок з ninjarope.cpp :


void NinjaRope::think()
{
    if ( m_length > game.options.ninja_rope_maxLength )
        m_length = game.options.ninja_rope_maxLength;

    if (!active)
        return;

    if ( justCreated && m_type->creation )
    {
        m_type->creation->run(this);
        justCreated = false;
    }

    for ( int i = 0; !deleteMe && i < m_type->repeat; ++i)
    {
        pos += spd;

        BaseVec<long> ipos(pos);

        // TODO: Try to attach to worms/objects

        Vec diff(m_worm->pos, pos);
        float curLen = diff.length();
        Vec force(diff * game.options.ninja_rope_pullForce);

        if(!game.level.getMaterial( ipos.x, ipos.y ).particle_pass)
        {
            if(!attached)
            {
                m_length = 450.f / 16.f - 1.0f;
                attached = true;
                spd.zero();
                if ( m_type->groundCollision  )
                    m_type->groundCollision->run(this);
            }
        }
        else
            attached = false;

        if(attached)
        {
            if(curLen > m_length)
            {
                m_worm->addSpeed(force / curLen);
            }
        }
        else
        {
            spd.y += m_type->gravity;

            if(curLen > m_length)
            {
                spd -= force / curLen;
            }
        }
    }
}

1
Гмм ... це, здається, зовсім не відповідає на моє запитання. Вся суть мого питання - намотування мотузки навколо світу з багатокутників. У Гусаноса, здається, немає обгортання та растрового світу.
Ендрю Рассел

1

Боюся, я не можу дати вам конкретний алгоритм у верхній частині голови, але мені здається, що для виявлення зіткнення мотузки ніндзя важливі лише дві речі: будь-які потенційно стикаються вершини на перешкодах в радіусі останнього «розщеплення», що дорівнює решті довжини відрізка; і поточний напрям гойдання (за годинниковою або проти годинникової стрілки). Якщо ви створили тимчасовий список кутів від вершини "розділення" до кожної з вершин, що знаходяться поблизу, вашому алгоритму просто слід було б подбати, якщо ваш сегмент збирається промахнутись повз цей кут для будь-якої заданої вершини. Якщо це так, то вам потрібно виконати операцію розділення, що легко, як пиріг - Це просто рядок від останньої вершини розбиття до нового розколу, а потім обчислюється новий залишок.

Я думаю, що вершини мають значення. Якщо ви загрожуєте потрапити на відрізок між вершинами на перешкоді, тоді ваше нормальне виявлення зіткнення для хлопця, що висить у кінці мотузки, повинно вступити. Іншими словами, ваша мотузка лише коли-небудь збирається "залізти" на кути так чи інакше, тож сегменти між ними не мають значення.

Вибачте, у мене немає нічого конкретного, але, сподіваємось, це приведе вас там, де вам потрібно бути, концептуально, щоб це сталося. :)


1

Ось публікація, яка містить посилання на статті про подібні типи моделювання (в інженерному / академічному контекстах, а не для ігор): https://gamedev.stackexchange.com/a/10350/6398

Я спробував принаймні два різні підходи до виявлення зіткнень + відповідь для такого роду "дротяного" моделювання (як видно в грі Umihara Kawase); принаймні, я думаю, що це те, що ти шукаєш - мабуть, не існує конкретного терміна для подібного моделювання, я просто називаю це "дротом", а не "мотузкою", тому що, здається, більшість людей вважають "мотузку" синонімом "ланцюжка частинок". І, якщо ви хочете, щоб поведінка мотузки ніндзя (тобто вона може штовхати ТА потягувати), це більше схоже на жорсткий дріт, ніж на мотузку. Все одно ..

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

(Я не можу повністю згадати OTOH, але ви можете підійти до нього наступним чином: знайдіть час t, коли точка a міститься у рядку, що проходить через b, c, (я думаю, що я це зробив, вирішивши, коли крапка (ab, cb) = 0, щоб знайти значення t), а потім задати дійсний час 0 <= t <1, знайдіть параметричне положення s а на відрізку bc, тобто a = (1-s) b + s c і якщо a знаходиться між b і c (тобто, якщо 0 <= s <= 1), це дійсне зіткнення.

AFAICR, ви також можете підійти до цього навпаки (тобто вирішити для s, а потім підключити це, щоб знайти t), але це набагато менш інтуїтивно. (Вибачте, якщо це не має сенсу, я не встигаю викопувати свої нотатки, і пройшло вже кілька років!))

Отже, тепер ви можете обчислити всі часи, в які відбуваються події (тобто вузли мотузки слід вставляти або видаляти); обробити найбільш ранню подію (вставити або видалити вузол), а потім повторити / повторити, поки не буде більше подій між t = 0 і t = 1.

Одне попередження про такий підхід: якщо об’єкти, якими мотузка може обернутись, є динамічними (особливо, якщо ви імітуєте їх І їх вплив на мотузку, і навпаки), то можуть виникнути проблеми, якщо ці об’єкти обрізати / проходити через кожен інше - дріт може заплутатися. І, безумовно, буде складним завданням запобігти такому взаємодію / русі (кути предметів, ковзаючи між собою) у фізичному моделюванні в стилі box2d .. невелика кількість проникнення між об'єктами - це нормальна поведінка в цьому контексті.

(Принаймні .. це була проблема з однією з моїх реалізацій "дроту".)

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

Я думаю, що підхід Пекуї працює і для цього, проте є альтернативні підходи. Один із підходів, який я використав, - це додавання допоміжних даних зіткнення: у кожній опуклої вершини v у світі (тобто кутах фігур, якими мотузка може обернутися), додати крапку u, що утворює відрізок прямої лінії uv, де u - деякий точка "всередині кута" (тобто всередині світу, "позаду" v; для обчислення u ви можете кинути промінь всередину v вздовж його інтерпольованої нормалі і зупинити деяку відстань після v або до того, як промінь перетинається з краєм світу і Виходить із суцільної області. Або ви можете просто вручну намалювати сегменти у світ, використовуючи візуальний інструмент / редактор рівнів).

У будь-якому разі, тепер у вас є набір "кутових рядків рядків" uv; для кожного uv і кожного сегмента ab в проводі перевірте, чи перетинаються ab і uv (тобто статичний, булевий рядок lineseg-lineseg-перетин); якщо так, повторіть повтор (розділіть рядок рядка ab на av і vb, тобто вставте v), записуючи, в якому напрямку мотузка зігнута на v. Потім для кожної пари сусідніх рядків ab, bc в проводі, перевіряйте, чи поточний напрямок вигину на b те саме, що і коли було створено b (усі ці випробування на "напрямок згину" є лише тестами області, що підписалися); якщо ні, то з’єднайте два сегменти у змінні (тобто видаліть b).

А може, я злився і потім розколовся, я забуваю - але це, безумовно, працює принаймні в одному з двох можливих замовлень! :)

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

У будь-якому випадку, сподіваюсь, це буде корисною ... документи в пості, з якими я також зв’язувався, також повинні дати вам кілька ідей.


0

Один із підходів до цього - моделювати мотузку як стикаються частинки, з'єднані пружинами. (досить жорсткі, можливо, навіть просто як кістка). Частинки стикаються з навколишнім середовищем, переконуючись, що мотузка обмотається навколо предметів.

Ось демонстрація з джерелом: http://www.ewjordan.com/rgbDemo/

(На першому рівні перейдіть вправо, є червона мотузка, з якою ви можете взаємодіяти)


2
Ага - це конкретно те, чого я не хочу (див. Питання).
Ендрю Расселл

Ага. Це не було зрозуміло з початкового питання. Дякуємо, що знайшли час, щоб розібратися в цьому. (Чудова діаграма!) Я б все-таки працював із серією дуже маленьких фіксованих з'єднань, на відміну від динамічних розщеплень - якщо це не величезна проблема продуктивності у вашому оточенні, кодувати це набагато простіше.
Рейчел Блюм
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.