La réponse est en fait assez simple si vous faites le calcul. Vous avez une distance fixe de Y et une distance variable de X (voir image 1). Vous devez trouver l'angle entre Z et X et tourner encore plus votre tourelle.
Étape 1 - Obtenez la distance entre la ligne de tourelle (V) et la ligne de canon (W) qui est Y (c'est constant mais cela ne fait pas de mal à calculer). Obtenez la distance de la tourelle à la cible (qui est X).
Étape 2 - Divisez Y par X, puis obtenez le sinus hyperbolique de la valeur
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.
Étape 3 - Tournez beaucoup plus la tourelle (autour de l'axe qui va de haut en bas, probablement vers le haut, mais vous seul pouvez connaître cette partie).
gameObject.transform.Rotate(Vector3.up, turnAngle);
Bien sûr, dans ce cas, vous en avez besoin pour tourner dans le sens antihoraire, vous devrez peut-être ajouter un moins devant le turnAngle là, comme dans -turnAngle
.
Modifié certaines parties. Merci à @ens d'avoir souligné la différence de distance.
L'OP a déclaré que son arme avait un angle alors nous y allons, l'image d'abord, l'explication plus tard:
Nous savons déjà du calcul précédent où viser la ligne rouge en fonction de la ligne bleue. Donc, visons d'abord la ligne bleue:
float turnAngle = angleBetweenTurretAndTarget - angleBetweenTurretAndGun;
turret.transform.Rotate(Vector3.up, turnAngle);
Le seul calcul qui diffère ici est le calcul de "X Prime" (X ') parce que l'angle entre le canon et la tourelle (angle "a") a changé la distance entre les lignes.
//(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);
Cette partie suivante est UNIQUEMENT nécessaire si vous faites des pistolets à tourelle modulaires (c.-à-d. L'utilisateur peut changer les pistolets sur une tourelle et différents pistolets ont des angles différents). Si vous faites cela dans l'éditeur, vous pouvez déjà voir quel est l'angle du canon selon la tourelle.
Il existe deux méthodes pour trouver l'angle "a", l'une est la méthode transform.up:
float angleBetween = Vector3.Angle(turret.transform.up, gun.transform.up);
La technique ci-dessus calculera en 3D, donc si vous voulez un résultat 2D, vous devez vous débarrasser de l'axe Z (c'est ce que je suppose où se trouve la gravité, mais si vous n'avez rien changé, dans Unity, c'est l'axe Y qui est vers le haut ou vers le bas, c'est-à-dire que la gravité est sur l'axe Y, vous devrez donc peut-être changer les choses):
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 deuxième façon est la méthode de rotation (je pense en 2D dans ce cas):
double angleRadians = Mathf.Asin(turret.transform.rotation.z - gun.transform.rotation.z);
double angle = 2 * Mathf.Rad2Deg * angleRadians;
Encore une fois, tous ces codes vous donneront des valeurs positives, vous devrez donc peut-être ajouter ou soustraire le montant en fonction de l'angle (il y a des calculs pour cela aussi, mais je ne vais pas aller plus loin). Un bon point de départ serait leVector2.Dot
méthode dans Unity.
Dernier bloc de code pour une explication supplémentaire de ce que nous faisons:
//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 vous avez tout fait correctement, vous devriez obtenir une scène comme celle-ci ( lien pour le package d'unité ):
Ce que je veux dire par des valeurs toujours positives:
La méthode Z peut donner des valeurs négatives:
Pour un exemple de scène, obtenez le package d'unité à partir de ce lien .
Voici le code que j'ai utilisé dans la scène (sur la tourelle):
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.
}
}
Code adapté en 3D avec X et Z comme plan 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.
}
}