Unity3D遊戲開發之從《魂鬥羅》遊戲說起(上)——目標追蹤

大家在玩《魂鬥羅》的時候一定知道遊戲中有一種追蹤彈吧,這種炮彈會隨着玩家位置的改變而改變,就像我們在某些軍事題材的電影中經常看到的鏡頭一樣,我方戰鬥機將敵機目標鎖定後發射炮彈,炮彈就會對敵機緊追不捨並最最終摧毀敵機。可是不管影視作品特效如何炫酷奪目,最終留給我們的就是華麗的特效背後蘊藏的原理。在遊戲開發領域,這種技術稱爲追蹤算法,它是屬於AI的一個範疇。常見的追中算法主要有三種,即座標追蹤、視線追蹤、攔截追蹤。下面呢,我們來分別講解這三種不同的追蹤算法:

       一、座標追蹤

      座標追蹤是最簡單、最基本的一種追蹤算法,如圖,

     其基本思路是根據追蹤目標的座標來改變追蹤物體的座標,使兩者間距離縮短。舉一個簡單地例子,假設我們使用二維座標mPosition來表示追蹤物體的座標,使用mTargetPos來表示追蹤目標的座標,則座標追蹤的算法可以簡單表示成下面的代碼:

[csharp] view plaincopyprint?在CODE上查看代碼片派生到我的代碼片

  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.       

     二、視線追蹤,主要是指每一時刻都追蹤者會沿着被追逐者之間的直線方向運動。如圖所示:

        由圖易知,該算法的關鍵是求解兩個位置間的連線。由向量的知識我們知道這條直線可以通過向量可以通過向量a-向量b得到。此時追蹤者的速度應該滿足約束條件:

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

         

        三、攔截追蹤,攔截追蹤是在前兩種方法的基礎上發展而來的一種方法,如果考慮的是追蹤目標太遠,如果兩者者速度一樣,或者相差不大,有可能很難追上。如圖

        此時,對於追蹤物體而言,它只需要知道追蹤目標的位置、方向與速度,就會計算一個最佳的攔截位置,且所需要的時間最短。我們假設最佳攔截點是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中建立一個簡單地場景,如圖所示:

       我們採取正交投影的方式來實現一個2D場景,場景中紅色的方塊爲目標物體,綠色的方塊爲追蹤物體。我們這裏採取的是視線追蹤的方法。我們首先來爲追蹤物體創建腳本Follow.cs。腳本定義如下:

[csharp] view plaincopyprint?在CODE上查看代碼片派生到我的代碼片

  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?在CODE上查看代碼片派生到我的代碼片

  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. }  

         好了,下面我們來測試一下程序運行的效果:

 

        不知道爲什麼錄製的GIF動畫看不到追蹤物體,囧啊,不過可以負責任地對大家說,程序沒什麼問題,哈哈。

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


        好了,今天的內容就是這樣了,希望大家喜歡,如果大家希望繼續在《魂鬥羅》遊戲中尋找有趣的設計。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章