Najpierw musisz określić różnicę kąta między kierunkiem kierunku wieży a kierunkiem do celu.
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;
Po uzyskaniu tych wielkości można ustawić wyrażenie drugiego stopnia dla kąta wieży. Musisz to obliczyć przy każdej aktualizacji, aby mieć pewność, że zawsze korzystasz z najnowszych danych pozycji i prędkości.
// Compute angular acceleration.
const float C0 = // Must be determined.
const float C1 = // Must be determined.
float angularAcc = C0 * angleDiff - C1 * turret.angularVelocity;
Tutaj pierwszy wyraz (zero stopni) w wyrażeniu przyspieszenia spowoduje, że wieża zacznie się obracać w kierunku celu. Jednak nie zatrzyma się w czasie, a raczej oscyluje wokół niego. Aby się zatrzymać, potrzebujemy drugiego członu tłumienia (pierwszego stopnia), który powoduje, że wysokiej prędkości obrotu przeciwstawia się duże przyspieszenie.
Teraz stałe dodatnie (niekoniecznie stałe programowe) muszą zostać określone i zrównoważone, aby system zachowywał się dobrze. C0
to główna kontrola prędkości systemu. Wysoka wartość dla C0
daje dużą prędkość obrotową, a niska wartość daje niską prędkość obrotową. Rzeczywista wartość zależy od wielu czynników, dlatego powinieneś użyć tutaj metody prób i błędów. C1
kontroluje wielkość tłumienia. Wyróżnik równania kwadratowego mówi nam, że jeśli C1*C1 - 4*C0 >= 0
mamy system non-oscylacyjny.
// New definition.
const float C1 = 2*sqrt(C0); // Stabilizes the system.
Prawdopodobnie powinieneś wybrać C1
nieco większy niż ten ze względów liczbowych, ale nie za duży, ponieważ może być bardzo nadmiernie tłumiony i wolno reagować. Ponownie musisz dostosować.
Należy również pamiętać, że ten kod oblicza tylko przyspieszenie kątowe. Kąt i prędkość kątowa muszą zostać zaktualizowane z tego miejsca gdzieś indziej, przy użyciu i pewnego rodzaju integratora. Z pytania zakładam, że zostało to uwzględnione.
Wreszcie jest coś do powiedzenia na temat opóźnień, ponieważ wieża prawdopodobnie zawsze będzie w tyle, gdy śledzi szybki cel. Prostym sposobem na rozwiązanie tego problemu jest dodanie liniowej prognozy do pozycji celu, tj. Zawsze celowanie lekko do przodu w kierunku do przodu celu.
// 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;
/// ...
Co do utrzymywania przez pewien czas wieży w promieniu celu, może to być trudny wymóg nałożenia bezpośrednio na tego rodzaju system. Możesz być pewien, że ten kontroler będzie dążył do tego, aby wieżyczka cały czas była wycelowana w cel (a raczej przewidywaną pozycję). Jeśli wynik okaże się nie być zadowalająca trzeba zmodyfikować parametry predictionTime
, C0
i C1
(w ramach stabilnych granicach).