自动追踪导弹功能实现

依旧先上效果图,小球模拟被追踪的目标,小方块为发射器,小长条这模拟追踪导弹,会跟着小球的位置而转向。

思路

由于是追踪导弹,所以导弹的朝向一定是会最终朝向目标点的。假设导弹为missile,目标为target,即最终

missile.transform.forward = (target.transform.position - missile.transform.position).normalized;

我们只需要每帧使用 Vector3.Lerp 将导弹的当前朝向慢慢的转变为最终的朝向即可。

至于转角速度,假设我们定一秒钟旋转180度。那么每帧的度数应该是180*deltatime,我们可以用 Vector3.Angle 来计算出当前方向和最终方向的夹角。所以我们 Vector3.Lerp 可以这样写

Vector3 angleOffset = Vector3.Angle(自身朝向, 最终朝向);
自身朝向 = Vector3.Lerp(自身朝向, 最终朝向, 每帧的旋转角度 / angleOffset);

新增知识点

Transform.TransformDirection(Vector3),从自身坐标到世界坐标变换方向。这个操作不会受到变换的缩放和位置的影响。返回的向量与direction有同样的长度。

这么说可能有点不明所以,所以就举个例子来说明,假设我们一个物体,需要以5的速度向正前方移动,那么对他自身而言,他的速度应该为Vector3(0,0,5),因为速度是有大小有方向的,Vector3(0,0,5)可以看出,方向为z轴正方向,大小为5。

那么得知物体速度后,我们怎么移动物体呢,transform.position = transform.position + Vector3(0,0,5) ?这个显然是不行的,因为你并不知道物体的朝向,这么相加只会让物体不停的向世界坐标的Z轴正方形移动,但并不一定是物体的正方向(物体的Z轴正方向)。

因此我们需要使用到上面的方法: 物体的Transform.TransformDirection(Vector3(0,0,5)) 得到一个新的Vector3,这个Vector3就是原始Vector3基于物体本地坐标的大小方向,转换为基于世界坐标的大小和方向。

比如物体和世界坐标方向一致,那么得到的还是Vector3(0,0,5)

如果方向相反即Rotate为(0,180,0),得到的即为Vector3(0,0,-5)

若物体的Rotate为(0,90,0),得到的即为Vector3(5,0,0),物体Z轴方向为世界坐标X轴的方向

Transform.InverseTransformDirection(Vector3),即上面操作的逆操作。

一般我们移动一个物体可能会用最简单的 transform.forward * speed,速度只是一个float值,并不存在方向。但是在真实情况中,物体一般不仅仅只有一个简单的向前的速度,可能还会存在重力导致的向下的速度,空气阻力造成的减速等等,所以我们用Vector3来作为速度,会更容易处理。

 

实现

首先我们在场景中建个cube作为炮弹的发射器,为其添加一个脚本Emitter,作用就是每隔两秒钟生成一个炮弹

public class Emitter : MonoBehaviour
{
    public GameObject missile;

    float currentTime;

	void Update()
	{
        currentTime += Time.deltaTime;
        if(currentTime > 2)
        {
            currentTime = 0;
            GameObject m = GameObject.Instantiate(missile);
            m.transform.localPosition = Vector3.zero;
            m.SetActive(true);
        }
    }
}

然后我们再建个cube作为炮弹,绑定到Emitter的missile上,同时为炮弹添加脚本TrackMissile,用来自动追踪目标

public class TrackMissile : MonoBehaviour
{
    //瞄准的目标
    public Transform target;

    //炮弹本地坐标速度,有大小有方向。
    Vector3 speed = new Vector3(0, 0, 5);

    //存储转向前炮弹的本地坐标速度
    Vector3 lastSpeed;

    //旋转的速度,单位 度/秒
    int rotateSpeed = 90;

    //目标到自身连线的向量,最终朝向
    Vector3 finalForward;

    //自己的forward朝向和mFinalForward之间的夹角
    float angleOffset;

    RaycastHit hit;

    void Start()
	{
        //将炮弹的本地坐标速度转换为世界坐标
        speed = transform.TransformDirection(speed);
        Debug.Log("speed:"+ speed);
    }

	void Update()
	{
        CheckHint();
        UpdateRotation();
        UpdatePosition();
    }

    //射线检测,如果击中目标点则销毁炮弹
    void CheckHint()
    {
        if(Physics.Raycast(transform.position, transform.forward, out hit)){
            if(hit.transform == target && hit.distance < 1)
            {
                Destroy(gameObject);
            }
        }
    }

    //更新位置
    void UpdatePosition()
    {
        transform.position = transform.position + speed * Time.deltaTime;
    }

    //旋转,使其朝向目标点,要改变速度的方向
    void UpdateRotation()
    {
        //先将速度转为本地坐标,旋转之后再变为世界坐标
        lastSpeed = transform.InverseTransformDirection(speed);

        ChangeForward(rotateSpeed * Time.deltaTime);

        speed = transform.TransformDirection(lastSpeed);
    }

    void ChangeForward(float speed)
    {
        //获得目标点到自身的朝向
        finalForward = (target.position - transform.position).normalized;
        if (finalForward != transform.forward)
        {
            angleOffset = Vector3.Angle(transform.forward, finalForward);
            if (angleOffset > rotateSpeed)
            {
                angleOffset = rotateSpeed;
            }
            //将自身forward朝向慢慢转向最终朝向
            transform.forward = Vector3.Lerp(transform.forward, finalForward, speed / angleOffset);
        }
    }
}

最后我们建个sphere,拖到发射器的边上,用于当被追踪目标,同时将这个目标拖到炮弹TrackMissile组件的target上即可。这样最简单的一个自动追踪炮弹就完成了。

发布了71 篇原创文章 · 获赞 160 · 访问量 13万+

猜你喜欢

转载自blog.csdn.net/wangjiangrong/article/details/102627877