最近在写一个空战类游戏,游戏中需要用到3D场景中的真实跟踪导弹模拟。
找了好多相关的代码和demo,但不是有缺陷就是逻辑涉及大量三角函数运算让我这个数学渣无法忍受。
正在我苦于无法解决的时候一篇关于导弹技术的文章给了我灵感。
具体过程我就不说了,还是直接说思路与代码实现。
首先,我们要知道,导弹的跟踪实际上是由两部分组成的,即推进力与扭力。
推进力我们可以通过移动模拟,所以让大部分人困扰最多的实际上是扭力。
而真实的导弹常会因为扭力不足无法命中目标,这就是真实导弹模拟的关键所在。
我们知道,UNITY为我们提供了非常方便的lookat函数,但lookat只能实时的瞄准对方,这会使导弹一定能命中。
那么关键点来了。我们不用导弹的弹体lookat目标,而是在导弹中建立一个子物体,让子物体lookat目标。我把这个子物体称为目
标指向器(伪陀螺仪)。
子物体朝向目标后会给我们提供旋转角度的数据,而我们只需要用MoveTowards以一定的速度让弹体的旋转角度不断迫近这个角度
就行了。
当然这会出现一个问题,欧拉角在超过360时会报错,还有就是当目标指向器的角度从360变到1时会出现bug,但这都很好解决,
只是简单的追击问题罢了。
这个问题解决后,我又开始考虑各角度的扭力分配,以保证导弹已最圆滑的曲线追击目标,最后完成的代码大概是这样。
/// <summary>
/// 2017.07.07
/// zcl1001zcl
/// 莲風
/// </summary>
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TrackingControl : MonoBehaviour
{
public float Torsion = 30.0f;
public float Speed = 5.0f;
public GameObject m_Gyroscope; //目标指向器(伪陀螺仪)
public GameObject Target; //目标
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
void FixedUpdate()
{
//(解释)让指向器指向目标
m_Gyroscope.transform.LookAt(Target.transform) ;
//(解释)获得相关指向器欧拉角数据和自身的欧拉角数据
Vector3 TrackingMissileEuler = transform.eulerAngles;
Vector3 GyroscopeEuler = m_Gyroscope.transform.eulerAngles;
Vector3 NewTrackingMissileEuler = Vector3.zero;
Vector3 NewGyroscopeEuler = Vector3.zero;
Detection(TrackingMissileEuler, GyroscopeEuler, out NewTrackingMissileEuler, out NewGyroscopeEuler);
float TorsionX = 0;
float TorsionY = 0;
float TorsionZ = 0;
TorqueDecomposition(NewTrackingMissileEuler, NewGyroscopeEuler, out TorsionX, out TorsionY, out TorsionZ);
float eulerX = Mathf.MoveTowards(NewTrackingMissileEuler.x, NewGyroscopeEuler.x , TorsionX);
float eulerY = Mathf.MoveTowards(NewTrackingMissileEuler.y, NewGyroscopeEuler.y , TorsionY);
float eulerZ = Mathf.MoveTowards(NewTrackingMissileEuler.z, NewGyroscopeEuler.z , TorsionZ);
//(解释)%360,防止超范围
transform.localEulerAngles = new Vector3(eulerX%360, eulerY%360, eulerZ%360);
//(解释)角度调整结束后,让导弹延自身的z轴移动
transform.Translate(new Vector3( 0, 0, Speed * Time.fixedDeltaTime));
}
//(解释)对3对欧拉角做计算,得出3个角之间所占比例
void TorqueDecomposition(Vector3 TrackingMissileEuler,Vector3 GyroscopeEuler ,out float TorsionX ,out float TorsionY, out float TorsionZ)
{
//(解释)实际上在lookat的过程中,z轴是无变化的,所以这里的Z轴只是用来保险用的
//(解释)另外,如果需要对Z轴做特殊处理(比如转弯时弹体倾斜)等也应该在这里进行,只不过需要自己重写z轴相关的代码
float eulerX = Mathf.Abs(GyroscopeEuler.x - TrackingMissileEuler.x);
float eulerY = Mathf.Abs(GyroscopeEuler.y - TrackingMissileEuler.y);
float eulerZ = Mathf.Abs(GyroscopeEuler.z - TrackingMissileEuler.z);
if ((eulerX + eulerY + eulerZ) > 0)
{
TorsionX = (eulerX / (eulerX + eulerY + eulerZ)) * Torsion * Time.fixedDeltaTime;
TorsionY = (eulerY / (eulerX + eulerY + eulerZ)) * Torsion * Time.fixedDeltaTime;
TorsionZ = (eulerZ / (eulerX + eulerY + eulerZ)) * Torsion * Time.fixedDeltaTime;
}
else
{
//(解释)这里实际上也只是个保险而已,因为如果eulerX + eulerY + eulerZ <=0(实际上不可能出现<0的情况)的话
//(解释)代表以完成方向调整,那扭力分配就没有意义了。
//(解释)另外,因为在追击过程中z轴不参与扭力,所以实际上这里应该是
//(解释)TorsionX = Torsion / 2.0f;
//(解释)TorsionY = Torsion / 2.0f;
//(解释)TorsionZ = 0;
//(解释)不过反正应该也对结果没任何影响,所以就懒得改了
TorsionX = Torsion / 3.0f;
TorsionY = Torsion / 3.0f;
TorsionZ = Torsion / 3.0f;
}
}
//(解释)该函数检测是否被套圈(即差>180度)
//(解释)如果差>180,那我们需要在小的那个数上+360
void Detection(Vector3 TrackingMissileEuler, Vector3 GyroscopeEuler, out Vector3 NewTrackingMissileEuler, out Vector3 NewGyroscopeEuler)
{
NewTrackingMissileEuler = Vector3.zero;
NewGyroscopeEuler = Vector3.zero;
Compare(TrackingMissileEuler.x, GyroscopeEuler.x, out NewTrackingMissileEuler.x, out NewGyroscopeEuler.x);
Compare(TrackingMissileEuler.y, GyroscopeEuler.y, out NewTrackingMissileEuler.y, out NewGyroscopeEuler.y);
Compare(TrackingMissileEuler.z, GyroscopeEuler.z, out NewTrackingMissileEuler.z, out NewGyroscopeEuler.z);
}
//(解释)该函数的作用为,传入TrackingMissileEuler与GyroscopeEuler,如果TrackingMissileEuler-GyroscopeEuler的绝对值>180
//(解释)则将2者中小的那个+360以后输出回来
//(解释)否则值不变返回
void Compare(float TrackingMissileEuler, float GyroscopeEuler, out float NewTrackingMissileEuler, out float NewGyroscopeEuler)
{
if (Mathf.Abs(GyroscopeEuler - TrackingMissileEuler) > 180)
{
if (GyroscopeEuler < TrackingMissileEuler)
{
GyroscopeEuler += 360;
}
else
{
TrackingMissileEuler += 360;
}
}
NewTrackingMissileEuler = TrackingMissileEuler;
NewGyroscopeEuler = GyroscopeEuler;
}
}
另外,本人之前是用cocos的,最近一两个月才开始接触unity,所以代码上肯定做不到完美,而且还会有很多C艹的影子,忘大家
见谅吧。我也只是给与我一样数学渣的同胞们奉献一份不需要复杂数学计算的跟踪解决方案而已,大家重在领会精神就好,以上。
项目下载:http://pan.baidu.com/s/1kVHWWkf