Gerçekçi Dönüşler Ekleme
Bir sonraki adım, ünitelerimiz için gerçekçi kavisli dönüşler eklemektir, böylece her dönüşlerinde aniden yön değiştirmezler. Basit bir çözüm, ani köşeleri sırayla düzeltmek için bir spline kullanmayı içerir. Bu, estetik kaygıların bazılarını çözerken, yine de çoğu birim için fiziksel olarak çok gerçekçi olmayan bir hareketle sonuçlanır. Örneğin, bir tankın ani bir virajını sıkı bir eğriye dönüştürebilir, ancak kavisli dönüş hala tankın gerçekleştirebileceğinden çok daha sıkı olacaktır.
Daha iyi bir çözüm için bilmemiz gereken ilk şey, ünitemiz için dönüş yarıçapıdır. Dönme yarıçapı oldukça basit bir kavramdır: arabanızda büyük bir park yerinde iseniz ve tekerleği gidebildiği kadar sola döndürün ve bir daire içinde sürmeye devam ediyorsanız, o dairenin yarıçapı dönüyor yarıçapı. Bir Volkswagen Beetle'ın dönüş yarıçapı, büyük bir SUV'nin dönüş yarıçapından önemli ölçüde daha küçük olacak ve bir kişinin dönüş yarıçapı, büyük, kereste bir ayının yarıçapından önemli ölçüde daha az olacaktır.
Diyelim ki bir noktada (başlangıç noktası) ve belirli bir yöne işaret ettiniz ve Şekil 5'te gösterildiği gibi başka bir noktaya (hedefe) gitmeniz gerekiyor. doğrudan hedefe işaret edene kadar bir daireye gidebilir ve daha sonra ileri gidebilir veya sağa dönüp aynı şeyi yapabilirsiniz.
Şekil 5'te en kısa yol açıkça alttaki yeşil çizgidir. Bu yol, Şekil 6'da gösterilen bazı geometrik ilişkilerden dolayı hesaplamak için oldukça basittir.
İlk önce dönüş çemberimizin merkezi olan ve her zaman başlangıç noktasından yarıçapı r olan P noktasının konumunu hesaplıyoruz. Başlangıç yönümüzden sağa dönersek, bu P'nin başlangıç noktasından (başlangıç_dizini - 90) bir açıda olduğu anlamına gelir, yani:
angleToP = initial_direction - 90
P.x = Origin.x + r * cos(angleToP)
P.y = Origin.y + r * sin(angleToP)
Artık merkez nokta P'nin yerini bildiğimize göre, şemada h olarak gösterilen P ile hedefe olan mesafeyi hesaplayabiliriz:
dx = Destination.x - P.x
dy = Destination.y - P.y
h = sqrt(dx*dx + dy*dy)
Bu noktada, hedefin daire içinde olmadığını kontrol etmek istiyoruz, çünkü öyleyse, asla ulaşamayız:
if (h < r)
return false
Şimdi d segmentinin uzunluğunu hesaplayabiliriz, çünkü sağ üçgenin diğer iki tarafının uzunluklarını zaten biliyoruz, yani h ve r. Sağ üçgen ilişkisinden açıyı da belirleyebiliriz:
d = sqrt(h*h - r*r)
theta = arccos(r / h)
Son olarak, daireyi terk edip düz çizgide başlamak için Q noktasını bulmak için toplam açıyı + bilmemiz gerekir ve P'den hedefe açı olarak kolayca belirlenir:
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)
Yukarıdaki hesaplamalar sağa dönüş yolunu temsil eder. Soldaki yol tam olarak aynı şekilde hesaplanabilir, ancak angleToP'yi hesaplamak için initial_direction öğesine 90 ekleriz ve daha sonra + yerine - kullanırız. Her ikisini de hesapladıktan sonra, hangi yolun daha kısa olduğunu görüyoruz ve bunu kullanıyoruz.
Bu algoritmayı ve onu takip edenleri uygulamamızda, her biri ya düz ya da kavisli olmak üzere en fazla dört farklı "çizgi parçası" saklayan bir veri yapısı kullanıyoruz. Burada açıklanan kavisli yollar için sadece iki segment kullanılır: bir ark ve ardından düz bir çizgi. Veri yapısı, parçanın bir yay mı yoksa düz bir çizgi mi olduğunu, parçanın uzunluğunu ve başlangıç konumunu belirten üyeler içerir. Segment düz bir çizgi ise, veri yapısı da açıyı belirtir; yaylar için dairenin merkezini, dairenin başlangıç açısını ve yayın tarafından kapsanan toplam radyanı belirtir.
İki nokta arasında almak için gerekli olan eğri yolu hesapladıktan sonra, Liste 2'de gösterildiği gibi, konumumuzu ve yönümüzü herhangi bir anda kolayca hesaplayabiliriz.
LISTING 2. Belirli bir zamanda konumun ve yönün hesaplanması.
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