本篇博客对直升机的飞行状态和攻击进行了简单的模拟,但是如果对仿真度没有太高的要求,这已经完全够用了。
对于旋转方式有困惑的可以参考
1.简易状态机基类
using UnityEngine;
using System.Collections;
public class FSM : MonoBehaviour
{
protected virtual void Initialize() {}
protected virtual void FSMUpdate() {}
protected virtual void FSMFixedUpdate() {}
//Use this for initialization
void Start()
{
Initialize();
}
// Update is called once per frame
void Update ()
{
FSMUpdate();
}
void FixedUpdate()
{
FSMFixedUpdate();
}
}
2.直升机状态类
这个类里设置了三个外部调用接口,分别是StartAttack,SetTargetPos,SetEnemy,大家可以根据需要自行控制
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
/// <summary>
/// 直升机状态
/// </summary>
public enum HState
{
Flying, // 飞行
Up, // 上升
Down, // 下降
Halt, // 停止
Hover, // 悬停
Aim, // 瞄准
}
public class Helicopter : FSM
{
/// <summary>
/// 攻击目标
/// </summary>
public Transform enemy;
/// <summary>
/// 移动目标点
/// </summary>
protected Vector3 targetPos;
/// <summary>
/// 攻击时间间隔
/// </summary>
protected float shootRate;
/// <summary>
/// 主旋翼
/// </summary>
public Transform mainRotor;
/// <summary>
/// 尾旋翼
/// </summary>
public Transform tailRotor;
/// <summary>
/// 发射器组
/// </summary>
public Transform[] launchs;
/// <summary>
/// 导弹
/// </summary>
public GameObject missile;
/// <summary>
/// 火箭弹
/// </summary>
public GameObject rocketSheel;
/// <summary>
/// 主旋翼旋转速度
/// </summary>
private float mainRatorSpeed;
/// <summary>
/// 尾翼旋转速度
/// </summary>
private float tailRatorSpeed;
/// <summary>
/// 最大俯仰角
/// </summary>
private float maxPitchAngle = 10;
/// <summary>
/// 最大横滚角
/// </summary>
private float maxRollAngle = 30;
/// <summary>
/// 最大升降速度
/// </summary>
private float maxLiftSpeed = 15;
/// <summary>
/// 最大移动速度
/// </summary>
private float maxMoveSpeed;
/// <summary>
/// 当前移动速度
/// </summary>
private float currentMoveSpeed;
/// <summary>
/// 旋转速度
/// </summary>
private float rotateSpeed = 1;
/// <summary>
/// 旋翼声音
/// </summary>
private AudioSource ratorSound;
/// <summary>
/// 移动后是否着陆
/// </summary>
private bool isLanding = false;
/// <summary>
/// 是否已瞄准
/// </summary>
private bool isAim = false;
public bool IsAim { get { return isAim; } }
/// <summary>
/// 武器种类
/// </summary>
private bool isMissile = false;
/// <summary>
/// 携弹量
/// </summary>
[SerializeField]
private float weaponNumber = 8;
/// <summary>
/// 直升机当前状态
/// </summary>
private HState currentFlyState = HState.Halt;
protected override void Initialize()
{
targetPos = transform.position + transform.forward * 0.1f;
ratorSound = gameObject.GetComponent<AudioSource>();
}
protected override void FSMFixedUpdate()
{
RotateRotor();
SwitchMove();
}
/// <summary>
/// 旋翼旋转
/// </summary>
private void RotateRotor()
{
if (currentFlyState != HState.Halt)
{
mainRatorSpeed = Mathf.Lerp(mainRatorSpeed, 15, Time.deltaTime);
tailRatorSpeed = Mathf.Lerp(tailRatorSpeed, 20, Time.deltaTime);
}
else
{
mainRatorSpeed = Mathf.Lerp(mainRatorSpeed, 0, Time.deltaTime);
tailRatorSpeed = Mathf.Lerp(tailRatorSpeed, 0, Time.deltaTime);
}
mainRotor.Rotate(0, mainRatorSpeed, 0);
tailRotor.Rotate(tailRatorSpeed, 0, 0);
}
/// <summary>
/// 选择飞行状态
/// </summary>
private void SwitchMove()
{
switch (currentFlyState)
{
case HState.Flying:
FlyingState();
break;
case HState.Up:
UpState();
break;
case HState.Down:
DownState();
break;
case HState.Hover:
HoverState();
break;
case HState.Halt:
break;
case HState.Aim:
AimState();
break;
default:
break;
}
}
/// <summary>
/// 飞行状态的移动
/// </summary>
private void FlyingState()
{
YawRotate(targetPos, maxRollAngle); // 偏航
float y = (targetPos - transform.position).normalized.y; // 获取y轴移动方向
currentMoveSpeed = Mathf.Lerp(currentMoveSpeed, maxMoveSpeed, Time.deltaTime);
transform.Translate(new Vector3(transform.forward.x, y, transform.forward.z) * currentMoveSpeed * Time.deltaTime, Space.World); // x,z轴方向用直升机自身方向
PitchRotate(maxPitchAngle); // 俯仰 飞行状态必俯仰
// 抵达目标点后更改状态
if (Vector3.Distance(transform.position, targetPos) < 5)
{
currentMoveSpeed = 0;
if (isLanding)
{
currentFlyState = HState.Down;
}
else
{
currentFlyState = HState.Hover;
}
}
}
/// <summary>
/// 悬停状态
/// </summary>
private void HoverState()
{
PitchRotate(0);
RollRotate(0);
}
/// <summary>
/// 降落状态
/// </summary>
private void DownState()
{
PitchRotate(0); // 俯仰修正
RollRotate(0); // 横滚修正
float height = transform.position.y - Terrain.activeTerrain.SampleHeight(transform.position);
if (height < 0.1)
{
ratorSound.Stop();
currentFlyState = HState.Halt;
return;
}
transform.Translate(Vector3.down * Time.deltaTime * Mathf.Lerp(Mathf.Min(height, maxLiftSpeed), maxLiftSpeed, Time.deltaTime));// 起飞速度以离地高度与飞行速度做插值
// 控制旋翼声音
ratorSound.volume = height / 10;
}
/// <summary>
/// 起飞状态
/// </summary>
private void UpState()
{
float height = transform.position.y - Terrain.activeTerrain.SampleHeight(transform.position);
if (height > 10)
{
currentFlyState = HState.Flying;
return;
}
YawRotate(targetPos, maxRollAngle);
transform.Translate(Vector3.up * Time.deltaTime * Mathf.Lerp(height, maxLiftSpeed, Time.deltaTime));// 起飞速度以离地高度与飞行速度做插值
if (!ratorSound.isPlaying)
{
ratorSound.Play();
}
// 控制旋翼声音
ratorSound.volume = height / 10;
}
/// <summary>
/// 攻击状态
/// </summary>
private void AimState()
{
// 俯仰和偏航到指定角度
Vector3 dir = enemy.position - (launchs[0].position + launchs[1].position) / 2;
Quaternion rotation = Quaternion.LookRotation(dir);
transform.rotation = Quaternion.Slerp(transform.rotation, rotation, Time.deltaTime);
if (Vector3.Angle(dir, transform.forward) < 0.2)
{
isAim = true;
}
else
{
isAim = false;
}
}
/// <summary>
/// 偏航 机身旋转
/// </summary>
private void YawRotate(Vector3 _targetPos, float rollAngle)
{
float height = transform.position.y - Terrain.activeTerrain.SampleHeight(transform.position); // 获取当前物体相对地面高度
if (height < 5)
{
return;
}
Vector3 dir = _targetPos - transform.position;
Quaternion targetRotation = Quaternion.LookRotation(dir);
float angle = transform.eulerAngles.y - targetRotation.eulerAngles.y;
if (angle < 0) angle += 360;
if (angle > rotateSpeed && angle < 180)
{
transform.Rotate(0f, -rotateSpeed, 0f, Space.World);
RollRotate(rollAngle);
}
else if (angle < 360 - rotateSpeed && angle > 180)
{
transform.Rotate(0f, rotateSpeed, 0f, Space.World);
RollRotate(-rollAngle);
}
else
{
// 偏航完成
if (currentFlyState == HState.Flying)
{
RollRotate(0);
}
}
}
/// <summary>
/// 横滚 左倾右倾
/// </summary>
/// <param name="target_z">横滚目标角度</param>
private void RollRotate(float target_z)
{
Vector3 currentEuler = transform.eulerAngles;
Quaternion from = Quaternion.Euler(transform.eulerAngles);
Quaternion to = Quaternion.Euler(new Vector3(currentEuler.x, currentEuler.y, target_z));
transform.rotation = Quaternion.Lerp(from, to, Time.deltaTime);
}
/// <summary>
/// 俯仰 前倾
/// </summary>
/// <param name="target_x">俯仰目标角度</param>
private void PitchRotate(float target_x)
{
Vector3 currentEuler = transform.eulerAngles;
Quaternion from = Quaternion.Euler(transform.eulerAngles);
Quaternion to = Quaternion.Euler(new Vector3(target_x, currentEuler.y, currentEuler.z));
transform.rotation = Quaternion.Lerp(from, to, Time.deltaTime);
}
/// <summary>
/// 初始化火力
/// </summary>
/// <param name="_launch">发射架</param>
/// <param name="_target">打击目标</param>
/// <param name="_Missile">导弹预制</param>
/// <param name="_velocity">导弹速度</param>
private void InitFirePower(Transform _launch, float _velocity = 400f)
{
int index;
if (isMissile)
{
//实例化导弹实体,项目中要用对象池改写
index = Random.Range(3, 4);
GameObject _missile = Instantiate(missile, launchs[index].position, _launch.rotation);
//实例化出来的导弹上挂导弹飞行的代码
}
else
{
//实例化火箭弹,项目中要用对象池改写
index = Random.Range(0, 2);
GameObject _rocketSheel = Instantiate(rocketSheel, launchs[index].position, _launch.rotation);
//实例化出来的火箭弹挂火箭弹飞行的代码
}
launchs[index].Find("FireEffect").GetComponent<ParticleSystem>().Play();
launchs[index].GetComponent<AudioSource>().Play();
}
/// <summary>
/// 攻击 startAttack为外部调用接口
/// </summary>
private IEnumerator Attack(float delay = 0.1f)
{
yield return new WaitForSeconds(delay);
if (launchs.Length > 0)
{
for (int i = 0; i < weaponNumber; i++)
{
InitFirePower(launchs[0]);
yield return new WaitForSeconds(0.2f);
}
}
}
public void StartAttack()
{
StartCoroutine(Attack());
}
/// <summary>
/// 设置目标点
/// </summary>
/// <param name="_pos"></param>
/// <param name="_speed"></param>
/// <param name="_isLanding"></param>
public void SetTargetPos(Vector3 _pos, float _speed, bool _isLanding)
{
targetPos = _pos;
maxMoveSpeed = _speed;
isLanding = _isLanding;
isAim = false;
switch (currentFlyState)
{
case HState.Down:
case HState.Halt:
currentFlyState = HState.Up;
break;
case HState.Hover:
case HState.Aim:
currentFlyState = HState.Flying;
break;
default:
break;
}
}
/// <summary>
/// 设置攻击目标
/// </summary>
/// <param name="_enemy"></param>
/// <param name="isMissile">武器是否是导弹</param>
public void SetEnemy(Transform _enemy, bool _isMissile = true)
{
if (launchs.Length <= 0)
{
return;
}
enemy = _enemy;
isMissile = _isMissile;
if (currentFlyState == HState.Hover || currentFlyState == HState.Flying)
{
currentFlyState = HState.Aim;
}
}
}