Я столкнулся с другой проблемой в моей маленькой прыгающей игре с мячом.
Мой мяч подпрыгивает нормально, за исключением последних моментов, когда он собирается отдохнуть. Движение мяча по основной части плавное, но ближе к концу шарик на некоторое время дергается, когда он оседает на нижней части экрана.
Я могу понять, почему это происходит, но я не могу сгладить это.
Буду признателен за любые советы, которые могут быть предложены.
Мой код обновления:
public void Update()
{
// Apply gravity if we're not already on the ground
if(Position.Y < GraphicsViewport.Height - Texture.Height)
{
Velocity += Physics.Gravity.Force;
}
Velocity *= Physics.Air.Resistance;
Position += Velocity;
if (Position.X < 0 || Position.X > GraphicsViewport.Width - Texture.Width)
{
// We've hit a vertical (side) boundary
// Apply friction
Velocity *= Physics.Surfaces.Concrete;
// Invert velocity
Velocity.X = -Velocity.X;
Position.X = Position.X + Velocity.X;
}
if (Position.Y < 0 || Position.Y > GraphicsViewport.Height - Texture.Height)
{
// We've hit a horizontal boundary
// Apply friction
Velocity *= Physics.Surfaces.Grass;
// Invert Velocity
Velocity.Y = -Velocity.Y;
Position.Y = Position.Y + Velocity.Y;
}
}
Возможно, я должен также указать на это Gravity
, Resistance
Grass
и Concrete
все они типа Vector2
.
Ответы:
Вот шаги, необходимые для улучшения вашего цикла симуляции физики.
1. Временной шаг
Основная проблема, которую я вижу в вашем коде, заключается в том, что он не учитывает время шага по физике. Должно быть очевидно, что здесь что-то не так,
Position += Velocity;
потому что единицы не совпадают. ЛибоVelocity
это на самом деле не скорость, либо чего-то не хватает.Даже если ваши значения скорости и гравитации масштабируются таким образом, чтобы каждый кадр происходил в единицу времени
1
(что означает, например, чтоVelocity
фактически означает расстояние, пройденное за одну секунду), время должно появляться где-то в вашем коде, либо неявно (путем фиксации переменных так, чтобы их имена отражают то, что они на самом деле хранят) или явно (путем введения временного шага). Я считаю, что проще всего объявить единицу времени:И используйте это значение везде, где это необходимо:
Обратите внимание, что любой приличный компилятор упростит умножения на
1.0
, так что часть не будет замедлять работу.Сейчас
Position += Velocity * TimeStep
все еще не совсем точно (см. Этот вопрос, чтобы понять, почему), но это, вероятно, будет делать пока.Кроме того, это должно учитывать время:
Это немного сложнее исправить; Один из возможных способов:
2. Двойные обновления
Теперь проверьте, что вы делаете, когда подпрыгиваете (показан только соответствующий код):
Вы можете видеть, что
TimeStep
используется дважды во время отскока. Это в основном дает мячу вдвое больше времени для обновления. Вот что должно произойти вместо этого:3. Гравитация
Проверьте эту часть кода сейчас:
Вы добавляете гравитацию на всю продолжительность кадра. Но что, если мяч действительно отскакивает во время этого кадра? Тогда скорость будет инвертирована, но гравитация, которая была добавлена, заставит шар ускоряться от земли! Таким образом, избыточная гравитация должна быть удалена при подпрыгивании , а затем добавлена в правильном направлении.
Может случиться, что даже повторное добавление силы тяжести в правильном направлении приведет к слишком сильному ускорению скорости. Чтобы избежать этого, вы можете либо пропустить гравитационное прибавление (в конце концов, оно не так уж много, и оно длится только кадр), либо ограничить скорость до нуля.
4. Фиксированный код
А вот и полностью обновленный код:
5. Дальнейшие дополнения
Для еще большей стабильности симуляции вы можете решить запустить симуляцию физики на более высокой частоте. Это делается тривиально из-за вышеуказанных изменений
TimeStep
, потому что вам просто нужно разбить ваш кадр на столько кусков, сколько вы хотите. Например:источник
velocity += gravity
это неправильно иvelocity += gravity * timestep
имеет смысл. Это может дать тот же результат в конце, но без комментария, говорящего «Я знаю, что я здесь делаю», это все равно означает ошибку кодирования, неаккуратный программист, отсутствие знаний о физике или просто прототип кода, который должен быть улучшенным.gravity
на самом деле ... не гравитация. Но я могу сделать это более ясным в посте.Добавьте проверку, чтобы остановить отскок, используя минимальную вертикальную скорость. И когда вы получите минимальный отскок, положите мяч в землю.
источник
Итак, я думаю, что проблема в том, что это происходит, когда ваш мяч приближается к пределу. Математически мяч никогда не останавливается на поверхности, он приближается к поверхности.
Однако ваша игра не использует непрерывное время. Это карта, которая использует приближение к дифференциальному уравнению. И это приближение недопустимо в этой ограничивающей ситуации (вы можете, но вам придется предпринять меньшие и меньшие временные шаги, что, я полагаю, неосуществимо.
С физической точки зрения происходит то, что, когда мяч находится очень близко к поверхности, он прилипает к нему, если общая сила ниже заданного порога.
@Zhen ответ будет хорошо, если ваша система однородна, а это не так. Он имеет некоторую гравитацию на оси Y.
Итак, я бы сказал, что решением будет не то, что скорость должна быть ниже заданного порога, а общая сила, приложенная к мячу после обновления, должна быть ниже заданного порога.
Эта сила является вкладом силы, приложенной стенкой к мячу, и силой тяжести.
Условие должно быть что-то вроде
если (newVelocity + Physics.Gravity.Force <порог)
обратите внимание, что newVelocity.y является положительной величиной, если отскок находится на стенке дна, а гравитация - отрицательной величиной.
Также обратите внимание, что newVelocity и Physics.Gravity.Force не имеют такие же размеры, как вы написали в
Это означает, что, как и вы, я предполагаю, что delta_time = 1 и ballMass = 1.
Надеюсь это поможет
источник
У вас есть обновление позиции в вашей проверке столкновения, это избыточно и неправильно. И это добавляет энергию мячу, таким образом потенциально помогая ему двигаться постоянно. Наряду с тем, что гравитация не применяется в некоторых кадрах, это создает странное движение. Убери это.
Теперь вы можете увидеть другую проблему: мяч застревает за пределами обозначенной области, постоянно подпрыгивая назад и вперед.
Простой способ решения этой проблемы - проверить, что мяч движется в правильном направлении, прежде чем менять его.
Таким образом вы должны сделать:
В:
И похоже на направление Y.
Для того, чтобы шар хорошо остановился, нужно в какой-то момент остановить гравитацию. Ваша текущая реализация гарантирует, что шар всегда будет всплывать, поскольку гравитация не тормозит его, пока он находится под землей. Вы должны перейти на постоянное применение гравитации. Это, однако, приводит к тому, что мяч медленно опускается в землю после оседания. Быстрое решение для этого, после применения силы тяжести, если шар находится ниже уровня поверхности и движется вниз, остановите его:
Эти изменения в целом должны дать вам достойную симуляцию. Но обратите внимание, что это все еще очень простая симуляция.
источник
Используйте метод мутатора для любых изменений скорости, затем в этом методе вы можете проверить обновленную скорость, чтобы определить, достаточно ли медленно она движется, чтобы успокоить ее. Большинство известных мне физических систем называют это «реституцией».
В приведенном выше методе мы ограничиваем отскок всякий раз, когда он находится на одной оси с гравитацией.
Что-то еще, чтобы рассмотреть, было бы обнаружение, когда шар столкнулся с землей, и если он движется довольно медленно во время столкновения, установите скорость вдоль оси тяжести в ноль.
источник
Другое дело: вы умножаете на коэффициент трения. Измените это - понизьте постоянную трения, но добавьте фиксированное поглощение энергии на отскок. Это будет намного быстрее гасить последние скачки.
источник