Primeiro, você precisa determinar a diferença de ângulo entre a direção da torre e a direção do alvo.
Vector2 turretToTarget = target.position - turret.position;
float desiredAngle = atan2(turretToTarget.y, turretToTarget.x);
float angleDiff = desiredAngle - turret.angle;
// Normalize angle to [-PI,PI] range. This ensures that the turret
// turns the shortest way.
while (angleDiff < -PI) angleDiff += 2*PI;
while (angleDiff >= PI) angleDiff -= 2*PI;
Depois de ter essas quantidades, você pode configurar uma expressão de segundo grau para o ângulo da torre. Você precisa calcular isso em cada atualização para garantir sempre o uso dos dados mais recentes de posições e velocidades.
// Compute angular acceleration.
const float C0 = // Must be determined.
const float C1 = // Must be determined.
float angularAcc = C0 * angleDiff - C1 * turret.angularVelocity;
Aqui, o primeiro termo (grau zero) na expressão de aceleração fará com que a torre comece a girar em direção ao alvo. No entanto, ele não para no tempo, mas oscila de um lado para o outro. Para fazê-lo parar, precisamos do amortecimento do segundo termo (primeiro grau), que faz com que uma alta velocidade de rotação seja oposta a uma alta aceleração.
Agora, as constantes positivas (não necessariamente constantes do programa) precisam ser determinadas e equilibradas para que o sistema se comporte bem. C0
é o principal controle da velocidade do sistema. Um valor alto para C0
dará uma velocidade de rotação rápida e um valor baixo dará uma velocidade de rotação baixa. O valor real depende de muitos fatores; portanto, você deve usar tentativa e erro aqui. C1
controla a magnitude do amortecimento. O discriminante da equação quadrática nos diz que se C1*C1 - 4*C0 >= 0
temos um sistema não-oscilante.
// New definition.
const float C1 = 2*sqrt(C0); // Stabilizes the system.
Você provavelmente deve escolher C1
um pouco maior que isso por razões numéricas, mas não muito grande, pois pode ficar muito úmido e lento para responder. Novamente, você precisa ajustar.
Também é importante observar que esse código calcula apenas a aceleração angular. O ângulo e a velocidade angular precisam ser atualizados a partir disso em outro lugar, usando algum tipo de integrador. Da pergunta, presumo que isso tenha sido coberto.
Finalmente, há algo a dizer sobre o atraso, porque a torre provavelmente sempre estará para trás ao rastrear um alvo rápido. Uma maneira simples de resolver isso é adicionar uma previsão linear à posição do alvo, ou seja, sempre apontar um pouco à frente na direção para frente do alvo.
// Improvement of the first lines above.
const float predictionTime = 1; // One second prediction, you need to experiment.
Vector2 turretToTarget = target.position + predictionTime * target.velocity - turret.position;
/// ...
Quanto a manter a torre apontada dentro do raio do alvo por algum tempo, pode ser um requisito difícil de impor diretamente a esse tipo de sistema. Você pode ter certeza de que este controlador se esforçará para manter a torre apontada para o alvo (ou melhor, para a posição prevista) o tempo todo. Se o resultado acaba por não ser satisfatório você tem que modificar os parâmetros predictionTime
, C0
e C1
(dentro de limites estáveis).