如果进行数学运算,答案实际上很简单。您有一个固定的Y距离和一个可变的X距离(见图1)。您需要找出Z和X之间的角度,并进一步旋转炮塔。
第1步-获得炮塔线(V)和炮线(W)之间的距离,该距离为Y(这是常数,但计算时不会受到伤害)。获取转塔到目标的距离(X)。
步骤2-将Y除以X,然后获得该值的双曲正弦值
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.
第3步-进一步旋转刀塔(围绕从顶部到底部的轴,最有可能是向上轴,但只有您知道那部分)。
gameObject.transform.Rotate(Vector3.up, turnAngle);
当然,在这种情况下,您需要将其逆时针旋转,因此您可能需要在turnAngle的前面添加一个负号,如中所示-turnAngle
。
编辑了一些部分。感谢@ens指出距离的差异。
OP说他的枪有一个角度,所以我们开始,首先是图像,然后是解释:
从前面的计算中我们已经知道将红线对准蓝线的位置。因此,首先瞄准蓝线:
float turnAngle = angleBetweenTurretAndTarget - angleBetweenTurretAndGun;
turret.transform.Rotate(Vector3.up, turnAngle);
此处唯一不同的计算是“ X Prime”(X')的计算,因为喷枪和炮塔之间的角度(角度“ a”)改变了线之间的距离。
//(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);
如果您要对炮塔模块进行模块化,则只有下一部分是必需的(即,用户可以在炮塔上更换喷枪,并且不同的喷枪具有不同的角度)。如果在编辑器中执行此操作,您已经可以看到根据炮塔角度的枪支角度。
有两种找到角度“ a”的方法,一种是transform.up方法:
float angleBetween = Vector3.Angle(turret.transform.up, gun.transform.up);
上面的技术将以3D进行计算,因此,如果要获得2D结果,则需要摆脱Z轴(这是我假设重力所在的位置,但是如果您不进行任何更改,则在Unity中,Y轴是向上还是向下,也就是说,重力在Y轴上,因此您可能必须进行更改):
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);
第二种方法是旋转方法(在这种情况下,我正在考虑2D):
double angleRadians = Mathf.Asin(turret.transform.rotation.z - gun.transform.rotation.z);
double angle = 2 * Mathf.Rad2Deg * angleRadians;
同样,所有这些代码都将为您提供正值,因此您可能必须根据角度来增加或减少该数量(也有相应的计算方法,但我不会对此进行深入介绍)。一个很好的起点是Vector2.Dot
Unity中的方法。
最后的代码块,用于进一步说明我们在做什么:
//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);
如果您做对了所有事情,那么应该会得到这样的场景(unitypackage的链接):
我的意思是始终为正值:
Z方法可以给出负值:
对于示例场景,请从此链接获取unitypackage。
这是我在场景中(转塔上)使用的代码:
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.
}
}
X和Z为2D平面的3D适应代码:
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.
}
}