Как я могу реализовать гравитацию? Не для определенного языка, просто псевдокод ...
Как я могу реализовать гравитацию? Не для определенного языка, просто псевдокод ...
Ответы:
Как отмечали другие в комментариях, базовый метод интеграции Эйлера, описанный в ответе tenpn, страдает от нескольких проблем:
Даже для простых движений, таких как баллистические прыжки в условиях постоянной силы тяжести, это приводит к систематической ошибке.
Ошибка зависит от временного шага, что означает, что изменение временного шага систематически меняет траектории объекта, что может быть замечено игроками, если игра использует переменный временной шаг. Даже для игр с фиксированным временным шагом физики изменение временного шага во время разработки может заметно повлиять на физику игры, например, на расстояние, на которое пролетит объект, запущенный с заданной силой, что может нарушить ранее спроектированные уровни.
Это не сохраняет энергию, даже если основная физика должна. В частности, объекты, которые должны постоянно колебаться (например, маятники, пружины, вращающиеся планеты и т. Д.), Могут неуклонно накапливать энергию, пока вся система не развалится.
К счастью, нетрудно заменить интеграцию Эйлера чем-то, что почти так же просто, но при этом не имеет ни одной из этих проблем - в частности, симплектического интегратора второго порядка, такого как интеграция с перепрыгиванием или близкий по скорости метод Верле . В частности, где базовая интеграция Эйлера обновляет скорость и положение как:
ускорение = сила (время, позиция) / масса; время + = временной шаг; положение + = временной шаг * скорость; скорость + = временной шаг * ускорение;
метод скорости Verlet делает это так:
ускорение = сила (время, позиция) / масса; время + = временной шаг; положение + = временной шаг * ( скорость + временной шаг * ускорение / 2) ; newAcceleration = сила (время, позиция) / масса; скорость + = временной шаг * ( ускорение + новое ускорение ) / 2 ;
Если у вас есть несколько взаимодействующих объектов, вы должны обновить все их позиции, прежде чем пересчитывать силы и обновлять скорости. Новое ускорение может быть сохранено и использовано для обновления позиции на следующем временном шаге, уменьшая количество вызовов force()
до одного (на объект) за временной шаг, как в методе Эйлера.
Кроме того, если ускорение обычно является постоянным (например, гравитация во время баллистического прыжка), мы можем упростить вышеупомянутое просто:
время + = временной шаг; положение + = временной шаг * ( скорость + временной шаг * ускорение / 2) ; скорость + = временной шаг * ускорение;
где дополнительный термин, выделенный жирным шрифтом, является единственным изменением по сравнению с базовой интеграцией Эйлера.
По сравнению с интеграцией Эйлера, методы скорости Верле и чехарды имеют несколько приятных свойств:
Для постоянного ускорения они дают точные результаты (в любом случае, вплоть до ошибок округления с плавающей запятой), а это означает, что траектории баллистических прыжков остаются неизменными даже при изменении временного шага.
Они являются интеграторами второго порядка, что означает, что даже при различном ускорении средняя ошибка интегрирования пропорциональна квадрату временного шага. Это может позволить большие временные шаги без ущерба для точности.
Они симплектичны , что означает, что они сохраняют энергию, если физика, лежащая в основе (по крайней мере, пока временной шаг постоянен). В частности, это означает, что вы не будете получать такие вещи, как планеты, спонтанно вылетающие с их орбит, или объекты, прикрепленные друг к другу с помощью пружин, постепенно качающиеся все больше и больше, пока все это не взорвется.
Тем не менее, метод определения скорости Верлета / перепрыгивания почти такой же простой и быстрый, как и базовая интеграция Эйлера, и, конечно, намного проще, чем альтернативы, такие как интеграция Рунге-Кутты четвертого порядка (которая, хотя и является очень хорошим интегратором, лишена симплектического свойства и требует четырех оценок). от force()
функции на шаг по времени). Поэтому я настоятельно рекомендую их всем, кто пишет код игровой физики, даже если это так же просто, как переход с одной платформы на другую.
Изменить: Хотя формальный вывод метода Верле скорости действителен только тогда, когда силы не зависят от скорости, на практике вы можете использовать его очень хорошо, даже с зависимыми от скорости силами, такими как сопротивление жидкости . Для достижения наилучших результатов вы должны использовать начальное значение ускорения для оценки новой скорости для второго вызова force()
, например:
ускорение = сила (время, положение, скорость) / масса; время + = временной шаг; положение + = временной шаг * ( скорость + временной шаг * ускорение / 2) ; скорость + = временной шаг * ускорение; newAcceleration = сила (время, положение, скорость) / масса; скорость + = временной шаг * (новое ускорение - ускорение) / 2 ;
Я не уверен, имеет ли этот конкретный вариант метода скорости Verlet конкретное имя, но я проверил его и, похоже, он работает очень хорошо. Он не так точен, как Рунге-Кутта четвертого порядка (как можно было бы ожидать от метода второго порядка), но он намного лучше, чем Эйлер или наивный метод скорости Верле без промежуточной оценки скорости, и все же сохраняет симплектическое свойство нормальной скорость Верле для консервативных, не зависящих от скорости сил.
Редактировать 2: Очень похожий алгоритм описан, например, Groot & Warren ( J. Chem. Phys. 1997) , хотя, читая между строк, кажется, что они пожертвовали некоторой точностью ради дополнительной скорости, сохранив newAcceleration
значение, вычисленное с использованием расчетной скорости. и повторно использовать его в acceleration
качестве следующего шага. Они также вводят параметр 0 ≤ λ ≤ 1, который умножается acceleration
на начальную оценку скорости; по какой-то причине они рекомендуют λ = 0,5, хотя все мои тесты показывают, что λ= 1 (что я и использую выше) работает так же или лучше, с повторным использованием ускорения или без него. Возможно, это как-то связано с тем, что их силы включают в себя компонент стохастического броуновского движения.
force(time, position, velocity)
моем ответе выше просто сокращение для "силы, действующей на объект при position
движении в velocity
at time
". Как правило, сила будет зависеть от того, находится ли объект в свободном падении или сидит на твердой поверхности, какие-либо близлежащие объекты оказывают на него силу, как быстро он движется по поверхности (трение) и / или через жидкость или газ (перетащить) и т. д.
Каждый цикл обновления вашей игры, сделайте это:
if (collidingBelow())
gravity = 0;
else gravity = [insert gravity value here];
velocity.y += gravity;
Например, в платформер, когда вы прыгаете, гравитация будет включена (collidingBelow сообщает вам, есть ли земля прямо под вами), и когда вы упадете на землю, она будет отключена.
Кроме того, чтобы реализовать переходы, сделайте следующее:
if (pressingJumpButton() && collidingBelow())
velocity.y = [insert jump speed here]; // the jump speed should be negative
И, очевидно, в цикле обновления вы также должны обновить свою позицию:
position += velocity;
Надлежащая независимая от частоты кадров * ньютоновская физическая интеграция:
Vector forces = 0.0f;
// gravity
forces += down * m_gravityConstant; // 9.8m/s/s on earth
// left/right movement
forces += right * m_movementConstant * controlInput; // where input is scaled -1..1
// add other forces in for taste - usual suspects include air resistence
// proportional to the square of velocity, against the direction of movement.
// this has the effect of capping max speed.
Vector acceleration = forces / m_massConstant;
m_velocity += acceleration * timeStep;
m_position += velocity * timeStep;
Настраивайте тяготение Constant, motionConstant и massConstant, пока не почувствуете себя правильно. Это интуитивно понятная вещь, и может потребоваться некоторое время, чтобы почувствовать себя прекрасно.
Легко расширить вектор сил, чтобы добавить новый игровой процесс - например, добавить силу от любого ближайшего взрыва или к черным дырам.
* редактировать: эти результаты будут неправильными с течением времени, но могут быть «достаточно хорошими» для вашей верности или способностей. Смотрите эту ссылку http://lol.zoy.org/blog/2011/12/14/understanding-motion-in-games для получения дополнительной информации.
position += velocity * timestep
выше на position += (velocity - acceleration * timestep / 2) * timestep
(где velocity - acceleration * timestep / 2
просто среднее значение старой и новой скоростей). В частности, этот интегратор дает точные результаты, если ускорение постоянное, как это обычно бывает для гравитации. Для повышения точности при изменяющемся ускорении вы можете добавить аналогичную поправку к обновлению скорости, чтобы получить интеграцию скорости с Верлетом .
Если вы хотите реализовать гравитацию в несколько большем масштабе, вы можете использовать этот вид расчета для каждого цикла:
for each object in the scene
for each other_object in the scene not equal to object
if object.mass * other_object.mass / object.distanceSquaredBetweenCenterOfMasses(other_object) < epsilon
abort the calculation for this pair
if object.mass is much, much bigger than other_object.mass
abort the calculation for this pair
force = gravitational_constant
* object.mass * other_object.mass
/ object.distanceSquaredBetweenCenterOfMasses(other_object)
object.addForceAtCenterOfMass(force * object.normalizedDirectionalVectorTo(other_object))
end for loop
end for loop
Для еще больших (галактических) масштабов одной гравитации недостаточно для создания «реального» движения. Взаимодействие звездных систем в значительной и очень заметной степени продиктовано уравнениями Навье-Стокса для гидродинамики, и вам придется учитывать конечную скорость света - и, следовательно, гравитацию - также.
Код, предоставленный Ильмари Кароненом, почти верен, но есть небольшой сбой. Вы фактически вычисляете ускорение 2 раза за такт, это не соответствует уравнениям учебника.
acceleration = force(time, position) / mass; // Here
time += timestep;
position += timestep * (velocity + timestep * acceleration / 2);
newAcceleration = force(time, position) / mass;
velocity += timestep * (acceleration + newAcceleration) / 2;
Следующий мод правильный:
time += timestep;
position += timestep * (velocity + timestep * acceleration / 2);
oldAcceletation = acceleration; // Store it
acceleration = force(time, position) / mass;
velocity += timestep * (acceleration + oldAcceleration) / 2;
Приветствия
Ответ Пеканта игнорировал время кадра, и это время от времени отличало ваше физическое поведение.
Если вы собираетесь сделать очень простую игру, вы можете создать свой собственный маленький физический движок - назначить массу и все виды физических параметров для каждого движущегося объекта, а также выполнить обнаружение столкновений, а затем обновлять их положение и скорость в каждом кадре. Чтобы ускорить этот прогресс, вам нужно упростить сетку столкновений, уменьшить количество вызовов обнаружения столкновений и т. Д. В большинстве случаев это проблема.
Лучше использовать физический движок, такой как Physix, ODE и Bullet. Любой из них будет стабильным и достаточно эффективным для вас.