http://download.jikexueyuan.com/detail/id/432.html
弹道计算是游戏里常见的问题,其中关于击中移动目标的自动计算提前量的话题,看似简单,其实还是挺复杂的数学。网上这方面的资料还真不多,而且都是写的含含糊糊。抽空总结一下自己的方法。
讨论的前提是,假设目标是在3D空间里以匀速直线方式运动。
1.直线弹道
在不考虑重力和空气阻力影响的情况下,子弹的弹道呈直线运动。这种情况下,其实是个纯平面几何空间的问题,不需要微积分和线代知识。
分析的情况如下图:
虽然在3D空间飞行,但火炮命中时,命中点和火炮位置、飞机初始位置处于一个三角形上,只需要平面几何知识就能解决问题。在这个三角形中,飞机起始位置P和火炮T的位置是确定的,飞机的飞行方向也是确定的,所以θ角是已知的,D的长度也是已知的,F和G的长度虽然不知道,但在命中点H相遇的时候经过的时间t都是一样的,所以F/G的比例实际等于两者速度的比例,而两者的速度都是已知的。这样就可以用高中的余弦公式来解决这个求边长的问题:
其中V_p和V_g分别代表飞机的速度和炮弹飞行的速度,这是一个标准的1元2次方程,化简、消元对某这么一个非数学专业的来说太麻烦了,直接用求根公式求解吧,有好的化简方法请指教。
在Unity中实现的方法:
Vector3 hitPoint = Vector3.zero;//存放命中点坐标 //假设飞机物体是aircraft,炮塔物体是gun 两者间的方向向量就是两种世界坐标相减 Vector3 D = gun.transform.position - aircraft.transform.position; //用飞机transform的TransformDirection方法把前进方向变换到世界坐标,就是飞机飞行的世界方向向量了 Vector3 aircraftDirection = aircraft.transform.TransformDirection(Vector3.foward); //再用Vector3.Angle方法求出与飞机前进方向之间的夹角 float THETA = Vector3.Angle(D,aircraftDirection); float DD = D.magnitude;//D是飞机炮塔间方向向量,D的magnitued就是两种间距离 float A =1-Mathf.Pow((gunVelocity/aircraftVelocity),2);//假设炮弹的速度是gunVeloctiy飞机的飞行线速度是aircraftVeloctiy float B = -(2*DD*Mathf.Cos(THETA**Mathf.Deg2Rad));//要变换成弧度 float C = DD*DD; float DELTA = B*B-4*A*C; if (DELTA>=0){//如果DELTA小于0,无解 float F1 = (-B+Mathf.Sqrt(B*B-4*A*C))/(2*A); float F2 = (-B-Mathf.Sqrt(B*B-4*A*C))/(2*A); if(F1<F2)//取较小的一个 F1 = F2; //命中点位置等于 飞机初始位置加上计算出F边长度乘以飞机前进的方向向量,这个乘法等于把前进的距离变换成世界坐标的位移 hitPoint = aircraft.transform.position + aircraftDirection * F1; }