最近开发完成一款打飞机的游戏,记录一下制作追踪导弹的方法,最开始在网上找到的资料制作出来的追踪导弹都不够真实,主要的问题是没有对导弹进行一个阀值处理,导弹每帧都始终会面向目标,而不是按照一定的角度进行旋转,导致无法躲避跟踪导弹,下面我来说一下更加真实的跟踪导弹的制作方法。
拖拽
首先,我的Demo里有两个小飞机,一个是主角,一个是敌机,有一个需求就是这两个飞机可以通过鼠标进行拖拽,所以先给出拖拽的脚本,直接绑定即可,当然记得给GameObject添加一个BoxCollder。
1 using UnityEngine; 2 using System.Collections; 3 4 ///5 /// 拖拽脚本. 6 /// 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 ///5 /// 控制脚本. 6 /// 7 public class TrackDemoScript : MonoBehaviour 8 { 9 public GameObject player;10 11 public GameObject enemy;12 13 ///14 /// 开火.15 /// 16 public void Fire()17 {18 GameObject go = Resources.Load("Prefab/Bullet");19 GameObject bullet = Instantiate(go, player.transform.position, Quaternion.identity) as GameObject;20 bullet.GetComponent ().target = enemy;21 }22 }
导弹被我制作为了一个预制件。
导弹逻辑
最重要的就是阀值了,我规定了每帧允许旋转的一个最大值,保证模拟出现实世界的效果,导弹要转弯肯定是画出一个弧线而不是马上掉头的。
1 using UnityEngine; 2 using System.Collections; 3 4 ///5 /// 跟踪导弹脚本. 6 /// 7 public class BulletScript : MonoBehaviour 8 { 9 ///10 /// 每秒最大可旋转的角度.11 /// 12 private const float MAX_ROTATION = 90;13 14 ///15 /// 每帧最大可旋转的角度.16 /// 17 private static float MAX_ROTATION_FRAME = MAX_ROTATION / ((float) (Application.targetFrameRate == -1 ? 60 : Application.targetFrameRate));18 19 ///20 /// 攻击目标.21 /// 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 ///54 /// 确保角度在 [0, 360) 这个区间内.55 /// 56 /// 任意数值的角度.57 ///对应的在 [0, 360) 这个区间内的角度. 58 private float MakeSureRightRotation(float rotation)59 {60 rotation += 360;61 rotation %= 360;62 return rotation;63 }64 }
运行即可查看效果。