Thêm lượt thực tế
Bước tiếp theo là thêm các đường cong cong thực tế cho các đơn vị của chúng tôi để chúng không xuất hiện thay đổi hướng đột ngột mỗi khi chúng cần rẽ. Một giải pháp đơn giản liên quan đến việc sử dụng một spline để làm mịn các góc đột ngột thành các lượt. Trong khi điều này giải quyết một số mối quan tâm thẩm mỹ, nó vẫn dẫn đến chuyển động vật lý rất không thực tế cho hầu hết các đơn vị. Ví dụ, nó có thể thay đổi một góc đột ngột của một chiếc xe tăng thành một đường cong chặt chẽ, nhưng khúc cua cong vẫn sẽ chặt hơn nhiều so với chiếc xe tăng thực sự có thể thực hiện.
Để có giải pháp tốt hơn, điều đầu tiên chúng ta cần biết là bán kính quay vòng cho đơn vị của chúng tôi. Bán kính quay vòng là một khái niệm khá đơn giản: nếu bạn đang ở trong một bãi đậu xe lớn trong xe của mình và xoay bánh xe sang trái theo hướng nó sẽ đi và tiến tới lái xe trong một vòng tròn, bán kính của vòng tròn đó là vòng quay của bạn bán kính. Bán kính quay vòng của một chiếc Beetle của Volkswagen sẽ nhỏ hơn đáng kể so với một chiếc SUV lớn, và bán kính quay vòng của một người sẽ thấp hơn đáng kể so với một con gấu lớn, gỗ.
Giả sử bạn đang ở một điểm nào đó (điểm gốc) và chỉ theo một hướng nhất định, và bạn cần đến một điểm khác (đích), như minh họa trong Hình 5. Con đường ngắn nhất được tìm thấy bằng cách rẽ trái theo bạn có thể, đi theo vòng tròn cho đến khi bạn được chỉ thẳng vào đích, rồi tiếp tục tiến về phía trước, hoặc bằng cách rẽ phải và làm điều tương tự.
Trong Hình 5, con đường ngắn nhất rõ ràng là đường màu xanh lá cây ở phía dưới. Đường dẫn này hóa ra khá đơn giản để tính toán do một số mối quan hệ hình học, được minh họa trong Hình 6.
Đầu tiên, chúng tôi tính toán vị trí của điểm P, là tâm của vòng tròn quay của chúng tôi và luôn luôn bán kính r so với điểm bắt đầu. Nếu chúng ta rẽ phải từ hướng ban đầu, điều đó có nghĩa là P nằm ở góc (init_direction - 90) so với điểm gốc, vì vậy:
angleToP = initial_direction - 90
P.x = Origin.x + r * cos(angleToP)
P.y = Origin.y + r * sin(angleToP)
Bây giờ chúng ta đã biết vị trí của điểm trung tâm P, chúng ta có thể tính khoảng cách từ P đến đích, được hiển thị là h trên sơ đồ:
dx = Destination.x - P.x
dy = Destination.y - P.y
h = sqrt(dx*dx + dy*dy)
Tại thời điểm này, chúng tôi cũng muốn kiểm tra xem đích đến không nằm trong vòng tròn, bởi vì nếu có, chúng tôi không bao giờ có thể đến được:
if (h < r)
return false
Bây giờ chúng ta có thể tính độ dài của đoạn d, vì chúng ta đã biết độ dài của hai cạnh còn lại của tam giác vuông, cụ thể là h và r. Chúng ta cũng có thể xác định góc từ mối quan hệ tam giác vuông:
d = sqrt(h*h - r*r)
theta = arccos(r / h)
Cuối cùng, để tìm ra điểm Q tại đó rời khỏi vòng tròn và bắt đầu trên đường thẳng, chúng ta cần biết tổng góc +, và dễ dàng xác định là góc từ P đến đích:
phi = arctan(dy / dx) [offset to the correct quadrant]
Q.x = P.x + r * cos(phi + theta)
Q.y = P.y + r * sin(phi + theta)
Các tính toán trên đại diện cho con đường rẽ phải. Đường dẫn bên trái có thể được tính theo cùng một cách chính xác, ngoại trừ việc chúng ta thêm 90 vào init_direction để tính angleToP, và sau đó chúng ta sử dụng - thay vì +. Sau khi tính toán cả hai, chúng ta chỉ cần xem đường dẫn nào ngắn hơn và sử dụng đường dẫn đó.
Khi thực hiện thuật toán này và các thuật toán tiếp theo, chúng tôi sử dụng cấu trúc dữ liệu lưu trữ tối đa bốn "phân đoạn dòng", mỗi phân đoạn là thẳng hoặc cong. Đối với các đường cong được mô tả ở đây, chỉ có hai đoạn được sử dụng: một vòng cung theo sau là một đường thẳng. Cấu trúc dữ liệu chứa các thành viên xác định xem phân đoạn là một cung hay một đường thẳng, độ dài của phân đoạn và vị trí bắt đầu của nó. Nếu đoạn là một đường thẳng, cấu trúc dữ liệu cũng chỉ định góc; đối với các cung, nó chỉ định tâm của vòng tròn, góc bắt đầu trên vòng tròn và tổng radian được bao phủ bởi vòng cung.
Khi chúng ta đã tính toán đường cong cần thiết để có được giữa hai điểm, chúng ta có thể dễ dàng tính toán vị trí và hướng của mình tại bất kỳ thời điểm nào được đưa ra theo thời gian, như trong Liệt kê 2.
DANH SÁCH 2. Tính toán vị trí và định hướng tại một thời điểm cụ thể.
distance = unit_speed * elapsed_time
loop i = 0 to 3:
if (distance < LineSegment[i].length)
// Unit is somewhere on this line segment
if LineSegment[i] is an arc
//determine current angle on arc (theta) by adding or
//subtracting (distance / r) to the starting angle
//depending on whether turning to the left or right
position.x = LineSegment[i].center.x + r*cos(theta)
position.y = LineSegment[i].center.y + r*sin(theta)
//determine current direction (direction) by adding or
//subtracting 90 to theta, depending on left/right
else
position.x = LineSegment[i].start.x
+ distance * cos(LineSegment[i].line_angle)
position.y = LineSegment[i].start.y
+ distance * sin(LineSegment[i].line_angle)
direction = theta
break out of loop
else
distance = distance - LineSegment[i].length