Як обчислити реакцію зіткнення між сферою і площиною?


9

Я намагаюся створити просту 3D-гру і потрібно обмежувати гравця в межах ігрового світу. Коли гравець б'є по сторонах світу, я хочу, щоб корабель гравця трохи відскочив.

Насправді я намагаюся вловити гравець у коробці, і не даю їм втекти по сторонах ...

Мені вдалося визначити межі ігрового світу як сукупність літаків, з нормалями та відстані від походження. У гравця є сферична сфера обмеження, і з цього веб-сайту http://www.gamasutra.com/view/feature/3383/simple_intersection_tests_for_games.php мені вдалося виявити зіткнення.

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

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

Відповіді:


4

Вам доведеться застосувати імпульс до вашого об'єкта, що є негайною зміною його швидкості. У реальному світі на об'єкт буде застосовано потужну силу протягом дуже короткого часового кроку, змінивши його прискорення і спричинивши зміну швидкості. Однак, оскільки ми працюємо в дискретному світі, нам доведеться трохи обдурити, щоб імітувати цю різку зміну напрямку. Що стосується сфери та площини, це досить просто. Найбільш основна реакція на зіткнення - це відображення швидкості сфери навколо нормальної площини, і тоді результат - нова швидкість сфери. Псевдо-код виглядатиме приблизно так:

reflected = 2 * plane.normal * (plane.normal * sphere.velocity)
sphere.velocity -= reflected

Звідти ви можете додати демпфірування (помножити на деякий коефіцієнт, як 0,9) для обліку енергії, втраченої на тепло або тертя. Якщо ви хочете включити кутову швидкість (можливо, ваша сфера обертається), то рівняння стануть трохи складнішими.

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


4
Це, по суті, правильний шлях, однак обчислення часу удару (TOI) може зробити речі більш точними, оскільки частоти кадрів коливаються або падають. Знаючи, спираючись на швидкість потоку, те, як давно відбувся вплив, може допомогти вам обчислити час удару, і використовуючи це, ви можете перемістити сферу назад до її положення в момент удару і регулювати швидкість звідти. Відрегулювавши положення та швидкість руху від точки удару, в момент удару ви рухаєтесь по новій швидкості на кількість часу, яке ви відняли, щоб дістатися до TOI.
Нік Фостер

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

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

@Piku і для цього ми з'ясуємо час у минулому, коли сталося зіткнення (називається TOI / час удару). Як тільки ми маємо це, ми можемо скористатися швидкістю об’єкта, щоб повернути його назад ( distance = speed * timeяк правило, з додатковою невеликою відстані, щоб уникнути помилок), а потім оновити його швидкість до того, який результат зіткнення.
Джонатан Дікінсон

@Piku також ми не розуміємо, де ми будемо в наступному кадрі (я ніколи не бачив, щоб це було зроблено особисто), але, як правило, ми робимо виявлення зіткнень та реакцію: ПІСЛЯ ми обчислюємо нову позицію для ЦОГО кадру, але ПЕРЕД ми застосовуємо нове положення для ЦЬОГО кадру.
Джонатан Дікінсон

1

F = ma, або a = F / m. Обчисліть точку зіткнення між сферою і площиною. Зазвичай це центр сфери - нормальний * радіус. Якщо ви хочете отримати більшу точність, обчисліть, наскільки куля проникла площину, і відрегулюйте свій розрахунок. Звичайно, це необов'язково, якщо ви не хочете дійсно точної фізики. Тепер обчисліть відносну швидкість вздовж норми. Для статичної площини це: Vball Dot N. Потім множте VballDotN на -1, і помножте на масу. У фізиці на цьому етапі ви також помножили це на коефіцієнт реституції (коефіцієнт відмов). Помножте цей скаляр на N і ви маєте свою силу.

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

vec3 Vrel = Ball.getVelocity();
float vDotN = Vrel.Dot(CollisionNormal);
vec3 F = -(1.0f+Ball.getRestitution())*vDotN;
F*=Ball.getMass();
Ball.accelerate(F/Ball.getMass());

Цей спосіб точний до формул реакції зіткнення. Якщо ви хочете ще більшої точності, ви захочете врахувати тертя, що призведе до того, що м'яч крутиться, але я не знаю, чи хочете ви цього у своїй грі. У випадку, якщо ви це зробите, саме так ви обчислюєте дотичну силу:

vec3 Ft = -(Ball.getvelocity()+(vDotN*CollisionNormal));
Ft*=Ball.getKineticFriction()+Wall.getKineticFriction(); //you could fudge these numbers
Ft*=Ball.getMass();
vec3 vec2Centre = Ball.getPosition()-ContactPoint;
vec3 Torque = cross(vec2Centre,Ft);
Ball.AngularAccelerate(Torque/Ball.getMomentofInertia(glm::normalize(Torque)));

Не забудьте розрахувати Ft перед застосуванням будь-яких лінійних ефектів, інакше тертя не буде точним.


Чи не повинен бути рядок 3 vec3 F = -CollisionNormal * (1.0f+Ball.getRestitution())*vDotN;:?
Shital Shah

Справді, так, я пропустив цю частину. Дякуємо, що вказали на це.
Ян Янг

0

Я б запропонував спочатку обчислити відстань від площини; і тоді, коли відстань <= до радіуса здійснюють реакцію зіткнення.

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.