采用了MVC框架,以四轴驱动的方式对直升机的启动、飞行做了仿真模拟,包括但不限于参数设置、启动发动机和旋翼、数据显示、HUD、UI、升降、水平移动、转弯等。
文末有完整的工程资源链接。
3d8111f9b.png" alt="954961c85a5c405c9429a5e3d8111f9b.png" />
1.旋翼
直升机飞行过程中,有顶部的主旋翼和尾部的尾桨需要转动。
在旋翼加速状态时,悬疑的转速逐渐增加到最大。旋翼转速达到最大时,直升机才能起飞。起飞后,旋翼转速始终保持不变。
旋翼的脚本RotorRotation.cs拖拽到主旋翼和尾桨上。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;/// <summary>
/// 旋翼旋转
/// </summary>
public class RotorRotation : MonoBehaviour
{public enum EnumRotateAxis{X, Y, Z}public EnumRotateAxis RotateAxis; // 螺旋桨转动的轴public bool IsReverse; // 是否反方向旋转public float Speed; // 转速,度private Vector3 m_V3Euler;private float m_RotateDegree; // 度/// <summary>/// Start is called before the first frame update/// </summary>void Start(){m_V3Euler = transform.localEulerAngles;}/// <summary>/// Update is called once per frame/// </summary>void Update(){if (IsReverse){m_RotateDegree -= Speed * Time.deltaTime;}else{m_RotateDegree += Speed * Time.deltaTime;}// 防止m_RotateDegree数值过大m_RotateDegree = m_RotateDegree % 360;switch (RotateAxis){case EnumRotateAxis.X:transform.localRotation = Quaternion.Euler(m_RotateDegree, m_V3Euler.y, m_V3Euler.z);break;case EnumRotateAxis.Y:transform.localRotation = Quaternion.Euler(m_V3Euler.x, m_RotateDegree, m_V3Euler.z);break;default:transform.localRotation = Quaternion.Euler(m_V3Euler.x, m_V3Euler.y, m_RotateDegree);break;}}
}
代码中定义了一个枚举EnumRotateAxis,根据枚举可以确定不同螺旋桨的旋转方向。
在Update函数中使用switch语句根据旋转轴使用Quaternion.Euler方法旋转轴。
在Update函数中有这样的一条语句:rotateDegree = rotateDegree % 360; 该语句对使用转速算出来的度数对360取余,我个人理解的主要目的是为了防止转速较高旋转的过快。
旋转轴的选择和是否反转,取决于模型。我的工程里主旋翼选择Y轴、IsReverse=false,尾桨选择X轴,IsReverse=true。
2.输入管理器
直升机的操控较为复杂,所以我没有用Unity的InputManager,而是自己写了一个InputManager.cs。
转向踏板,控制转向。总距操纵杆,控制主旋翼的桨叶角,也就是控制升降。周期变距杆,控制水平方向的移动。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;/// <summary>
/// 输入管理器
/// </summary>
public class InputManager : MonoBehaviour
{public float Sensitivity = 3.0f; // 敏感度[Space(20)][Header("转向踏板")]public KeyCode KeyTurnLeft = KeyCode.A;public KeyCode KeyTurnRight = KeyCode.D;[Header("总距操纵杆")]public KeyCode KeyMoveDown = KeyCode.S;public KeyCode KeyMoveUp = KeyCode.W;[Header("周期变距杆")]public KeyCode KeyMoveLeft = KeyCode.LeftArrow;public KeyCode KeyMoveRight = KeyCode.RightArrow;public KeyCode KeyMoveBack = KeyCode.DownArrow;public KeyCode KeyMoveForward = KeyCode.UpArrow;[Space(20)][Header("发动机按钮")]public KeyCode keyMenu = KeyCode.Escape;[Header("发动机按钮")]public KeyCode KeyEngine = KeyCode.E;[Header("旋翼按钮")]public KeyCode KeyRotor = KeyCode.R;[Header("切换视角按钮")]public KeyCode KeyPerspective = KeyCode.V;[Header("地图按钮")]public KeyCode KeyMap = KeyCode.M;[Header("HUD按钮")]public KeyCode KeyHUD = KeyCode.H;[Space(20)][Header("输入值")][Range(-1, 1)] public float TurnLeftRight;[Range(-1, 1)] public float MoveDownUp;[Range(-1, 1)] public float MoveLeftRight;[Range(-1, 1)] public float MoveBackFoward;/// <summary>/// Update is called once per frame/// </summary>void Update(){// 闲置输入值的大小MoveBackFoward = Mathf.Clamp(MoveBackFoward, -1, 1);TurnLeftRight = Mathf.Clamp(TurnLeftRight, -1, 1);MoveLeftRight = Mathf.Clamp(MoveLeftRight, -1, 1);MoveDownUp = Mathf.Clamp(MoveDownUp, -1, 1);}/// <summary>/// 固定帧更新/// </summary>void FixedUpdate(){if (Input.GetKey(KeyTurnLeft)){TurnLeftRight = Mathf.Lerp(TurnLeftRight, -1, Time.deltaTime * Sensitivity);}else if (Input.GetKey(KeyTurnRight)){TurnLeftRight = Mathf.Lerp(TurnLeftRight, 1, Time.deltaTime * Sensitivity);}else if ((Input.GetKey(KeyTurnLeft) && Input.GetKey(KeyTurnRight)) || (!Input.GetKey(KeyTurnLeft) && !Input.GetKey(KeyTurnRight))){TurnLeftRight = Mathf.Lerp(TurnLeftRight, 0, Time.deltaTime * Sensitivity);}if (TurnLeftRight < -0.99f){TurnLeftRight = -1;}else if (TurnLeftRight > -0.01f && TurnLeftRight < 0.01f){TurnLeftRight = 0;}else if (TurnLeftRight > 0.99f){TurnLeftRight = 1;}if (Input.GetKey(KeyMoveBack)){MoveBackFoward = Mathf.Lerp(MoveBackFoward, -1, Time.deltaTime * Sensitivity);}else if (Input.GetKey(KeyMoveForward)){MoveBackFoward = Mathf.Lerp(MoveBackFoward, 1, Time.deltaTime * Sensitivity);}else if ((Input.GetKey(KeyMoveBack) && Input.GetKey(KeyMoveForward)) || (!Input.GetKey(KeyMoveBack) && !Input.GetKey(KeyMoveForward))){MoveBackFoward = Mathf.Lerp(MoveBackFoward, 0, Time.deltaTime * Sensitivity);}if (MoveBackFoward < -0.99f){MoveBackFoward = -1;}else if (MoveBackFoward > -0.01f && MoveBackFoward < 0.01f){MoveBackFoward = 0;}else if (MoveBackFoward > 0.99f){MoveBackFoward = 1;}if (Input.GetKey(KeyMoveLeft)){MoveLeftRight = Mathf.Lerp(MoveLeftRight, -1, Time.deltaTime * Sensitivity);}else if (Input.GetKey(KeyMoveRight)){MoveLeftRight = Mathf.Lerp(MoveLeftRight, 1, Time.deltaTime * Sensitivity);}else if ((Input.GetKey(KeyMoveLeft) && Input.GetKey(KeyMoveRight)) || (!Input.GetKey(KeyMoveLeft) && !Input.GetKey(KeyMoveRight))){MoveLeftRight = Mathf.Lerp(MoveLeftRight, 0, Time.deltaTime * Sensitivity);}if (MoveLeftRight < -0.99f){MoveLeftRight = -1;}else if (MoveLeftRight > -0.01f && MoveLeftRight < 0.01f){MoveLeftRight = 0;}else if (MoveLeftRight > 0.99f){MoveLeftRight = 1;}if (Input.GetKey(KeyMoveDown)){MoveDownUp = Mathf.Lerp(MoveDownUp, -1, Time.deltaTime * Sensitivity);}else if (Input.GetKey(KeyMoveUp)){MoveDownUp = Mathf.Lerp(MoveDownUp, 1, Time.deltaTime * Sensitivity);}else if ((Input.GetKey(KeyMoveDown) && Input.GetKey(KeyMoveUp)) || (!Input.GetKey(KeyMoveDown) && !Input.GetKey(KeyMoveUp))){MoveDownUp = Mathf.Lerp(MoveDownUp, 0, Time.deltaTime * Sensitivity);}if (MoveDownUp < -0.99f){MoveDownUp = -1;}else if (MoveDownUp > -0.01f && MoveDownUp < 0.01f){MoveDownUp = 0;}else if (MoveDownUp > 0.99f){MoveDownUp = 1;}}
}
3.直升机驾驶
创建脚本HelicopterDrive.cs,拖拽到Player上。
3.1.处理发动机和旋翼
3.1.1.直升机的状态
/// <summary>
/// 直升机状态
/// </summary>
public enum EnumHeliState
{Idle, // 闲置EngineOn, // 发动机打开RotorAC, // 旋翼加速RotorDC, // 旋翼减速RotorMax // 旋翼最大速(可以飞行)
}
3.1.2.处理发动机
按E键控制发动机的开关,让直升机在闲置状态和发动机开启状态中切换,并控制音频和旋翼的转速。
/// <summary>/// 处理发动机/// </summary>void HandleEngine(){// 控制发动机if (Input.GetKeyUp(Controller.Inputs.KeyEngine)){// 当直升机处于闲置状态时,可开启发动机,进入发动机启动状态if (HeliState == EnumHeliState.Idle){// 调整发动机转速DOTween.To(() => EngineSpeed, x => EngineSpeed = x, EngineSpeedMax, EngineTime);// 控制音频AsEngine.Play();StartCoroutine(DelayHandleEngine(EngineTime, true));}// 当直升机处于发动机启动状态时,可关闭发动机,进入闲置状态else if (HeliState == EnumHeliState.EngineOn){// 调整发动机转速DOTween.To(() => EngineSpeed, x => EngineSpeed = x, 0, EngineTime);// 控制音频AsEngine.Stop();StartCoroutine(DelayHandleEngine(EngineTime, false));}}// 控制音高AsEngine.pitch = 3.0f * (EngineSpeed / EngineSpeedMax);}/// <summary>/// 延时处理发动机/// </summary>/// <param name="t"></param>/// <param name="b"></param>/// <returns></returns>IEnumerator DelayHandleEngine(float t, bool b){yield return new WaitForSeconds(t);// 切换直升机状态if (b){ChangeHeliState(EnumHeliState.EngineOn);}else{ChangeHeliState(EnumHeliState.Idle);}}
其中用到了DOTween.To方法,可以让数值、向量等像DOTween动画一样变化。
DOTween.To(()=>变量,x=> 变量=x , 变量目标值, 过渡时间);
3.1.3.处理旋翼
按R键控制旋翼的开关,让直升机在发动机开启状态和发动机最大苏状态中切换,并控制音频和旋翼的转速。其中旋翼加速状态和旋翼减速状态是过渡状态。
/// <summary>/// 处理旋翼/// </summary>void HandleRotor(){// 传递旋翼转速MainRotor.Speed = m_MainRotorSpeed;TailRotor.Speed = m_TailRotorSpeed;// 控制旋翼if (Input.GetKeyUp(Controller.Inputs.KeyRotor)){// 当直升机处于发动机启动状态时,可开启旋翼,进入旋翼加速状态,延时后为旋翼最大速状态if (HeliState == EnumHeliState.EngineOn){// 切换直升机状态ChangeHeliState(EnumHeliState.RotorAC);// 调整旋翼转速DOTween.To(() => m_MainRotorSpeed, x => m_MainRotorSpeed = x, MainRotorSpeedMax, RotorTime);DOTween.To(() => m_TailRotorSpeed, x => m_TailRotorSpeed = x, TailRotorSpeedMax, RotorTime);// 控制音频AsMainRotor.Play();AsTailRotor.Play();StartCoroutine(DelayHandleRotor(RotorTime, true));}else if (HeliState == EnumHeliState.RotorMax){if (IsLand){// 切换直升机状态ChangeHeliState(EnumHeliState.RotorDC);// 调整旋翼转速DOTween.To(() => m_MainRotorSpeed, x => m_MainRotorSpeed = x, 0, RotorTime * 0.5f);DOTween.To(() => m_TailRotorSpeed, x => m_TailRotorSpeed = x, 0, RotorTime * 0.5f);// 控制音频AsMainRotor.Stop();AsTailRotor.Stop();StartCoroutine(DelayHandleRotor(RotorTime * 0.5f, false));}}}// 控制音高AsMainRotor.pitch = m_MainRotorSpeed / MainRotorSpeedMax;AsTailRotor.pitch = 1.5f * (m_TailRotorSpeed / TailRotorSpeedMax);// 处理主旋翼周期变距倾转TfFeather.localEulerAngles = new Vector3(V2Cyclic.y, 0, -V2Cyclic.x) * 7.5f;}/// <summary>/// 延时处理旋翼/// </summary>/// <param name="t"></param>/// <param name="b"></param>/// <returns></returns>IEnumerator DelayHandleRotor(float t, bool b){yield return new WaitForSeconds(t);// 切换直升机状态if (b){ChangeHeliState(EnumHeliState.RotorMax);}else{ChangeHeliState(EnumHeliState.EngineOn);}}
这里有周期变距(主旋翼的父物体)角度的调整。
这里说一下大多数直升机的飞行原理:
周期变距向前俯,向前飞。周期变距向后仰,向后飞。周期变距向左歪,向左飞。周期变距向右歪,向后飞。
主旋翼桨叶角的大小控制升力的大小。尾桨桨叶角的大小控制转向力的大小。
目录
1.旋翼
2.输入管理器
3.直升机驾驶
3.1.处理发动机和旋翼
3.1.1.直升机的状态
3.1.2.处理发动机
3.1.3.处理旋翼
3.2.输入值与受力分析
3.2.1.处理输入
3.2.2.处理受力与受力分析
3.2.3.处理数据
3.3.直升机的移动与旋转
3.2.输入值与受力分析
3.2.1.处理输入
/// <summary>/// 处理输入/// </summary>void HandleInput(){// 处理输入值Pedals = Controller.Inputs.TurnLeftRight;Collective = Controller.Inputs.MoveDownUp;V2Cyclic = new Vector3(Controller.Inputs.MoveLeftRight, Controller.Inputs.MoveBackFoward);// 处理传动值TransmissionH1 = Mathf.Lerp(TransmissionH1, Pedals, Time.deltaTime);TransmissionV1 = Mathf.Lerp(TransmissionV1, Collective, Time.deltaTime * 0.2f);TransmissionH2 = Mathf.Lerp(TransmissionH2, V2Cyclic.x, Time.deltaTime * 0.2f);TransmissionV2 = Mathf.Lerp(TransmissionV2, V2Cyclic.y, Time.deltaTime * 0.05f);}
由于直升机的重量和阻力,所以接收输入之后还需要再次差值运输,达到平滑延迟的效果。
3.2.2.处理受力与受力分析
/// <summary>/// 处理受力/// </summary>void HandleForce(){if (HeliState == EnumHeliState.RotorMax){// 处理垂直方向的力if (TransmissionV1 > 0.1f){m_UpForce = Mathf.Lerp(m_UpForce, m_UpForceStasis + ((UpForceMax - m_UpForceStasis) * TransmissionV1), Time.deltaTime);}else if (TransmissionV1 >= -0.1f && TransmissionV1 <= 0.1f){m_UpForce = Mathf.Lerp(m_UpForce, m_UpForceStasis, Time.deltaTime);}else if (TransmissionV1 < -0.1f){if (m_UpForceStasis == 0){m_UpForce = Mathf.Lerp(m_UpForce, 0, Time.deltaTime);}else{m_UpForce = Mathf.Lerp(m_UpForce, m_UpForceStasis + (m_UpForceStasis * TransmissionV1), Time.deltaTime);}}if (!IsLand){// 处理水平左右方向的力if (TransmissionH2 > 0.1f){m_HorizontalForce = Mathf.Lerp(m_HorizontalForce, ForwardForceMax * TransmissionH2 * 0.25f, Time.deltaTime);}else if (TransmissionH2 >= -0.1f && TransmissionH2 <= 0.1f){m_HorizontalForce = Mathf.Lerp(m_HorizontalForce, 0, Time.deltaTime);}else if (TransmissionH2 < 0.1f){m_HorizontalForce = Mathf.Lerp(m_HorizontalForce, ForwardForceMax * TransmissionH2 * 0.25f, Time.deltaTime);}// 处理水平前后方向的力if (TransmissionV2 > 0.1f){m_ForwardForce = Mathf.Lerp(m_ForwardForce, ForwardForceMax * TransmissionV2, Time.deltaTime);}else if (TransmissionV2 >= -0.1f && TransmissionV2 <= 0.1f){m_ForwardForce = Mathf.Lerp(m_ForwardForce, 0, Time.deltaTime);}else if (TransmissionV2 < 0.1f){m_ForwardForce = Mathf.Lerp(m_ForwardForce, ForwardForceMax * TransmissionV2 * 0.25f, Time.deltaTime);}// 处理转向力if (TransmissionH1 > 0.1f){m_TurnForce = Mathf.Lerp(m_TurnForce, TurnForceMax * TransmissionH1, Time.deltaTime);}else if (TransmissionH1 >= -0.1f && TransmissionH1 <= 0.1f){m_TurnForce = Mathf.Lerp(m_TurnForce, 0, Time.deltaTime);}else if (TransmissionH1 < 0.1f){m_TurnForce = Mathf.Lerp(m_TurnForce, TurnForceMax * TransmissionH1, Time.deltaTime);}}}}
3.2.3.处理数据
/// <summary>/// 处理数据/// </summary>void HandleData(){// 处理高度和判断是否着陆RaycastHit hit;Vector3 direction = transform.TransformDirection(Vector3.down);Ray ray = new Ray(transform.position, direction);if (Physics.Raycast(ray, out hit, 3000, LayerGround)){Height = hit.distance;if (Height > HeightMin * 0.01f){IsLand = false;}else{IsLand = true;}}// 处理海拔Altitude = Controller.TfHeliTransform.position.y;// 处理角度if (Controller.TfHeliTransform.eulerAngles.x <= -180){PitchAngle = Controller.TfHeliTransform.eulerAngles.x + 360;}else if (Controller.TfHeliTransform.eulerAngles.x > -180 && Controller.TfHeliTransform.eulerAngles.x <= 180){PitchAngle = Controller.TfHeliTransform.eulerAngles.x;}else if (Controller.TfHeliTransform.eulerAngles.x > 180){PitchAngle = Controller.TfHeliTransform.eulerAngles.x - 360;}if (Controller.TfHeliTransform.eulerAngles.y <= -180){YawAngle = Controller.TfHeliTransform.eulerAngles.y + 360;}else if (Controller.TfHeliTransform.eulerAngles.y > -180 && Controller.TfHeliTransform.eulerAngles.y <= 180){YawAngle = Controller.TfHeliTransform.eulerAngles.y;}else if (Controller.TfHeliTransform.eulerAngles.y > 180){YawAngle = Controller.TfHeliTransform.eulerAngles.y - 360;}if (Controller.TfHeliTransform.eulerAngles.z <= -180){RollAngle = Controller.TfHeliTransform.eulerAngles.z + 360;}else if (Controller.TfHeliTransform.eulerAngles.z > -180 && Controller.TfHeliTransform.eulerAngles.z <= 180){RollAngle = Controller.TfHeliTransform.eulerAngles.z;}else if (Controller.TfHeliTransform.eulerAngles.z > 180){RollAngle = Controller.TfHeliTransform.eulerAngles.z - 360;}// 处理均衡升力if (IsLand){m_UpForceStasis = 0;}else{m_UpForceStasis = m_Rigidbody.mass * -Physics.clothGravity.y /Mathf.Cos((Mathf.Abs(PitchAngle) + Mathf.Abs(RollAngle)) * Mathf.Deg2Rad);}// 处理速度HorizontalVelocity = Vector3.Distance(new Vector3(m_Rigidbody.velocity.x, 0, m_Rigidbody.velocity.z), Vector3.zero);VerticalVelocity = m_Rigidbody.velocity.y;ForwardVelocity = m_Rigidbody.velocity.z;RightVelocity = m_Rigidbody.velocity.z;YawVelocity = m_Rigidbody.angularVelocity.y * Mathf.Rad2Deg;}
键盘的操作不如操纵杆,为了便于操作,定义了一个升力的均衡值m_UpForceStasis。
当直升机着陆时,m_UpForceStasis为0,即在不控制直升机升降时,升力会自动恢复为0。
当直升机离地时,m_UpForceStasis为直升机收到的重力,直升机会悬停。
3.3.直升机的移动与旋转
这些都是在FixedUpdate()里的。
/// <summary>/// 升降/// </summary>void FixedLift(){m_Rigidbody.AddRelativeForce(Vector3.up * m_UpForce);}/// <summary>/// 移动/// </summary>void FixedMove(){m_Rigidbody.AddRelativeForce(Vector3.right * m_HorizontalForce);m_Rigidbody.AddRelativeForce(Vector3.forward * m_ForwardForce);}/// <summary>/// 转向/// </summary>void FixedTurn(){m_Rigidbody.AddRelativeTorque(0, m_TurnForce, 0);}float tx = 0, ty = 0;/// <summary>/// 倾斜和左右移动/// </summary>void FixedTilt(){tx = Mathf.Lerp(tx, V2Cyclic.y * 7.5f, Time.deltaTime * 0.5f) / (m_Rigidbody.mass / 9000);ty = Mathf.Lerp(ty, -V2Cyclic.x * 25.0f, Time.deltaTime * 0.5f) / (m_Rigidbody.mass / 9000);//m_V3BaseTilying = new Vector3(m_ForwardForce / m_Rigidbody.mass / 94, transform.localEulerAngles.y, -m_HorizontalForce / m_Rigidbody.mass / 4);m_V3BaseTilying = new Vector3(tx, transform.localEulerAngles.y, ty);transform.rotation = Quaternion.Euler(m_V3BaseTilying);}
直升机的受力已经写好了,所以这里让受力与传动值或者输入值绑定就可以了。
3.4.布朗运动
为了让直升机飞行的姿态更加自然,我们让直升机在飞行时进行布朗运动。布朗运动的振幅与直升机的高度相关。
/// <summary>
/// 布朗运动
/// </summary>
void FixedBrownian()
{if (Height <= HeightMin * 0.1f){Brownian.V3PositionScale = Vector3.zero;Brownian.V3RotationScale = Vector3.zero;}else if (Height > HeightMin * 0.1f && Height < HeightMin * 1.1f){Brownian.V3PositionScale = new Vector3(0.25f, 1f, 0.25f) * (Height - HeightMin * 0.1f) / HeightMin;Brownian.V3RotationScale = new Vector3(1f, 0.5f, 0.25f) * (Height - HeightMin * 0.1f) / HeightMin;}else if (Height > HeightMin * 1.1f){Brownian.V3PositionScale = new Vector3(0.25f, 1f, 0.25f);Brownian.V3RotationScale = new Vector3(1f, 0.5f, 0.25f);}
}
布朗运动的插件我会免费放在这里,BrownianMotion.cs的脚本拖在这里。
Unity3D 布朗运动算法插件 Brownian Motion
3d9812bfe.png" alt="a3fece2608f4463eb6ef5fa3d9812bfe.png" />
4.用户操作界面
4.1.视图层和仪表盘显示器界面的定义
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;/// <summary>
/// 视图层
/// </summary>
public class ViewLayer : MonoBehaviour
{public ControllerLayer Controller; // 控制层public MonitorUI Monitor; // 仪表盘显示器UI[Space(50)][Header("…………UI…………")][Header("大地图")]public Image PageMap; // 大地图页面[Space(20)][Header("HUD")]public Image PageHUD; // HUD页面[Space(50)][Header("…………其它…………")]public Transform TfFollow; // 跟随锚点public Camera CamMain; // 主摄像机public Transform TfTarget; // 主摄像机目标public Camera CamFPS; // 舱内视角摄像机public Camera CamMap; // 大地图摄像机
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;/// <summary>
/// 仪表盘显示器UI
/// </summary>
public class MonitorUI : MonoBehaviour
{[Header("HUD")]public Image ImgBG; // 背景图public Image ImgPitch; // 俯仰角public Text TextRollAngle; // 横滚角[Space(10)][Header("速度")]public Image ImgHorizontalVelocity; // 水平速度public Text TextHorizontalSpeed;public Slider SldVerticalVelocity; // 垂直速度public Text TextVerticalVelocity;public Slider SldForwardVelocity; // 前进速度public Text TextForwardVelocity;public Slider SldRightVelocity; // 横移速度public Text TextRightVelocity;public Slider SldYawVelocity; // 转向速度public Text TextYawVelocity;[Space(10)][Header("小地图")]public Image ImgHSI; // 罗盘
}
4.2.处理输入
依然是通过我自己写的InputManager来控制鼠标键盘的输入。
/// <summary>/// 处理输入/// </summary>void HandleInput(){if (Input.GetKeyUp(Inputs.KeyPerspective)){SwitchPerspective();}if (Input.GetKeyUp(Inputs.KeyMap)){SwitchMapPage();}if (Input.GetKeyUp(Inputs.KeyHUD)){SwitchHUDPage();}}
4.3.主视角、大地图视角跟随直升机
/// <summary>/// 跟随直升机/// </summary>void FollowHelicopter(){// Follow同步View.TfFollow.position = HeliDrive.transform.position;View.TfFollow.eulerAngles = new Vector3(0, HeliDrive.transform.eulerAngles.y, 0);// 大地图摄像机同步位置View.CamMap.transform.position = new Vector3(HeliDrive.transform.position.x, 9000, HeliDrive.transform.position.z);}
4.4.显示数据
在仪表盘显示器的画布中显示数据。反正这些都不需要操作,所有之后再通过图层打在UI画布上。
/// <summary>/// 显示数据/// </summary>public void FixedUpdateMonitor(){View.Monitor.ImgBG.transform.localPosition = new Vector3(0, HeliDrive.PitchAngle * 10, 0);View.Monitor.ImgPitch.transform.localPosition = new Vector3(0, HeliDrive.PitchAngle * 10, 0);View.Monitor.ImgBG.transform.localEulerAngles = new Vector3(0, 0, -HeliDrive.RollAngle);View.Monitor.TextRollAngle.text = HeliDrive.RollAngle.ToString("f1");View.Monitor.ImgHorizontalVelocity.transform.localEulerAngles = new Vector3(0, 0, -HeliDrive.HorizontalVelocity * 3.6f);View.Monitor.TextHorizontalSpeed.text = (HeliDrive.HorizontalVelocity * 3.6f).ToString("f0") + "km/h";View.Monitor.SldVerticalVelocity.value = HeliDrive.VerticalVelocity;View.Monitor.TextVerticalVelocity.text = HeliDrive.VerticalVelocity.ToString("f1") + "m/s";View.Monitor.SldForwardVelocity.value = HeliDrive.ForwardVelocity;View.Monitor.TextForwardVelocity.text = HeliDrive.ForwardVelocity.ToString("f1") + "m/s";View.Monitor.SldRightVelocity.value = HeliDrive.RightVelocity;View.Monitor.TextRightVelocity.text = HeliDrive.RightVelocity.ToString("f1") + "m/s";View.Monitor.SldYawVelocity.value = HeliDrive.YawVelocity;View.Monitor.TextYawVelocity.text = HeliDrive.YawVelocity.ToString("f2") + "°/s";View.Monitor.ImgHSI.transform.localEulerAngles = new Vector3(0, 0, HeliDrive.YawAngle);}
4.5.切换视角
public EnumPerspectiveType PerspectiveType; // 视角public enum EnumPerspectiveType{TPS, // 与距离舱外视角FPS // 驾驶员仓内视角}
/// <summary>/// 切换视角/// </summary>public void SwitchPerspective(){if (PerspectiveType == EnumPerspectiveType.FPS){OnTPS();}else if (PerspectiveType == EnumPerspectiveType.TPS){OnFPS();}}public void OnFPS(){PerspectiveType = EnumPerspectiveType.FPS;View.CamMain.gameObject.SetActive(false);View.CamFPS.gameObject.SetActive(true);}public void OnTPS(){PerspectiveType = EnumPerspectiveType.TPS;View.CamMain.gameObject.SetActive(true);View.CamFPS.gameObject.SetActive(false);}
4.6.控制页面
public void SwitchMapPage(){if (View.PageMap.gameObject.active){View.PageMap.gameObject.SetActive(false);}else{View.PageMap.gameObject.SetActive(true);View.PageHUD.gameObject.SetActive(false);}}/// <summary>/// 开关HUD页面/// </summary>public void SwitchHUDPage(){if (View.PageHUD.gameObject.active){View.PageHUD.gameObject.SetActive(false);}else{View.PageMap.gameObject.SetActive(false);View.PageHUD.gameObject.SetActive(true);}}
5.资源链接
Unity3D 完全的直升机控制器插件(虚拟仿真级别)