Створити досить хороший рух автомобіля не так складно (але ця посада буде досить довгою). Вам потрібно буде "імітувати" пару основних сил, щоб зробити рух автомобіля фізично правдоподібним.
(Усі зразки коду є псевдокодом.)
Прискорення
По-перше, вам, очевидно, знадобиться прискорення. Щось таке просте, як це робиться в наступному рядку:
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, що означає, що ваш автомобіль повернеться миттєво. Ви можете виправити це за допомогою дуже простої лінійної інтерполяції (лерпінгу):
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
Заключні записки
Я згадав, як слід застосовувати лерпінг до керма; Я думаю, що вам може знадобитися зробити те ж саме для прискорення і, можливо, для кута повороту (вам доведеться зберігати їхні значення з попереднього кадру та лерп з цього). Також всі вектори відносно машини (вперед, вправо, вгору) повинні бути довжиною 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))