La respuesta es bastante fácil si haces los cálculos. Tiene una distancia fija de Y y una distancia variable de X (Ver Imagen 1). Necesitas encontrar el ángulo entre Z y X y girar tu torreta mucho más.
Paso 1: obtenga la distancia entre la línea de la torreta (V) y la línea de la pistola (W) que es Y (esto es constante pero no hace daño calcular). Obtenga la distancia de la torreta al objetivo (que es X).
Paso 2: divida Y entre X y luego obtenga el seno hiperbólico del valor
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.
Paso 3: gire la torreta mucho más (alrededor del eje que va de arriba hacia abajo, probablemente hacia arriba pero solo usted puede conocer esa parte).
gameObject.transform.Rotate(Vector3.up, turnAngle);
Por supuesto, en este caso, es necesario que gire en sentido antihorario, por lo que es posible que deba agregar un signo menos delante del ángulo de giro allí, como en -turnAngle
.
Editado algunas partes. Gracias a @ens por señalar la diferencia en la distancia.
El OP dijo que su arma tiene un ángulo, así que aquí vamos, imagen primero, explicación después:
Ya sabemos del cálculo anterior dónde apuntar la línea roja de acuerdo con la línea azul. Apuntando primero a la línea azul:
float turnAngle = angleBetweenTurretAndTarget - angleBetweenTurretAndGun;
turret.transform.Rotate(Vector3.up, turnAngle);
El único cálculo que difiere aquí es el cálculo de "X Prime" (X ') porque el ángulo entre la pistola y la torreta (ángulo "a") cambió la distancia entre las líneas.
//(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);
La siguiente parte SÓLO es necesaria si está haciendo las pistolas de torreta modulares (es decir, el usuario puede cambiar las pistolas en una torreta y diferentes pistolas tienen ángulos diferentes). Si está haciendo esto en el editor, ya puede ver cuál es el ángulo de la pistola según la torreta.
Hay dos métodos para encontrar el ángulo "a", uno es el método transform.up:
float angleBetween = Vector3.Angle(turret.transform.up, gun.transform.up);
La técnica anterior se calculará en 3D, por lo que si desea un resultado en 2D, debe deshacerse del eje Z (eso es lo que supongo donde está la gravedad, pero si no cambió nada, en Unity es el eje Y que está arriba o abajo, es decir, la gravedad está en el eje Y, por lo que puede que tenga que cambiar las cosas)
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);
La segunda forma es el método de rotación (estoy pensando en 2D en este caso):
double angleRadians = Mathf.Asin(turret.transform.rotation.z - gun.transform.rotation.z);
double angle = 2 * Mathf.Rad2Deg * angleRadians;
Nuevamente, todos estos códigos le darán valores que son positivos, por lo que es posible que tenga que sumar o restar la cantidad dependiendo del ángulo (también hay cálculos para eso, pero no voy a profundizar en eso). Un buen lugar para comenzar con esto sería el Vector2.Dot
método en Unity.
Bloque final de código para una explicación adicional de lo que estamos haciendo:
//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);
Si hiciste todo bien, deberías obtener una escena como esta ( enlace para el paquete de la unidad ):
lo que quiero decir con valores siempre positivos:
El método Z puede dar valores negativos:
Para una escena de ejemplo, obtenga el paquete de la unidad desde este enlace .
Aquí está el código que he usado en la escena (en la torreta):
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.
}
}
Código adaptado 3D con X y Z como plano 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.
}
}