大家在玩《魂斗罗》的时候一定知道游戏中有一种追踪弹吧,这种炮弹会随着玩家位置的改变而改变,就像我们在某些军事题材的电影中经常看到的镜头一样,我方战斗机将敌机目标锁定后发射炮弹,炮弹就会对敌机紧追不舍并最最终摧毁敌机。可是不管影视作品特效如何炫酷夺目,最终留给我们的就是华丽的特效背后蕴藏的原理。在游戏开发领域,这种技术称为追踪算法,它是属于AI的一个范畴。常见的追中算法主要有三种,即坐标追踪、视线追踪、拦截追踪。下面呢,我们来分别讲解这三种不同的追踪算法:

       一、坐标追踪

      坐标追踪是最简单、最基本的一种追踪算法,如图,

Unity3D游戏开发之从《魂斗罗》游戏说起(上)——目标追踪

     其基本思路是根据追踪目标的坐标来改变追踪物体的坐标,使两者间距离缩短。举一个简单地例子,假设我们使用二维坐标mPosition来表示追踪物体的坐标,使用mTargetPos来表示追踪目标的坐标,则坐标追踪的算法可以简单表示成下面的代码:

[csharp] view plaincopyprint?Unity3D游戏开发之从《魂斗罗》游戏说起(上)——目标追踪Unity3D游戏开发之从《魂斗罗》游戏说起(上)——目标追踪

  1. if(mPosition.x<mTargetPos.x) mPosition.x+=Speed*Time.deltaTime;  

  2. if(mPosition.x>mTargetPos.x) mPosition.x-=Speed*Time.deltaTime;  

  3. if(mPosition.y<mTargetPos.y) mPosition.y+=Speed*Time.deltaTime;    

  4. if(mPosition.y>mTargetPos.y) mPosition.y-=Speed*Time.deltaTime;  

  5.       

     二、视线追踪,主要是指每一时刻都追踪者会沿着被追逐者之间的直线方向运动。如图所示:

Unity3D游戏开发之从《魂斗罗》游戏说起(上)——目标追踪

        由图易知,该算法的关键是求解两个位置间的连线。由向量的知识我们知道这条直线可以通过向量可以通过向量a-向量b得到。此时追踪者的速度应该满足约束条件:

        水平分速度Vx/垂直分速度Vy=c向量的水平分量/c向量的垂直向量。

         

        三、拦截追踪,拦截追踪是在前两种方法的基础上发展而来的一种方法,如果考虑的是追踪目标太远,如果两者者速度一样,或者相差不大,有可能很难追上。如图

Unity3D游戏开发之从《魂斗罗》游戏说起(上)——目标追踪

        此时,对于追踪物体而言,它只需要知道追踪目标的位置、方向与速度,就会计算一个最佳的拦截位置,且所需要的时间最短。我们假设最佳拦截点是S2,由速度与位移的关系很容易知道 S2=Sp+t*V。其中t是追踪者追上猎物的时间。接下来问题变为一个简单的追击问题,求追击时间t。

       首先我们建立追踪者与猎物的速度向量Va与Vp,及位置向量Sa与Sp。  设速度差向量 Vd =Vp-Va 。称为靠拢速度。 设距离差向量 Sd =Sp-Sa 。称为靠拢距离。  于是,靠拢时间 t=|Sd|/|Vd| 。即路程除以速度等于时间。  套用公式S2=Sp+t*V 就得到了拦截点S2,剩下的过程就与视线追踪一样。

      好了,在理解了追踪算法的基本原理以后,下面我们以一个最简单的例子来演示今天的内容。首先我们在Unity3D中建立一个简单地场景,如图所示:

Unity3D游戏开发之从《魂斗罗》游戏说起(上)——目标追踪

       我们采取正交投影的方式来实现一个2D场景,场景中红色的方块为目标物体,绿色的方块为追踪物体。我们这里采取的是视线追踪的方法。我们首先来为追踪物体创建脚本Follow.cs。脚本定义如下:

