Odpowiedź jest dość prosta, jeśli wykonasz matematykę. Masz ustaloną odległość Y i zmienną odległość X (patrz zdjęcie 1). Musisz znaleźć kąt między Z i X i jeszcze bardziej obrócić wieżę.
Krok 1 - Uzyskaj odległość między linią wieży (V) a linią dział (W), która ma wartość Y (jest stała, ale nie boli, aby ją obliczyć). Uzyskaj odległość od wieży do celu (czyli X).
Krok 2 - Podziel Y przez X, a następnie uzyskaj sinus hiperboliczny wartości
double turnRadians = Mathf.Asin(Y/X);
double angle = Mathf.Rad2Deg * turnRadians;
//where B is the red dot, A is a point on the X line and C is a point on the Z line.
Krok 3 - Obróć wieżę jeszcze bardziej (wokół osi, która biegnie od góry do dołu, najprawdopodobniej w górę, ale tylko ty możesz poznać tę część).
gameObject.transform.Rotate(Vector3.up, turnAngle);
Oczywiście w tym przypadku trzeba go obrócić przeciwnie do ruchu wskazówek zegara, więc może być konieczne dodanie minus przed zakrętem tam, jak w -turnAngle
.
Edytowałem niektóre części. Dzięki @ens za wskazanie różnicy odległości.
OP powiedział, że jego pistolet ma kąt, więc proszę, najpierw zdjęcie, wyjaśnienie później:
Wiemy już z poprzednich obliczeń, gdzie celować czerwoną linią zgodnie z niebieską linią. Celując najpierw w niebieską linię:
float turnAngle = angleBetweenTurretAndTarget - angleBetweenTurretAndGun;
turret.transform.Rotate(Vector3.up, turnAngle);
Jedyne obliczenie, które się tutaj różni, to obliczenie „X Prime” (X '), ponieważ kąt między działem a wieżą (kąt „a”) zmienił odległość między liniami.
//(this part had a mistake of using previous code inside new variable names, YPrime and Y are shown as X' and X in the 2nd picture.
float YPrime = Cos(a)*Y; //this part is what @ens is doing in his answer
double turnRadians = Mathf.Asin(YPrime/X);
double angle = Mathf.Rad2Deg * turnRadians;
turret.transform.Rotate(Vector3.up, angle);
Ta kolejna część jest konieczna WYŁĄCZNIE, jeśli wykonujesz broń modułową (tzn. Użytkownik może zmieniać broń na wieży, a różne działa mają różne kąty). Jeśli robisz to w edytorze, możesz już zobaczyć, jaki jest kąt strzału w zależności od wieży.
Istnieją dwie metody znajdowania kąta „a”, jedna to metoda transform.up:
float angleBetween = Vector3.Angle(turret.transform.up, gun.transform.up);
Powyższa technika będzie obliczana w 3D, więc jeśli chcesz uzyskać wynik 2D, musisz pozbyć się osi Z (to jest to, co zakładam, gdzie jest grawitacja, ale jeśli nic nie zmieniłeś, w Unity to oś Y, która jest w górę lub w dół, tj. grawitacja znajduje się na osi Y, więc może być konieczna zmiana rzeczy):
Vector2 turretVector = new Vector2(turret.transform.up.x, turret.transform.up.y);
Vector2 gunVector = new Vector2(gun.transform.up.x, gun.transform.up.y);
float angleBetween = Vector2.Angle(turretVector, gunVector);
Drugi sposób to metoda rotacji (w tym przypadku myślę w 2D):
double angleRadians = Mathf.Asin(turret.transform.rotation.z - gun.transform.rotation.z);
double angle = 2 * Mathf.Rad2Deg * angleRadians;
Ponownie, wszystkie te kody podadzą ci wartości, które są dodatnie, więc być może będziesz musiał dodać lub odjąć kwotę w zależności od kąta (są też obliczenia, ale nie zamierzam zagłębiać się tak dokładnie). Dobrym miejscem do rozpoczęcia tego jest Vector2.Dot
metoda w Unity.
Ostatni blok kodu dla dodatkowego wyjaśnienia tego, co robimy:
//turn turret towards target
turretTransform.up = targetTransform.position - turretTransform.position;
//adjust for gun angle
if (weaponTransform.localEulerAngles.z <180) //if the value is over 180 it's actually a negative for us
turretTransform.Rotate(Vector3.forward, 90 - b - a);
else
turretTransform.Rotate(Vector3.forward, 90 - b + a);
Jeśli zrobiłeś wszystko dobrze, powinieneś dostać taką scenę ( link do pakietu unity ):
Co rozumiem przez zawsze pozytywne wartości:
Metoda Z może dawać wartości ujemne:
Na przykład, pobierz pakiet unity z tego linku .
Oto kod, którego użyłem w scenie (na wieży):
public class TurretAimCorrection : MonoBehaviour
{
public Transform targetTransform;
public Transform turretTransform;
public Transform weaponTransform;
private float f, d, x, y, h, b, a, weaponAngle, turnAngle;
private void Start()
{
TurnCorrection();
}
private void Update()
{
TurnCorrection();
}
void TurnCorrection()
{
//find distances and angles
d = Vector2.Distance(new Vector2(targetTransform.position.x, targetTransform.position.y), new Vector2(turretTransform.position.x, turretTransform.position.y));
x = Vector2.Distance(new Vector2(turretTransform.position.x, turretTransform.position.y), new Vector2(weaponTransform.position.x, weaponTransform.position.y));
weaponAngle = weaponTransform.localEulerAngles.z;
weaponAngle = weaponAngle * Mathf.Deg2Rad;
y = Mathf.Abs(Mathf.Cos(weaponAngle) * x);
b = Mathf.Rad2Deg * Mathf.Acos(y / d);
a = Mathf.Rad2Deg * Mathf.Acos(y / x);
//turn turret towards target
turretTransform.up = targetTransform.position - turretTransform.position;
//adjust for gun angle
if (weaponTransform.localEulerAngles.z < 180)
turretTransform.Rotate(Vector3.forward, 90 - b - a);
else
turretTransform.Rotate(Vector3.forward, 90 - b + a);
//Please leave this comment in the code. This code was made by
//http://gamedev.stackexchange.com/users/93538/john-hamilton a.k.a. CrazyIvanTR.
//This code is provided as is, with no guarantees. It has worked in local tests on Unity 5.5.0f3.
}
}
Dostosowany kod 3D z X i Z jako płaszczyzną 2D:
public class TurretAimCorrection : MonoBehaviour
{
public Transform targetTransform; //drag target here
public Transform turretTransform; //drag turret base or turret top part here
public Transform weaponTransform; //drag the attached weapon here
private float d, x, y, b, a, weaponAngle, turnAngle;
private void Start()
{
TurnAdjustment();
}
private void Update()
{
TurnAdjustment();
}
void TurnAdjustment()
{
d = Vector2.Distance(new Vector2(targetTransform.position.x, targetTransform.position.z), new Vector2(turretTransform.position.x, turretTransform.position.z));
x = Vector2.Distance(new Vector2(turretTransform.position.x, turretTransform.position.z), new Vector2(weaponTransform.position.x, weaponTransform.position.z));
weaponAngle = weaponTransform.localEulerAngles.y;
weaponAngle = weaponAngle * Mathf.Deg2Rad;
y = Mathf.Abs(Mathf.Cos(weaponAngle) * x);
b = Mathf.Rad2Deg * Mathf.Acos(y / d);
a = Mathf.Rad2Deg * Mathf.Acos(y / x);
//turn turret towards target
turretTransform.forward = new Vector3(targetTransform.position.x, 0, targetTransform.position.z) - new Vector3(turretTransform.position.x, 0, turretTransform.position.z);
//adjust for gun angle
if (weaponTransform.localEulerAngles.y < 180)
turretTransform.Rotate(Vector3.up, - a +b-90);
else
turretTransform.Rotate(Vector3.up, + a+ b - 90);
//Please leave this comment in the code. This code was made by
//http://gamedev.stackexchange.com/users/93538/john-hamilton a.k.a. CrazyIvanTR.
//This code is provided as is, with no guarantees. It has worked in local tests on Unity 5.5.0f3.
}
}