Agregar vueltas realistas
El siguiente paso es agregar giros curvos realistas para nuestras unidades, de modo que no parezcan cambiar de dirección abruptamente cada vez que necesiten girar. Una solución simple implica el uso de una spline para suavizar las esquinas abruptas en giros. Si bien esto resuelve algunas de las preocupaciones estéticas, aún resulta en un movimiento físicamente muy poco realista para la mayoría de las unidades. Por ejemplo, podría cambiar una curva abrupta de un tanque en una curva cerrada, pero el giro curvo aún sería mucho más apretado de lo que realmente podría realizar el tanque.
Para una mejor solución, lo primero que debemos saber es el radio de giro de nuestra unidad. El radio de giro es un concepto bastante simple: si está en un estacionamiento grande en su automóvil, y gira la rueda hacia la izquierda todo lo que pueda y procede a conducir en un círculo, el radio de ese círculo es su giro radio. El radio de giro de un Volkswagen Beetle será sustancialmente menor que el de un SUV grande, y el radio de giro de una persona será sustancialmente menor que el de un oso grande y pesado.
Digamos que está en algún punto (origen) y apuntado en una determinada dirección, y necesita llegar a otro punto (destino), como se ilustra en la Figura 5. El camino más corto se encuentra girando a la izquierda todo lo que pueda puede hacerlo, yendo en círculo hasta que apunte directamente al destino, y luego avanzando, o girando a la derecha y haciendo lo mismo.
En la Figura 5, la ruta más corta es claramente la línea verde en la parte inferior. Este camino resulta bastante sencillo de calcular debido a algunas relaciones geométricas, ilustradas en la Figura 6.
Primero calculamos la ubicación del punto P, que es el centro de nuestro círculo de giro, y siempre está a un radio de distancia del punto de partida. Si estamos girando a la derecha desde nuestra dirección inicial, eso significa que P está en un ángulo de (initial_direction - 90) desde el origen, entonces:
angleToP = initial_direction - 90
P.x = Origin.x + r * cos(angleToP)
P.y = Origin.y + r * sin(angleToP)
Ahora que conocemos la ubicación del punto central P, podemos calcular la distancia desde P hasta el destino, que se muestra como h en el diagrama:
dx = Destination.x - P.x
dy = Destination.y - P.y
h = sqrt(dx*dx + dy*dy)
En este punto, también queremos verificar que el destino no esté dentro del círculo, porque si lo fuera, nunca podríamos alcanzarlo:
if (h < r)
return false
Ahora podemos calcular la longitud del segmento d, ya que ya conocemos las longitudes de los otros dos lados del triángulo rectángulo, a saber, h y r. También podemos determinar el ángulo a partir de la relación triángulo rectángulo:
d = sqrt(h*h - r*r)
theta = arccos(r / h)
Finalmente, para descubrir el punto Q en el que salir del círculo y comenzar en la línea recta, necesitamos conocer el ángulo total +, y se determina fácilmente como el ángulo desde P hasta el destino:
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)
Los cálculos anteriores representan la ruta de giro a la derecha. La ruta de la izquierda se puede calcular exactamente de la misma manera, excepto que agregamos 90 a initial_direction para calcular angleToP, y luego usamos - en lugar de +. Después de calcular ambos, simplemente vemos qué camino es más corto y usamos ese.
En nuestra implementación de este algoritmo y los siguientes, utilizamos una estructura de datos que almacena hasta cuatro "segmentos de línea" distintos, cada uno de los cuales es recto o curvo. Para las rutas curvas descritas aquí, solo se utilizan dos segmentos: un arco seguido de una línea recta. La estructura de datos contiene miembros que especifican si el segmento es un arco o una línea recta, la longitud del segmento y su posición inicial. Si el segmento es una línea recta, la estructura de datos también especifica el ángulo; para los arcos, especifica el centro del círculo, el ángulo inicial del círculo y el total de radianes cubiertos por el arco.
Una vez que hemos calculado la ruta curva necesaria para llegar entre dos puntos, podemos calcular fácilmente nuestra posición y dirección en cualquier instante dado, como se muestra en el Listado 2.
LISTADO 2. Cálculo de la posición y orientación en un momento particular.
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