Я пытаюсь скопировать логику Sonic physics engine, которая была написана для системы с фиксированным сроком (60 FPS) в переменном возрасте времени (Slick2D, если быть точным).Переменные временные метки и сила тяжести/трение
В оригинале при нажатии кнопки перехода игрок velocity.y
настроен на -6,5, а для отметки velocity.y
каждый отметит 0,21875.
Каждый раз, когда вызывается мое логическое обновление, передается параметр дельта-времени, определяющий, сколько миллисов прошло. Если больше миллисов прошло, чем я ожидал, я повторяю логику обновления, передавая «внутреннюю дельта», которая не больше 1 или меньше, если мы имеем дело с «остатком» целевого кадра.
E.g. если мы ожидаем, что кадр займет 16 мс, а он сделает займет 16 мс, цикл будет повторяться один раз и пройдет thisMiniTick
как 1. Если дельта не была 16 мс, а 40 мс, цикл будет выполняться три раза, проходя 1, 1, и, наконец, 0,5.
Я ошибочно думал, что в каждой из этих внутренних циклов обновления я мог бы сделать velocity.y += (gravity * thisMiniTickRelative)
, но это не сработает. При более быстрых кадрах применяется недостаточная гравитация, вызывающая более высокий скачок, а на более медленных кадрах скачок ниже (хотя и не так близко, как заметно).
Есть ли способ сделать это, который будет работать практически для всех кадров, или я должен прибегнуть к установке верхней и нижней границы для delta
?
«внутренней обновление» петля:
float timeRemaining = delta/1000f;
while(timeRemaining > 0)
{
float thisMiniTick = Math.min(timeRemaining, 1f/FRAMES_PER_SECOND);
float thisMiniTickRelative = thisMiniTick/(1f/FRAMES_PER_SECOND);
updateInput(container, game, thisMiniTickRelative);
if (playerAirState)
{
playerVelocity.y += (GRAVITY * thisMiniTickRelative);
}
clampPlayerVelocity();
playerPosition.add(playerVelocity);
doCollisions();
timeRemaining -= thisMiniTick;
}
Спасибо за подробный ответ, это обнадеживает, чтобы увидеть, что нет никакого волшебного решения, которое я не мог понять. Почему вы считаете ошибку timeRemaining ошибкой? У меня первоначально была скорость, умноженная на время, прошедшее, но это означало, что коллизионный код нуждался в большой перезаписи, поскольку игрок мог проехать больше, чем целая плитка за один тик и, таким образом, «пропустить» плитки. –
Да, я, возможно, переоценил это, но я предполагаю, что, когда я видел, что 'timeRemaining' идет наперекосяк, это когда люди пытаются использовать это значение, чтобы выяснить, сколько еще времени им нужно делать другими вещами, такими как вычисления AI и т. Д. Это не значит, что вы не можете его использовать, просто с помощью различных способов решения этой проблемы, привязка всего к значению 'delta' даст вам самые надежные результаты. – jefflunt
Что касается игроков, движущихся слишком быстро для столкновения, есть несколько способов подойти к этой проблеме. Можно было бы сделать расчет столкновения по всему пространству, пройденному игроком (если игрок переместил 3 плитки, то вам нужно выяснить, что произошло с каким-либо из них, и отскочить назад. Другой, гораздо более простой путь - либо увеличить детализацию ваших логических обновлений (корректируя интервалы обновления макс/макс логики), либо заставить игрока двигаться медленнее. – jefflunt