[csharp] view plaincopyprint?Unity3D游戏开发之从《魂斗罗》游戏说起(上)——目标追踪Unity3D游戏开发之从《魂斗罗》游戏说起(上)——目标追踪

  1. using UnityEngine;  

  2. using System.Collections;  

  3.   

  4. public class Follow : MonoBehaviour {  

  5.   

  6.     //追踪的目标物体  

  7.     public Transform Target;  

  8.     //两个物体间的最小距离  

  9.     public float MinDistance=1F;  

  10.     //两个物体间的最大距离  

  11.     public float MaxDistance=5F;  

  12.     //定义追踪的速度  

  13.     public float Speed=0.25F;  

  14.     //定义追踪物体的坐标  

  15.     private Vector2 mPosition;  

  16.     //定义目标物体的坐标  

  17.     private Vector2 mTargetPos;  

  18.     //是否在追踪  

  19.     private bool isFollow=false;  

  20.   

  21.     void Start ()   

  22.     {  

  23.         mPosition=new Vector2(transform.position.x,transform.position.y);  

  24.         mTargetPos=new Vector2(Target.transform.position.x,Target.position.y);  

  25.     }  

  26.   

  27.     void Update ()   

  28.     {  

  29.       //获取目标物体的位置  

  30.       mTargetPos=new Vector2(Target.transform.position.x,Target.position.y);  

  31.       //如果追踪物体与目标物体之间距离大于等于最大距离,则开始追踪  

  32.       if(Vector2.Distance(mPosition,mTargetPos)>=MaxDistance)  

  33.       {  

  34.             isFollow=true;  

  35.       }  

  36.       //如果追踪物体与目标物体之间距离小于等于最小距离,则停止追踪  

  37.       if(Vector2.Distance(mPosition,mTargetPos)<=MinDistance)  

  38.       {  

  39.             isFollow=false;  

  40.       }  

  41.       //如果开始追踪,则执行下面的代码  

  42.       if(isFollow)  

  43.       {  

  44.             //计算坐标值  

  45.             if(mPosition.x<mTargetPos.x) mPosition.x+=Speed*Time.deltaTime*Mathf.Abs(Mathf.Cos(getAngle()));  

  46.             if(mPosition.x>mTargetPos.x) mPosition.x-=Speed*Time.deltaTime*Mathf.Abs(Mathf.Cos(getAngle()));  

  47.             if(mPosition.y<mTargetPos.y) mPosition.y+=Speed*Time.deltaTime*Mathf.Abs(Mathf.Sin(getAngle()));  

  48.             if(mPosition.y>mTargetPos.y) mPosition.y-=Speed*Time.deltaTime*Mathf.Abs(Mathf.Sin(getAngle()));  

  49.             //改变追踪物体的坐标  

  50.             transform.position=new Vector3(mPosition.x,mPosition.y,0);  

  51.       }  

  52.     }  

  53.   

  54.   

  55.     private float getAngle()  

  56.     {  

  57.         float angle=0;  

  58.         //获取水平方向与竖直方向的变化量  

  59.         float deltaX=mTargetPos.x-mPosition.x;  

  60.         float deltaY=mTargetPos.y-mPosition.y;  

  61.         //计算角度  

  62.         if(deltaX>0 && deltaY>0)  

  63.             angle= Mathf.Atan(deltaY/deltaX);  

  64.         if(deltaX<0 && deltaY>0)  

  65.             angle= Mathf.PI-Mathf.Atan(deltaY/deltaX);  

  66.         if(deltaX<0 && deltaY<0)  

  67.             angle= Mathf.PI+Mathf.Atan(deltaY/deltaX);  

  68.         if(deltaX>0 && deltaY<0)  

  69.             angle= 2*Mathf.PI-Mathf.Atan(deltaY/deltaX);  

  70.         if(deltaX==0)  

  71.         {  

  72.             angle=Mathf.PI/2;  

  73.         }  

  74.         if(deltaY==0)  

  75.         {  

  76.             angle=0;  

  77.         }  

  78.         return angle;  

  79.     }  

  80. }  

      在这段脚本中,我们定义了一个getAngle()方法,该方法用于获取追踪物体与目标物体连线与X轴正方向所成的夹角。这里主要用到三角函数知识,大家可以再温习下以前学过的知识,无论是程序设计还是游戏开发,数学都应该是我们最应该掌握的东西。好了,通过角度计算,我们可以将速度分解到水平和垂直两个方向,从而保证视线追踪中的约束条件成立。如果我们将getAngle()方法从脚本中去除,则这就是最简单的坐标追踪,希望大家自己去探讨和研究啊,如果有时间的话,会将三种算法的代码都展示出来的,希望大家继续关注我的博客啊。呵呵。好了,接下来,我们来为追踪目标创建一个脚本,以便玩家可以控制追踪目标躲避追踪。脚本定义如下:

[csharp] view plaincopyprint?Unity3D游戏开发之从《魂斗罗》游戏说起(上)——目标追踪Unity3D游戏开发之从《魂斗罗》游戏说起(上)——目标追踪

  1. using UnityEngine;  

  2. using System.Collections;  

  3.   

  4. public class Control : MonoBehaviour {  

  5.   

  6.     //定义移动的速度  

  7.     public float Speed=0.25F;  

  8.     //定义当前位置  

  9.     private float mX,mY;  

  10.     void Start ()   

  11.     {  

  12.         mX=transform.position.x;  

  13.         mY=transform.position.y;  

  14.     }  

  15.   

  16.     void Update ()   

  17.     {  

  18.       if(Input.GetKey(KeyCode.A))  

  19.       {  

  20.             mX-=Speed*Time.deltaTime;  

  21.       }  

  22.       if(Input.GetKey(KeyCode.D))  

  23.       {  

  24.             mX+=Speed*Time.deltaTime;  

  25.       }  

  26.       if(Input.GetKey(KeyCode.W))  

  27.       {  

  28.             mY+=Speed*Time.deltaTime;  

  29.       }  

  30.       if(Input.GetKey(KeyCode.S))  

  31.       {  

  32.             mY-=Speed*Time.deltaTime;  

  33.       }  

  34.       transform.position=new Vector3(mX,mY,0);  

  35.     }  

  36. }  

         好了,下面我们来测试一下程序运行的效果:

 Unity3D游戏开发之从《魂斗罗》游戏说起(上)——目标追踪

        不知道为什么录制的GIF动画看不到追踪物体,囧啊,不过可以负责任地对大家说,程序没什么问题,哈哈。

更多精彩请到http://www.gopedu.com/


        好了,今天的内容就是这样了,希望大家喜欢,如果大家希望继续在《魂斗罗》游戏中寻找有趣的设计。