Я реализовал собственный физический движок, и я довольно близок к тому, чтобы он работал так, как мне хотелось бы. Существует сила тяжести, столкновения и реакция столкновения. К сожалению, кажется, что есть некоторое дрожание среди почти стационарных объектов, скорее всего, из-за неизменных низких физических тиков.
Я посмотрел онлайн и попробовал некоторые из найденных реализаций, включая некоторые из моих собственных попыток. Вот решения, которые я попробовал:
- Демпфирующее движение, когда скорость / импульс / потенциальная энергия ниже порога.
- Применять гравитацию только тогда, когда скорость / импульс / потенциальная энергия выше порога.
- Реализация функции сна. который проверяет положение объекта за последние 60 кадров и спит, если он не вышел за пределы ограничивающей рамки.
- Итерация по объектам сверху вниз при применении тестирования и разрешения столкновений.
Вот мой код:
for each (auto ball in m_Balls)
{
ball->Update(t);
ball->Accelerate(m_Gravity);
}
// This disgusting hack sorts the balls by height. In a more complete physics
// implementation, I guess I could change the sorting based on the direction of
// gravitational force. This hack is necessary to prevent balls being pulled downwards
// into other balls by gravity; by calculating from the bottom of the pile of
// objects, we avoid issues that occur when adjustments push the object towards gravity.
m_Balls.sort([](const CSprite* a, const CSprite* b)
{return a->m_pos.m_y < b->m_pos.m_y; });
static float cor = 0.8f;
for each (auto ball in m_Balls)
{
for each (auto collider in m_Walls)
{
if (collider->HitTest(ball, 1))
{
float offset = 0;
auto n = Helper::GetNormal(ball, collider, offset);
ball->SetPosition(ball->GetPosition() + (n * offset));
auto r = ball->GetVelocity() - ((1 + cor) * Dot(ball->GetVelocity(), n) * n);
ball->SetVelocity(r);
ball->SetPosition(ball->GetPosition() + ball->GetVelocity() * DeltaTime());
}
}
CVector adjustment;
for each (auto collider in m_Balls)
{
if (ball == collider)
{
break;
}
auto diff = collider->GetPosition() - ball->GetPosition();
float distance = diff.Length();
if (distance <= (ball->GetWidth() / 2) + (collider->GetWidth() / 2))
{
auto midPoint = (ball->GetPosition() + collider->GetPosition()) * 0.5f;
adjustment = diff.Normalise() * (ball->GetWidth() / 2
- Distance(ball->GetPosition(), midPoint));
ball->SetPosition(ball->GetPosition() - adjustment);
diff = collider->GetPosition() - ball->GetPosition();
if (Dot(ball->GetVelocity() - collider->GetVelocity(), diff) > 0)
{
auto n = diff.Normalise();
auto u = Dot(cor * ball->GetVelocity() - collider->GetVelocity(), n) * n;
ball->Accelerate(-u);
collider->Accelerate(u);
}
}
}
if (ball->GetSpeed() > MAX_SPEED)
{
ball->SetSpeed(MAX_SPEED);
}
}
Как я могу предотвратить дрожание среди почти стационарных физических объектов?
physics
vector
collision-resolution
あ ら ま あ
источник
источник
Ответы:
Что ж, получается, что одна из моих булевых проверок была причиной этой проблемы.
Этот код здесь:
Ломал все. У меня сложилось впечатление, что он будет просто игнорировать столкновения с самим собой, но по какой-то причине он предотвращал столкновения в правильном порядке. Не совсем уверен, почему, я думаю, это ошибка в игровом движке, который я использую. В любом случае, вот рабочий код, который реализует состояние сна для всех шаров - когда движение после 30 кадров ограничено определенной ограничивающей областью, объект переводится в состояние сна, в течение которого к нему не прикладываются никакие силы (в этом случае сила тяжести пример). Он просыпается после того, как он перемещается за пределы этой ограничивающей области чем-то - обычно это регулировка или столкновение с другим шаром.
источник