Không khó để tạo ra một phong trào xe hơi khá tốt (nhưng bài đăng này sẽ khá dài). Bạn sẽ cần phải "mô phỏng" một vài lực cơ bản để làm cho chiếc xe chuyển động hợp lý.
(Tất cả các mẫu mã là mã giả.)
Sự tăng tốc
Đầu tiên, rõ ràng bạn sẽ cần tăng tốc. Một cái gì đó đơn giản như dòng sau sẽ làm:
acceleration_vector = forward_vector * acceleration_input * acceleration_factor
forward_vector
- Một vectơ chỉ cùng hướng với ô tô.
acceleration_input
- Đầu vào phải nằm trong khoảng [-1, 1].
acceleration_factor
- Giá trị của gia tốc (pixel / giây ^ 2 hoặc bất kể đơn vị của bạn là gì).
Hệ thống lái
Chỉ đạo cũng khá đơn giản. Về nguyên tắc, những gì bạn sẽ làm là xoay vectơ về phía trước của chiếc xe để làm cho nó chỉ theo một số hướng khác.
steer_angle = steer_input * steer_factor
new_forward_vector = rotate_around_axis(forward_vector, up_vector, steer_angle)
Bạn có thể gặp một biến chứng ở đây, tuy nhiên. Nếu đầu vào của bạn thông qua bàn phím, giá trị của nó sẽ là -1 hoặc 1, có nghĩa là xe của bạn sẽ chuyển ngay lập tức. Bạn có thể khắc phục điều này bằng cách sử dụng phép nội suy tuyến tính rất đơn giản (lerping):
amount = time_since_last_frame * steer_lerp_factor
forward_vector = lerp(forward_vector, new_forward_vector, amount)
Số lượng phải phụ thuộc vào thời gian sao cho chuyển động của bạn không phụ thuộc vào tốc độ khung hình của bạn. Số lượng phải nằm trong khoảng [0, 1] và càng nhỏ, quá trình chuyển đổi giữa các vectơ cũ và mới sẽ càng mượt mà.
(Tại thời điểm này, bạn sẽ thấy rằng chiếc xe sẽ lái ngay cả khi nó đứng yên. Để ngăn chặn điều đó, nhân steer_angle
với current_speed / max_speed
, nơi max_speed
là một hằng số được xác định bởi bạn.)
Di chuyển
Bây giờ chúng ta sẽ áp dụng khả năng tăng tốc và di chuyển chiếc xe một số pixel nhất định dựa trên vận tốc, gia tốc và khả năng lái của nó. Chúng tôi cũng sẽ muốn giới hạn tốc độ của xe sao cho nó không di chuyển quá nhanh.
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
Xe của bạn đang trượt
Nếu tôi đúng, chiếc xe của bạn bây giờ dường như bị trượt bất cứ khi nào bạn rẽ như thể đang ở trên băng. Điều này là do không có ma sát. Trên một chiếc xe thật có ma sát bên cao (do các bánh xe không thể quay sang một bên: P).
Bạn sẽ cần phải giảm vận tốc bên. Bằng cách không giảm hoàn toàn, bạn cũng có thể làm cho chiếc xe dường như bị trôi.
lateral_velocity = right_vector * dot(velocity_vector, right_vector)
lateral_friction = -lateral_velocity * lateral_friction_factor
Vì chúng ta đang nói về ma sát, bạn cũng có thể muốn có một lực (ma sát) làm giảm vận tốc của bạn để khi bạn dừng tăng tốc, cuối cùng xe của bạn sẽ dừng lại.
backwards_friction = -velocity_vector * backwards_friction_factor
Mã của bạn để di chuyển chiếc xe bây giờ sẽ trông như thế này:
// 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
Ghi chú kết thúc
Tôi đã đề cập đến cách bạn nên áp dụng lerping để chỉ đạo; Tôi nghĩ rằng bạn có thể cần phải làm điều tương tự để tăng tốc và có thể cho góc lái (bạn sẽ phải lưu trữ giá trị của chúng từ khung trước đó và lerp từ đó). Ngoài ra tất cả các vectơ liên quan đến xe (tiến, phải, lên) nên có chiều dài 1.
Ngoài ra, ma sát phức tạp hơn một chút so với tôi chỉ ra ở đây. Bạn phải luôn đảm bảo rằng chiều dài của nó không bao giờ lớn hơn tốc độ cần thiết để khiến xe dừng lại (nếu không ma sát sẽ khiến xe chuyển động ngược chiều). Vì vậy, bạn nên có một cái gì đó như:
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))