Как рассчитать реакцию на столкновение сферы и плоскости?

9

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

По сути, я пытаюсь поймать игрока в коробку и не дать ему уйти через боковые стороны ...

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

Теперь я не могу понять, что делать при обнаружении столкновения. Лучшее, чем я могу управлять, - это застревание игрока в самолете, прохождение прямо через него или многократный отскок от него с очень высокой скоростью.

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

Piku
источник

Ответы:

4

Вы должны будете применить импульс к вашему объекту, который сразу же изменит его скорость. В реальном мире мощная сила будет применяться к объекту за очень короткий промежуток времени, обращая его ускорение и вызывая изменение его скорости. Однако, поскольку мы работаем в дискретном мире, нам нужно немного обмануть, чтобы смоделировать это резкое изменение направления. Для сферы и плоскости это довольно просто. Самый базовый отклик на столкновение состоит в том, чтобы отразить скорость сферы вокруг нормали плоскости, и тогда результатом является новая скорость сферы. Псевдокод будет выглядеть примерно так:

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

Оттуда вы можете добавить некоторое демпфирование (умножить на некоторый коэффициент, например 0,9), чтобы учесть потерю энергии на тепло или трение. Если вы хотите задействовать угловую скорость (возможно, ваша сфера вращается), то уравнения становятся немного сложнее.

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

kevintodisco
источник
4
По сути, это правильный путь, однако расчет времени воздействия (TOI) может сделать вещи более точными, поскольку частота кадров колеблется или падает. Зная, исходя из текущей скорости, как давно произошло столкновение, вы можете рассчитать время удара и, используя его, вы можете переместить сферу обратно в ее положение в момент удара и отрегулировать скорость оттуда. После регулировки положения и скорости от точки удара во время удара вы затем двигаетесь вдоль новой скорости на количество времени, которое вы вычли, чтобы добраться до TOI.
Ник Фостер
Хорошо, это, кажется, в основном работает, но это немного ... странно. Я думаю, что я могу делать это не в том месте в моем коде. Должен ли я пройтись по всем моим объектам и проверить, будут ли они сталкиваться, прежде чем переместить их (основываясь на том, где они будут находиться в следующем кадре) или переместить их, а затем проверить на столкновения?
Пику
@ Пику, нет, не обнаруживай, столкнутся ли они. Если происходит столкновение, помните, что есть очень хороший шанс, что два объекта теперь перекрываются далеко за пределами того места, где произошло бы фактическое столкновение. По сути, вам необходимо выяснить, где произошло столкновение, как если бы у вас была бесконечная частота кадров (чего у вас нет), и переместить объект обратно в положение, где столкновение первоначально произошло бы. Если вы не разделите объекты таким образом, вы будете постоянно реагировать на одно и то же столкновение, и объект застрянет.
Джонатан Дикинсон
@Piku и для этого мы выясним время в прошлом, когда произошло столкновение (называемое TOI / время удара). Как только мы получим это, мы можем использовать скорость объекта, чтобы переместить его назад ( distance = speed * timeобычно с дополнительным небольшим расстоянием, чтобы избежать ошибки), а затем обновить его скорость до того, каков будет результат столкновения.
Джонатан Дикинсон
@Piku также мы не выясняем, где мы будем в следующем кадре (я никогда не видел, чтобы это было сделано лично), но, как правило, мы делаем обнаружение столкновений и реагирование: ПОСЛЕ того, как мы вычисляем новую позицию для ЭТОГО кадра, но ДО мы применяем новую позицию для этого кадра.
Джонатан Дикинсон
1

F = ма или а = F / м. Рассчитайте точку столкновения между сферой и плоскостью. Обычно это центр сферы - нормальный * радиус. Если вы хотите большей точности, рассчитайте, как далеко сфера проникла в плоскость, и скорректируйте свой расчет. Конечно, это во многом необязательно, если только вы не хотите действительно точной физики. Теперь вычислите относительную скорость по нормали. Для статической плоскости это: 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

Я бы посоветовал сначала подсчитать расстояние от самолета. а затем при расстоянии <= до радиуса проводят реакцию столкновения.

Затем вы можете изменить это, чтобы вычислить расстояние и, если расстояние меньше этого радиуса (что означает, что объект перекрывается), сместить положение шариков и затем выполнить реакцию столкновения.

Красное небо
источник