Не так сложно создать довольно хорошее автомобильное движение (но этот пост будет довольно длинным). Вам нужно «смоделировать» пару основных сил, чтобы сделать автомобиль физически правдоподобным.
(Все примеры кода являются псевдокодом.)
ускорение
Во-первых, вам, очевидно, понадобится ускорение. Что-то простое, как в следующей строке:
acceleration_vector = forward_vector * acceleration_input * acceleration_factor
forward_vector
- Вектор, указывающий в том же направлении, что и автомобиль.
acceleration_input
- Вход должен быть в интервале [-1, 1].
acceleration_factor
- Значение ускорения (в пикселях в секунду ^ 2 или каковы ваши единицы измерения).
Рулевое управление
Рулевое управление тоже довольно простое. В принципе, то, что вы будете делать, это вращать вектор вперед автомобиля, чтобы он указывал в каком-то другом направлении.
steer_angle = steer_input * steer_factor
new_forward_vector = rotate_around_axis(forward_vector, up_vector, steer_angle)
Вы можете столкнуться с осложнением здесь, однако. Если ваш ввод осуществляется с клавиатуры, его значение будет равно -1 или 1, что означает, что ваш автомобиль будет поворачиваться мгновенно. Вы можете исправить это, используя очень простую линейную интерполяцию (lerping):
amount = time_since_last_frame * steer_lerp_factor
forward_vector = lerp(forward_vector, new_forward_vector, amount)
Сумма должна зависеть от времени так, чтобы ваше движение не зависело от вашей частоты кадров. Количество должно быть между [0, 1] и чем оно меньше, тем плавнее будет переход между старым и новым векторами.
(В этот момент вы обнаружите, что автомобиль будет поворачиваться, даже если он стоит на месте. Чтобы этого не произошло , умножьте steer_angle
на значение current_speed / max_speed
, max_speed
определяемое вами как постоянная.)
перемещение
Теперь мы применим ускорение и переместим автомобиль на определенное количество пикселей в зависимости от его скорости, ускорения и рулевого управления. Мы также хотим ограничить скорость автомобиля, чтобы он не двигался бесконечно быстро.
current_speed = velocity_vector.norm()
if (current_speed < max_speed)
{
velocity_vector += acceleration_vector * time_since_last_frame
}
position_vector += velocity_vector * time_since_last_frame
Ваша машина сейчас скользит
Если я прав, ваша машина должна теперь скользить, когда вы поворачиваете, как если бы она была на льду. Это потому, что нет трения. На реальной машине высокое боковое трение (из-за того, что колеса не могут вращаться вбок: P).
Вам нужно будет уменьшить боковую скорость. Не уменьшая его полностью, вы также можете сделать автомобиль дрейфующим.
lateral_velocity = right_vector * dot(velocity_vector, right_vector)
lateral_friction = -lateral_velocity * lateral_friction_factor
Поскольку мы говорим о трении, вам также может понадобиться сила (трения), которая уменьшает вашу скорость, так что, когда вы прекращаете ускоряться, ваша машина в конечном итоге остановится.
backwards_friction = -velocity_vector * backwards_friction_factor
Ваш код для перемещения машины теперь должен выглядеть так:
// Friction should be calculated before you apply the acceleration
lateral_velocity = right_vector * dot(velocity_vector, right_vector)
lateral_friction = -lateral_velocity * lateral_friction_factor
backwards_friction = -velocity_vector * backwards_friction_factor
velocity_vector += (backwards_friction + lateral_friction) * time_since_last_frame
current_speed = velocity_vector.norm()
if (current_speed < max_speed)
{
velocity_vector += acceleration_vector * time_since_last_frame
}
position_vector += velocity_vector * time_since_last_frame
Закрытие заметки
Я упоминал, как вы должны применить lerping к рулевому управлению; Я думаю, что вам может понадобиться сделать то же самое для ускорения и, возможно, для угла поворота (вам придется хранить их значения из предыдущего кадра и отрываться от него). Также все векторы относительно автомобиля (вперед, вправо, вверх) должны иметь длину 1.
Кроме того, трение немного сложнее, чем я показал здесь. Вы должны всегда следить за тем, чтобы его длина никогда не превышала длины ускорения, необходимого для остановки автомобиля (иначе трение заставило бы автомобиль двигаться в противоположном направлении). Таким образом, вы должны иметь что-то вроде:
dt = time_since_last_frame
backwards_friction.resize(min(backwards_friction.norm(), velocity_vector.norm() / dt))
lateral_friction.resize(min(lateral_friction.norm(), lateral_velocity.norm() / dt))