Unity3D 关于飞行类型集合
Posted 彭然心跳
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Unity3D 关于飞行类型集合相关的知识,希望对你有一定的参考价值。
在3D游戏中如果涉及到射击类游戏那么就会有飞行类的脚本比如直线飞行,导弹追踪飞行,抛物线飞行,激光飞行,等等。。。
1.这些飞行的脚本有一个共同的特点都需要有目标,和方向, 那么先写一个基类脚本先上干货
//这个脚本基本是供外面调(赋值什么的)
using System.Collections; using System.Collections.Generic; using UnityEngine; public class FlyAI : MonoBehaviour { private static int MYUID=0; protected int m_MyUID=0; public const int EVENT_IDLE = 0;//空闲 public const int EVENT_START = 1;//开始飞行 public const int EVENT_FLYING = 2;//飞行中 public const int EVENT_ARRIVE = 3;//到达目标 public const int EVENT_END = 4;//到达最大航程,飞行结束 public delegate void OnFlyEvent(int nEvent); protected OnFlyEvent onFlyEvent = null; protected string m_Myname = ""; protected bool m_bActive = false; protected int m_CurFlyEvent = EVENT_IDLE; protected GameObject m_MyGo = null; //自己的GameObject protected Vector3 m_MeStartPos = new Vector3(0, 0, 0);//自己的初始位置 protected Vector3 m_MeCurrPos = new Vector3(0, 0, 0);//自己的当前位置 protected float m_fTurnRate = 1f; //自己的转湾倍率(标量) protected float m_fSpeedRate = 1f; //自己的飞行速度(单位米/秒) protected Vector3 m_Velocity = new Vector3(1, 0, 1);//自己的速度方向 protected float m_fMaxRange = 10000f; //飞行最大飞行距离 private FightTarg m_ArmTarg = null;//目标 private GameObject m_TargGo = null; //目标的GameObject protected Vector3 m_TargStartPos = new Vector3(0, 0, 0);//目标的初始位置 protected Vector3 m_TargCurrPos = new Vector3(0, 0, 0);//目标的当前位置 protected float m_fArriveRange = 0.1f;//达到距离(离目标距离多少为到达目标,单位:米) private bool m_bTargIsAlive = false; protected float m_FlyLife = 0; //已飞行时间 protected float m_FlyDistance = 0; //累计飞行距离 protected bool m_bTimeFreeze = false; protected virtual void Awake() { MYUID++; m_MyUID=MYUID; m_CurFlyEvent = EVENT_IDLE; m_Myname = "未命名"; } protected virtual void Start() { m_bActive = true; m_bTargIsAlive = true; } protected virtual void Update() { if (m_bTimeFreeze) return; if (m_bActive) { if (m_CurFlyEvent == EVENT_IDLE) { DoEvent(EVENT_START);//开始飞行事件 } else { DoFly(); } } } protected virtual void OnDestroy() { m_bActive = false; m_TargGo = null; m_MyGo = null; } public void EnableTimeFreeze(bool bEnable) { m_bTimeFreeze = bEnable; OnTimeFreeze(); } protected virtual void OnTimeFreeze() { //子类实现其自定义功能 } protected virtual void DoFly() { m_FlyLife = m_FlyLife + Time.deltaTime; if (m_TargGo == null || m_bTargIsAlive == false) return; if (m_ArmTarg != null && m_ArmTarg.IsDie()) {//如果目标死亡,则不复制位置了 m_TargGo = null; m_ArmTarg = null; m_bTargIsAlive = false; return; } //复制目标GameObject当前位置 DefendTool.CopyVector3(m_TargGo.transform.position, out m_TargCurrPos); return; } public void SetName(string flyname) { m_Myname = flyname; } public void AppendName(string flyname) { m_Myname = m_Myname + ":" + flyname; } public void Pause() { m_bActive = false; } public void Resume() { m_bActive = true; } public void EnableFly(bool bEnable) { m_bActive = bEnable; } protected float SinProjectToVector(Vector3 from, Vector3 to) { float result = Vector3.Dot(from.normalized, to.normalized);//向量的点积 result = Mathf.Acos(result);//弧度 return Mathf.Abs(from.magnitude * Mathf.Sin(result));//点到直线距离 } public void RegisterEvent(OnFlyEvent _callback) { onFlyEvent -= _callback; onFlyEvent += _callback; } public void UnRegisterEvent(OnFlyEvent _callback) { onFlyEvent -= _callback; } protected void DoEvent(int Evnet) { if (Evnet == m_CurFlyEvent) return; m_CurFlyEvent = Evnet; if (onFlyEvent == null) return; //try {//屏蔽回调产生的错误导致关联错误 onFlyEvent(m_CurFlyEvent); } //catch (System.Exception e) //{ // Debug.LogError("FlyAI::DoEvent() name:" + m_Myname + ",回调处理中发生错误:" + e.StackTrace); //} return; } /// <summary> /// 设置飞行器的参数 /// </summary> /// <param name="meGameobject">飞行器GameObject</param> /// <param name="startDir">飞行器初始速度方向</param> /// <param name="fSpeedRate">飞行速度(单位米/秒)</param> /// <param name="fTurnRate">转弯倍率(标量,大于0)</param> /// <param name="fMaxRange">飞行器最大的飞行距离</param> public virtual void InitMe(GameObject meGameobject, Vector3 startDir, float fSpeedRate, float fTurnRate, float fMaxRange=1000000) { m_MyGo = meGameobject; DefendTool.CopyVector3(startDir, out m_Velocity); m_fSpeedRate = fSpeedRate; m_fTurnRate = fTurnRate; m_fMaxRange = fMaxRange; m_FlyDistance = 0; DefendTool.CopyVector3(m_MyGo.transform.position, out m_MeStartPos); DefendTool.CopyVector3(m_MyGo.transform.position, out m_MeCurrPos); } /// <summary> /// 设置飞行器追踪目标参数 /// </summary> /// <param name="ITarg">目标ITarg</param> /// <param name="targGameobject">目标GameObject</param> /// <param name="fArriveRange">达到距离(离目标距离多少为到达目标)</param> public virtual void InitTarg(FightTarg targ, GameObject targGameobject, float fArriveRange) { m_ArmTarg = targ; m_TargGo = targGameobject; DefendTool.CopyVector3(m_TargGo.transform.position, out m_TargStartPos); DefendTool.CopyVector3(m_TargGo.transform.position, out m_TargCurrPos); m_fArriveRange = fArriveRange; } /// <summary> /// 设置飞行器追踪目标参数 /// </summary> /// <param name="ITarg">目标ITarg</param> /// <param name="targPosition">目标静态位置</param> /// <param name="fArriveRange">达到距离(离目标距离多少为到达目标)</param> public void InitTarg(FightTarg targ, Vector3 targPosition, float fArriveRange) { m_ArmTarg = targ; m_TargGo = null; DefendTool.CopyVector3(targPosition, out m_TargStartPos); DefendTool.CopyVector3(targPosition, out m_TargCurrPos); m_fArriveRange = fArriveRange; } }
2.上面的基类已经为我提供了数据接下来就是关于子弹飞行的时间以及事件基类
using System.Collections; using System.Collections.Generic; using UnityEngine; public class FlyWeapon : FlyAI {//武器飞行器基类(子弹,导弹,炸弹,激光等飞行器基类) protected float m_MaxLife = 0; //最大生命周期 protected override void Awake() { base.Awake(); SetName("FlyWeapon"); } protected override void Start() { base.Start(); } protected override void Update() { base.Update(); } protected override void OnDestroy() { base.OnDestroy(); } protected override void DoFly() { base.DoFly(); } protected void DestroyBullet() { EnableFly(false); Destroy(this);//消除子弹脚本 } }
3_1.终于可以写子弹的本体脚本了(先写一个直线飞行的子弹)
using System.Collections; using System.Collections.Generic; using UnityEngine; public class FlyWeaponLine : FlyWeapon {//子弹类飞行方式,直线飞行追踪目标,没有转弯半径,直接转向 protected override void Awake() { base.Awake(); SetName("FlyWeaponLine"); } protected override void Start() { base.Start(); } protected override void Update() { base.Update(); } protected override void OnDestroy() { base.OnDestroy(); } protected override void DoFly() { base.DoFly(); //本时间飞行距离 bool bThrough = false; float flyDistance=Time.deltaTime*m_fSpeedRate; Vector3 remainpath = m_TargStartPos - transform.position; float remainDistance = remainpath.magnitude; if (remainDistance <= flyDistance) { flyDistance = remainDistance; bThrough = true; } m_FlyDistance = m_FlyDistance + flyDistance; remainpath = remainpath.normalized * flyDistance; transform.position = transform.position + remainpath; DefendTool.CopyVector3(transform.position, out m_MeCurrPos);//保存新位置 //调整子弹朝向 transform.LookAt(m_TargStartPos); //判断此次飞行是否传过目标物体 if (remainDistance < m_fArriveRange || bThrough) { DoEvent(EVENT_ARRIVE); DestroyBullet();//结束子弹 } else { if (m_FlyDistance >= m_fMaxRange) { DoEvent(EVENT_END); //飞行结束 DestroyBullet();//结束子弹 } else { DoEvent(EVENT_FLYING); } } return; } }
3_2:导弹追踪
using System.Collections; using System.Collections.Generic; using UnityEngine; public class FlyWeaponNavi : FlyWeapon {//导弹类飞行方式,自动追踪目标,有转弯半径 protected Vector3 planeangle = new Vector3(1, 0, 1); protected float m_fCurrSpeed = 0; protected override void Awake() { base.Awake(); SetName("FlyWeaponNavi"); } protected override void Start() { base.Start(); transform.Rotate(m_Velocity); m_fCurrSpeed = 0; } protected override void Update() { base.Update(); } protected override void OnDestroy() { base.OnDestroy(); } protected override void DoFly() { base.DoFly(); //转向处理 float per = m_FlyLife/2; float rate = per * per; if (rate >= 1) rate = 1; Vector3 v1 = m_TargCurrPos - transform.position; m_fTurnRate = Mathf.Lerp(0, 0.8f, rate); transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(v1), m_fTurnRate ); //求运动距离 per = m_FlyLife / 1.4f; rate = per * per; if (rate >= 1) rate = 1; bool bArrived = false; m_fCurrSpeed = Mathf.Lerp(m_fSpeedRate/3, m_fSpeedRate, rate); float flylength = m_fCurrSpeed * Time.deltaTime; Vector3 v0_me = transform.forward * flylength; //运行方向向量 Vector3 v1_totarg = m_TargCurrPos - transform.position;//子弹指向目标的向量 Vector3 v2_project = Vector3.Project(v1_totarg,v0_me.normalized);//向量v1在v0上的投影向量 float flyDistance = v0_me.magnitude; //本次飞行距离(米) float totargDistance = v1_totarg.magnitude;//和目标的距离(米) float projectDistance = v2_project.magnitude;//投影距离 float sinDistance = SinProjectToVector(v1_totarg, v0_me);//目标点到运行方向的距离 m_fArriveRange = 1f; if(projectDistance<=flyDistance) {//穿越 if (sinDistance<=m_fArriveRange) {//到达 bArrived = true; v0_me = v0_me.normalized * projectDistance / flyDistance * flylength; } } transform.position = transform.position + v0_me; DefendTool.CopyVector3(transform.position, out m_MeCurrPos);//保存新位置 if (bArrived == true) { DoEvent(EVENT_ARRIVE); DestroyBullet();//结束子弹 //Debug.LogError("FlyWeaponNavi::DoFly()导弹。。。到达目标物体"); } else { if (m_FlyDistance >= m_fMaxRange) { DoEvent(EVENT_END); //飞行结束 DestroyBullet();//结束子弹 //Debug.LogError("FlyWeaponNavi::DoFly()导弹。。。EVENT_END"); } else { DoEvent(EVENT_FLYING); } } return; } }
3_3.1抛物线飞行方式(下面的逻辑是向下或者向上都可以以抛物线的方式飞行)前方高能
using System.Collections; using System.Collections.Generic; using UnityEngine; public class FlyWeaponParabola : FlyWeapon {//炸弹类飞行方式,按抛物线方式进行飞行到原始目标,不能追踪目标新位置 private const float m_G = -9.8f; //重力加速度,相对y轴是负的 private float m_SpeedHorizontal=0;//水平初速度 private float m_SpeedVertical=0;//垂直初速度 private float m_D0=0;//水平距离 private float m_H0=0;//垂直距离 private float m_theta=0; //初速与水平面的夹角 private float m_dx=0; private float m_dy=0; private float m_dz=0; protected override void Awake() { base.Awake(); SetName("FlyWeaponParabola"); } protected override void Start() { base.Start(); //求出水平和垂直初速和夹角 int n = 0; bool bFind = false; while(n<16) { bFind=FindThetaAndSubSpeed(); if (bFind) break; n++; m_fSpeedRate = m_fSpeedRate * 1.5f; } if (bFind==false) { Debug.LogError("FlyWeaponParabola::Start() 抛物线子弹无法抛到位!"); } } protected override void Update() { base.Update(); } protected override void OnDestroy() { base.OnDestroy(); } protected override void DoFly() { base.DoFly(); //计算运行位置 bool bEnd = false; float t = m_FlyLife; if (t >= m_MaxLife) { t = m_MaxLife; bEnd = true; } float y = m_G / 2 * t * t + m_SpeedVertical * t; float d = m_SpeedHorizontal * t; float x = m_dx * d / m_D0; float z = m_dz * d / m_D0; Vector3 nextpos = new Vector3(m_MeStartPos.x + x, m_MeStartPos.y + y, m_MeStartPos.z + z); m_MyGo.transform.position = nextpos; DefendTool.CopyVector3(m_MyGo.transform.position, out m_MeCurrPos);//保存新位置 //调整子弹朝向 m_MyGo.transform.LookAt(m_TargStartPos); //判断此次飞行是否传过目标物体 if (bEnd) { Vector3 distance = m_TargStartPos - m_MyGo.transform.position; if (distance.magnitude < m_fArriveRange) { DoEvent(EVENT_ARRIVE); } else { DoEvent(EVENT_END); } DestroyBullet();//结束子弹 } else { DoEvent(EVENT_FLYING); } return; } /// <summary> /// 求角度和水平和垂直速度 /// </summary> /// <returns></returns> private bool FindThetaAndSubSpeed() { //求出初速与水平面的夹角 m_dx = m_TargStartPos.x - m_MeStartPos.x; m_dy = m_TargStartPos.y - m_MeStartPos.y; m_dz = m_TargStartPos.z - m_MeStartPos.z; m_H0 = m_dy; m_D0 = Mathf.Sqrt(m_dx * m_dx + m_dz * m_dz); if (m_D0 < 0.1) {//同点投放 m_D0 = 0.1f; } m_theta = m_dy / m_D0; m_theta = Mathf.Atan(m_theta);//反正切,返回值介于-pi/2 与 pi/2 之间 float LimtAngle = Mathf.PI / 2; //限制90度 float inc = Mathf.PI * (0.1f / 360);//每次增量为0.1度 //初始角度 m_SpeedHorizontal=m_fSpeedRate * Mathf.Cos(m_theta);//水平初速度 m_SpeedVertical=m_fSpeedRate * Mathf.Sin(m_theta);//垂直初速度 m_MaxLife = m_D0 / m_SpeedHorizontal; float h1 = m_G * m_MaxLife * m_MaxLife / 2 + m_SpeedVertical * m_MaxLife; float h2 = h1; bool bFind = false; while(true) { if (Mathf.Abs(m_H0 - h1) < 0.1f) { bFind = true; break;//找到了 } if (h2 < h1) { bFind = false; break;//未找到了 } m_theta = m_theta + inc; if (m_theta >= LimtAngle) {//超出角度,未找到了 bFind = false; m_theta = m_theta -inc; return true; } h1 = h2; m_SpeedHorizontal = m_fSpeedRate * Mathf.Cos(m_theta);//水平初速度 m_SpeedVertical = m_fSpeedRate * Mathf.Sin(m_theta);//垂直初速度 m_MaxLife = m_D0 / m_SpeedHorizontal; h2 = m_G * m_MaxLife * m_MaxLife / 2 + m_SpeedVertical * m_MaxLife; } //如果无法抛物到位 if (bFind==false) { float dh = 0; if (m_H0<0) { dh = m_H0; } //////////////////////////////////////// //ax^2+bx+c=0; //G/2*t^2+vVertical*t-dh=0; /////////////////////////////////////// float a = m_G / 2; float b = m_SpeedVertical; float c = 0 - dh; float d = b * b - 4 * a * c; if (d>=0) { b = 0 - b; float t1 = (b - Mathf.Sqrt(d)) / (2 * a); float t2 = (b + Mathf.Sqrt(d)) / (2 * a); //取正的小的值 d = t1 < t2 ? t1 : t2; if (d < 0) d = t1 > t2 ? t1 : t2; } if (d <0) { m_MaxLife = 1.0f; } else { m_MaxLife = d; } } //Debug.LogError("FlyWeaponParabola::FindThetaAndSubSpeed() m_MaxLife:" + m_MaxLife); return bFind; } ///// <summary> ///// 求角度和水平和垂直速度 ///// </summary> ///// <returns></returns> //private bool FindThetaAndSubSpeed() //{ // //求出初速与水平面的夹角 // //求出出发点和目标点连线与水平面的夹角theta // //当目标点在出发点水平面之下则theta小于0 // //当目标点和出发点在同水平面则theta为0 // //当目标点在出发点水平面之上则theta大于0 // m_dx = m_TargStartPos.x - m_MeStartPos.x; // m_dy = m_TargStartPos.y - m_MeStartPos.y; // m_dz = m_TargStartPos.z - m_MeStartPos.z; // m_H0 = m_dy; // m_D0 = Mathf.Sqrt(m_dx * m_dx + m_dz * m_dz); // if (m_D0 < 0.05) // {//同点投放 // m_D0 = 0.01f; // } // float theta0 = m_dy / m_D0; // theta0 = Mathf.Atan(theta0);//反正切,返回值介于-pi/2 与 pi/2 之间 // float LimtAngle = Mathf.PI / 2; //限制90度 // float inc = 2 * Mathf.PI * (0.1f / 360);//每次增量为0.1度 // float theta1 = theta0; // float hLow = 0; // float hNow = 0; // float hNew = 0; // float vHorizontal = 0; // float vVertical = 0; // float life = 0; // bool bFindTheta = false; // //查找合适角度 // CalThisTheta(theta1, out vHorizontal, out vVertical, out hNew, out life); // hLow = hNew; // hNow = hNew; // while (true) // { // if (hNew <= hNow) // { // //到达顶点,但无法靠近目标,也使用此角度 // break; // } // hNow = hNew; // if (Mathf.Abs(hNow - m_H0) < 0.1) // { // //距离靠近目标,使用此角度 // bFindTheta = true; // break; // } // if (hNow > m_H0 || hNow < hLow) // { // //跨越目标,使用此角度 // bFindTheta = true; // break; // } // if ((theta0 + inc) > LimtAngle) // { // //跨越垂直角度,使用此角度 // bFindTheta = true; // break; // } // if (hNow <= m_H0) // { // hLow = hNow; // } // theta0 = theta0 + inc; // CalThisTheta(theta0, out vHorizontal, out vVertical, out hNew, out life); // } // if (bFindTheta == false) // { // Debug.LogError("FlyWeaponParabola::FindThetaAndSubSpeed() 敌人在射程之外!!!"); // } // m_SpeedHorizontal = vHorizontal;//水平初速度 // m_SpeedVertical = vVertical;//垂直初速度 // m_theta = theta0; // m_MaxLife = life; // return bFindTheta; //} //private void CalThisTheta(float theta0, out float vHorizontal, out float vVertical, out float h, out float t) //{ // vHorizontal = m_fSpeedRate * Mathf.Cos(theta0);//水平初速度 // vVertical = m_fSpeedRate * Mathf.Sin(theta0);//垂直初速度 // if (vHorizontal < 0.001) // {//垂直投放 // vHorizontal = 0; // vVertical = m_fSpeedRate; // h = m_H0; // //////////////////////////////////////// // //ax^2+bx+c=0; // //G/2*t^2+vVertical*t-m_H0=0; // /////////////////////////////////////// // float a = m_G / 2; // float b = vVertical; // float c = 0 - m_H0; // t = b * b - 4 * a * c; // if (t >= 0) // { // b = 0 - b; // float t1 = (b - Mathf.Sqrt(t)) / (2 * a); // float t2 = (b + Mathf.Sqrt(t)) / (2 * a); // //取正的小的值 // t = t1 < t2 ? t1 : t2; // if (t < 0) // t = t1 > t2 ? t1 : t2; // } // if (t < 0) // {//垂直无解情况,无法达到高点 vt+gt^2/2=0; v=gt/2 ,totla time = 2t=-4v/g; // t = Mathf.Abs(-4 * vVertical / m_G); // } // } // else // {//不是垂直情况 // t = m_D0 / vHorizontal;//水平运行时间 // h = vVertical * t + m_G / 2 * t * t; // } // return; //} }
3_3.2抛物线飞行方式(只能向上抛,算出和对象的距离,高度与速度大小有关,当高度和自己的高度一样的时候会删除自己或者做相应的事件)这个和上面的基类完全没关系,是抛物线的一个拓展组件)
using System.Collections; using System.Collections.Generic; using UnityEngine; public class ParabolaBullet : MonoBehaviour { private const float g = 9.8f; //重力加速度 private float m_Speed = 10;//水平飞行速度(米/秒) private float m_verticalSpeed=0;//垂直速度 private Vector3 m_moveDirection;//水平移动方向 public Vector3 m_TargPos;//目标位置 //private Vector3 m_FromPos;//开始位置 private float angleSpeed; private float angle; private float m_FlyTime = 0;//飞行时间 private bool m_IsArrived = false; private float m_distance = 0f; void Start() { //m_FromPos = new Vector3(transform.position.x,transform.position.y,transform.position.z); m_distance = Vector3.Distance(transform.position, m_TargPos);//总水平飞行距离 m_Speed = m_distance; if (m_Speed < 0.5f) m_Speed = 0.5f; m_Speed = m_Speed / 1.1f;//10f; float tempTime = m_distance / m_Speed;//总水平飞行时间 float riseTime;//上升的时间 riseTime = tempTime / 2;//上升的时间等于1半水平飞行时间 m_verticalSpeed = g * riseTime; transform.LookAt(m_TargPos); float tempTan = m_verticalSpeed / m_Speed; double hu = Mathf.Atan(tempTan); angle = (float)(180 / Mathf.PI * hu); transform.eulerAngles = new Vector3(-angle, transform.eulerAngles.y, transform.eulerAngles.z); angleSpeed = angle / riseTime; m_moveDirection = m_TargPos - transform.position; } void Update() { if (transform.position.y < m_TargPos.y) { //finish m_IsArrived = true; return; } try { m_FlyTime += Time.deltaTime; float currVerticalSpeed = m_verticalSpeed - g * m_FlyTime;//当前垂直方向速度 transform.Translate(m_moveDirection.normalized * m_Speed * Time.deltaTime, Space.World);//水平方向移动 transform.Translate(Vector3.up * currVerticalSpeed * Time.deltaTime, Space.World);//垂直方向移动 float testAngle = -angle + angleSpeed * m_FlyTime; transform.eulerAngles = new Vector3(testAngle, transform.eulerAngles.y, transform.eulerAngles.z); } catch (System.Exception e) { Debug.LogError("ParabolaBullet::Update() err:" + e.Message); } } public bool IsArrived() { return m_IsArrived; } }
以上是关于Unity3D 关于飞行类型集合的主要内容,如果未能解决你的问题,请参考以下文章
我的Android进阶之旅关于Android平台获取文件的mime类型:为啥不传小写后缀名就获取不到mimeType?为啥android 4.4系统获取不到webp格式的mimeType呢?(代码片段
我的Android进阶之旅关于Android平台获取文件的mime类型:为啥不传小写后缀名就获取不到mimeType?为啥android 4.4系统获取不到webp格式的mimeType呢?(代码片段