最近開發完成一款打飛機的游戲,記錄一下制作追蹤導彈的方法,最開始在網上找到的資料制作出來的追蹤導彈都不夠真實,主要的問題是沒有對導彈進行一個閥值處理,導彈每幀都始終會面向目標,而不是按照一定的角度進行旋轉,導致無法躲避跟蹤導彈,下面我來說一下更加真實的跟蹤導彈的制作方法。
拖拽
首先,我的Demo里有兩個小飛機,一個是主角,一個是敵機,有一個需求就是這兩個飛機可以通過鼠標進行拖拽,所以先給出拖拽的腳本,直接綁定即可,當然記得給GameObject添加一個BoxCollder。
1 using UnityEngine; 2 using System.Collections; 3 4 /// <summary> 5 /// 拖拽腳本. 6 /// </summary> 7 public class DragAndDrop : MonoBehaviour 8 { 9 bool isCatched = false; 10 11 void Update() 12 { 13 if(Input.GetMouseButtonDown(0)) 14 { 15 //根據鼠標位置創建一條垂直於屏幕的射線 16 Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); 17 //保存射線信息的結構體 18 RaycastHit hit; 19 //對射線進行碰撞, 如果存在碰撞 20 if(Physics.Raycast(ray, out hit)) 21 { 22 //碰撞到當前對象時 23 if(hit.collider.gameObject == this.gameObject) 24 { 25 //標記為抓取狀態 26 isCatched = true; 27 } 28 } 29 } 30 31 if(Input.GetMouseButtonUp(0)) 32 { 33 //取消抓取狀態 34 isCatched = false; 35 } 36 37 if(isCatched) 38 { 39 //獲取鼠標點在場景中的位置 40 Vector3 pos = Camera.main.ScreenToWorldPoint(Input.mousePosition); 41 pos.z = 0; 42 //設置位置 43 this.transform.position = pos; 44 } 45 } 46 }
管理類
我使用UGUI添加了一個發射導彈的按鈕,需要一個管理類來管理這部分的邏輯:
1 using UnityEngine; 2 using System.Collections; 3 4 /// <summary> 5 /// 控制腳本. 6 /// </summary> 7 public class TrackDemoScript : MonoBehaviour 8 { 9 public GameObject player; 10 11 public GameObject enemy; 12 13 /// <summary> 14 /// 開火. 15 /// </summary> 16 public void Fire() 17 { 18 GameObject go = Resources.Load<GameObject>("Prefab/Bullet"); 19 GameObject bullet = Instantiate(go, player.transform.position, Quaternion.identity) as GameObject; 20 bullet.GetComponent<BulletScript>().target = enemy; 21 } 22 }
導彈被我制作為了一個預制件。
導彈邏輯
最重要的就是閥值了,我規定了每幀允許旋轉的一個最大值,保證模擬出現實世界的效果,導彈要轉彎肯定是畫出一個弧線而不是馬上掉頭的。
1 using UnityEngine; 2 using System.Collections; 3 4 /// <summary> 5 /// 跟蹤導彈腳本. 6 /// </summary> 7 public class BulletScript : MonoBehaviour 8 { 9 /// <summary> 10 /// 每秒最大可旋轉的角度. 11 /// </summary> 12 private const float MAX_ROTATION = 90; 13 14 /// <summary> 15 /// 每幀最大可旋轉的角度. 16 /// </summary> 17 private static float MAX_ROTATION_FRAME = MAX_ROTATION / ((float) (Application.targetFrameRate == -1 ? 60 : Application.targetFrameRate)); 18 19 /// <summary> 20 /// 攻擊目標. 21 /// </summary> 22 public GameObject target; 23 24 void Start() 25 { 26 } 27 28 void Update() 29 { 30 //轉向目標 31 float dx = target.transform.position.x - this.transform.position.x; 32 float dy = target.transform.position.y - this.transform.position.y; 33 float rotationZ = Mathf.Atan2(dy, dx) * 180 / Mathf.PI; 34 //得到最終的角度並且確保在 [0, 360) 這個區間內 35 rotationZ -= 90; 36 rotationZ = MakeSureRightRotation(rotationZ); 37 //獲取增加的角度 38 float originRotationZ = MakeSureRightRotation(this.transform.eulerAngles.z); 39 float addRotationZ = rotationZ - originRotationZ; 40 //超過 180 度需要修改為負方向的角度 41 if(addRotationZ > 180) 42 { 43 addRotationZ -= 360; 44 } 45 //不超過每幀最大可旋轉的閥值 46 addRotationZ = Mathf.Max(-MAX_ROTATION_FRAME, Mathf.Min(MAX_ROTATION_FRAME, addRotationZ)); 47 //應用旋轉 48 this.transform.eulerAngles = new Vector3(0, 0, this.transform.eulerAngles.z + addRotationZ); 49 //移動 50 this.transform.Translate(new Vector3(0, 2.0f * Time.deltaTime, 0)); 51 } 52 53 /// <summary> 54 /// 確保角度在 [0, 360) 這個區間內. 55 /// </summary> 56 /// <param name="rotation">任意數值的角度.</param> 57 /// <returns>對應的在 [0, 360) 這個區間內的角度.</returns> 58 private float MakeSureRightRotation(float rotation) 59 { 60 rotation += 360; 61 rotation %= 360; 62 return rotation; 63 } 64 }
運行即可查看效果。