Unity 简单的飞机控制器脚本
前言
前几天突发奇想的想做一个3d飞机游戏,于是先自己写了一下简单的控制器,发现效果不是很好。之后浏览了一下网上的资源发现免费的不全,别的要钱。只好自己设计了。下面是我设计完后的脚本。
代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
[Tooltip("限飞速度,限制飞机能否起飞")]
/// <summary>
/// 限飞速度
/// </summary>
public float GroundMaxSpeed = 5;
[Tooltip("水平的最大速度")]
/// <summary>
/// 水平的最大速度
/// </summary>
public float HorizotalMaxSpeed = 5;
[Tooltip("垂直最大速度")]
/// <summary>
/// 垂直最大速度
/// </summary>
public float VerticalMaxSpeed = 5;
[Tooltip("最大飞行高度")]
/// <summary>
/// 最大飞行高度
/// </summary>
public float MaxFlyHeight = 100;
[Tooltip("每次旋转的角度分量")]
/// <summary>
/// 每次旋转的角度增量
/// </summary>
public float EveryRotateValue = 5;
[Tooltip("水平移动加速度")]
/// <summary>
/// 水平移动加速度
/// </summary>
public float AccSpeed = 10;
[Tooltip("垂直上升加速度")]
/// <summary>
/// 上升加速度
/// </summary>
public float UpAccSpeed = 10;
[Tooltip("平衡轴的名称:'x+y','x+z','y+z'")]
/// <summary>
/// 平衡轴的名称 x,y,z
/// </summary>
public string BalancedAxis = null;
[Tooltip("平衡回调速度,值越大速度越慢,越小速度越快")]
/// <summary>
/// 平衡回调速度,值越大速度越慢,越小速度越快
/// </summary>
public float ReBalanceSpeed = 10;
/// <summary>
/// z轴旋转总值,自身
/// </summary>
float zLocalRotateValue = 0;
/// <summary>
/// x轴旋转总值,自身
/// </summary>
float xLocalRotateValue = 0;
/// <summary>
/// y轴旋转总值,自身
/// </summary>
float yLocalRotateValue = 0;
/// <summary>
/// 是否能够发动上升引擎
/// </summary>
bool CanFly = false;
/// <summary>
/// 实时更新的平衡轴的值
/// </summary>
Vector3 UpdateBalancedValue = new Vector3(0, 0, 0);
/// <summary>
/// 飞机水平两个平衡轴的值,本例用模型为x轴
/// </summary>
Vector3 BalanceAxisValue;
/// <summary>
/// 是否处于平衡调整状态
/// </summary>
bool IsBalancing = false;
/// <summary>
/// 自身的刚体
/// </summary>
Rigidbody rigidbody;
// Start is called before the first frame update
void Start()
{
//获取初始坐标作为水平位置直接关系轴的值,本例为x,z轴
BalanceAxisValue = new Vector3(transform.localEulerAngles.x,
transform.localEulerAngles.y,
transform.localEulerAngles.z);
//初始化三个旋转总值的初始值
Update3LocalRotateValue();
//获取当前的刚体组件
rigidbody = GetComponent<Rigidbody>();
if (!rigidbody)
Debug.LogError("刚体未添加!");
}
// Update is called once per frame
void Update()
{
if (!IsBalancing)
{
//限制速度
LimitSpeed();
//实时检测是否可以起飞
SpeedCheck();
//行为检测
MainController();
}
}
/// <summary>
/// 检查平衡轴字符串输入是否正确
/// </summary>
bool CheckBlanceAxisString()
{
switch (BalancedAxis)
{
//输入正确
case "x+y":
return true;
//输入正确
case "x+z":
return true;
case "y+z":
return true;
case "y+x":
BalancedAxis = "x+y";
return true;
case "z+x":
BalancedAxis = "x+z";
return true;
case "z+y":
BalancedAxis = "y+z";
return true;
//除开以上的所有结果视为错误
default:
return false;
}
}
/// <summary>
/// 飞机行为控制函数
/// </summary>
void MainController()
{
//前进
if (Input.GetKeyDown(KeyCode.W) || Input.GetKey(KeyCode.W))
{
//调用GoFarward前进
GoFarward();
}
//后退
if (Input.GetKeyDown(KeyCode.S) || Input.GetKey(KeyCode.S))
{
//调用GoBack后退
GoBack();
}
//左移
if (Input.GetKeyDown(KeyCode.A) || Input.GetKey(KeyCode.A))
{
//调用GoLeft左侧飞
GoLeft();
}
//右移
if (Input.GetKeyDown(KeyCode.D) || Input.GetKey(KeyCode.D))
{
//调用GoRight右侧飞
GoRight();
}
//飞机上升
if (CanFly && Input.GetKey(KeyCode.Space))//等价于CanFly==true
{
//调用上升引擎让飞机上升
UpPlane();
}
//左旋
if (Input.GetKeyDown(KeyCode.Q) || Input.GetKey(KeyCode.Q))
{
//调用TurnLeft飞机左旋
TurnLeft();
}
//右旋
if (Input.GetKeyDown(KeyCode.E) || Input.GetKey(KeyCode.E))
{
//调用TurnRight飞机右旋
TurnRight();
}
//上挑
if (Input.GetKeyDown(KeyCode.Alpha1) || Input.GetKey(KeyCode.Alpha1))
{
//调用TurnUp飞机上挑
TurnUp();
}
//下挑
if (Input.GetKeyDown(KeyCode.Alpha2) || Input.GetKey(KeyCode.Alpha2))
{
//调用TurnDown飞机下挑
TurnDown();
}
//开启平衡调整,平衡调整状态下,不能对飞机进行操作
if (Input.GetKeyDown(KeyCode.B))
{
//开启自动平衡模式
IsBalancing = true;
StartCoroutine(Balanced());
}
}
/// <summary>
/// 起飞速度检测
/// </summary>
void SpeedCheck()
{
if (rigidbody.velocity.magnitude >= GroundMaxSpeed)
{
CanFly = true;
}
}
/// <summary>
/// 上升引擎函数
/// </summary>
void UpPlane()
{
if (rigidbody)
{
rigidbody.AddForce(transform.up * UpAccSpeed*Time.deltaTime);
}
}
/// <summary>
/// 给飞机一个向前的力
/// </summary>
void GoFarward()
{
if (rigidbody)
{
rigidbody.AddForce(-transform.forward*AccSpeed*Time.deltaTime);
}
}
/// <summary>
/// 控制飞机向后飞
/// </summary>
void GoBack()
{
if (rigidbody)
{
rigidbody.AddForce(transform.forward * AccSpeed * Time.deltaTime);
}
}
/// <summary>
/// 控制飞机向左飞
/// </summary>
void GoLeft()
{
if (rigidbody)
{
//添加力
rigidbody.AddForce(transform.right * AccSpeed * Time.deltaTime);
}
}
/// <summary>
/// 控制飞机向右飞
/// </summary>
void GoRight()
{
if (rigidbody)
{
//添加力
rigidbody.AddForce(-transform.right * AccSpeed * Time.deltaTime);
}
}
/// <summary>
/// 限制飞机速度
/// </summary>
void LimitSpeed()
{
//限制水平方向上的最大速度
if (rigidbody.velocity.x <= -HorizotalMaxSpeed)
{
rigidbody.velocity = new Vector3(-HorizotalMaxSpeed, rigidbody.velocity.y, rigidbody.velocity.z);
}
else if (rigidbody.velocity.x >= HorizotalMaxSpeed)
{
rigidbody.velocity = new Vector3(HorizotalMaxSpeed, rigidbody.velocity.y, rigidbody.velocity.z);
}
if (rigidbody.velocity.y <= -HorizotalMaxSpeed)
{
rigidbody.velocity = new Vector3(rigidbody.velocity.x,-HorizotalMaxSpeed, rigidbody.velocity.z);
}
else if (rigidbody.velocity.y >= HorizotalMaxSpeed)
{
rigidbody.velocity = new Vector3(rigidbody.velocity.x, HorizotalMaxSpeed, rigidbody.velocity.z);
}
//限制垂直方向上的最大速度
if (rigidbody.velocity.z <= -VerticalMaxSpeed)
{
rigidbody.velocity = new Vector3(rigidbody.velocity.x, rigidbody.velocity.y, -VerticalMaxSpeed);
}
else if (rigidbody.velocity.y >= VerticalMaxSpeed)
{
rigidbody.velocity = new Vector3(rigidbody.velocity.x, rigidbody.velocity.y, VerticalMaxSpeed);
}
//限制飞机能飞到的最大高度
if (transform.position.y >= MaxFlyHeight)
{
transform.position = new Vector3(transform.position.x,
MaxFlyHeight, transform.position.z);
}
}
/// <summary>
/// 保持飞机平衡
/// </summary>
IEnumerator Balanced()
{
//首先判断BalancedAxis的字符串是否有问题
if (!CheckBlanceAxisString())
{
//退出自动平衡
IsBalancing = false;
Debug.LogError("Banlanced Axis字符串有错误,\n请认真检查修改后再使用自动平衡功能。");
//返回空
yield return null;
}
//更新实时平衡轴的位置,并从0-360制转化为-180到180制
UpdateBalancedValueF();
Vector2 UpBalanceValue;//实时平衡轴位置的二维值
Vector2 BalanceValue;//平衡轴位置的二维值
Vector2 DeltaValue1;//差值1,不对数据优化时的差值
Vector2 DeltaValue2;//差值2,将角度限制在-180,180后的差值
Vector3 AddValue;//每次的增值
//因为不修改水平旋转的角度,所以需要判断
if (BalancedAxis == "x+y")
{
//将对应平衡轴值临时保存为2维向量
UpBalanceValue = new Vector2(UpdateBalancedValue.x, UpdateBalancedValue.y);
BalanceValue = new Vector2(BalanceAxisValue.x, BalanceAxisValue.y);
//选取代价最小的调整方向,由于系统内部是0-360制
//对比转化为-180到180制来得到最小偏转角度
DeltaValue1 = BalanceValue - UpBalanceValue;
DeltaValue2 = new Vector2(
LimitAngle(BalanceAxisValue.x, 180)- LimitAngle(UpBalanceValue.x, 180)
, LimitAngle(BalanceAxisValue.y, 180) - LimitAngle(UpBalanceValue.y, 180));
if (DeltaValue1.magnitude <= DeltaValue2.magnitude)
{
AddValue = new Vector3(DeltaValue1.x, DeltaValue1.y, 0);
}
else
{
AddValue = new Vector3(DeltaValue2.x, DeltaValue2.y, 0);
}
}
else if (BalancedAxis == "x+z")
{
//同上
UpBalanceValue = new Vector2(UpdateBalancedValue.x, UpdateBalancedValue.z);
BalanceValue = new Vector2(BalanceAxisValue.x, BalanceAxisValue.z);
DeltaValue1 = BalanceValue - UpBalanceValue;
DeltaValue2 = new Vector2(
LimitAngle(BalanceAxisValue.x, 180) - LimitAngle(UpBalanceValue.x, 180)
, LimitAngle(BalanceAxisValue.y, 180) - LimitAngle(UpBalanceValue.y, 180));
if (DeltaValue1.magnitude <= DeltaValue2.magnitude)
{
AddValue = new Vector3(DeltaValue1.x, 0, DeltaValue1.y);
}
else
{
AddValue = new Vector3(DeltaValue2.x, 0, DeltaValue2.y);
}
}
else if (BalancedAxis == "y+z")
{
//同上
UpBalanceValue = new Vector2(UpdateBalancedValue.y, UpdateBalancedValue.z);
BalanceValue = new Vector2(BalanceAxisValue.x, BalanceAxisValue.y);
DeltaValue1 = BalanceValue - UpBalanceValue;
DeltaValue2 = new Vector2(
LimitAngle(BalanceAxisValue.x, 180) - LimitAngle(UpBalanceValue.x, 180)
, LimitAngle(BalanceAxisValue.y, 180) - LimitAngle(UpBalanceValue.y, 180));
if (DeltaValue1.magnitude <= DeltaValue2.magnitude)
{
AddValue = new Vector3(0, DeltaValue1.x, DeltaValue1.y);
}
else
{
AddValue = new Vector3(0, DeltaValue2.x, DeltaValue2.y);
}
}
else
{
AddValue = new Vector3(0, 0, 0);
}
while (true)
{
//当当前平衡轴与目标平衡位置时的差值大于1还需要调整平衡
if ((BalanceAxisValue-UpdateBalancedValue).magnitude>=1)
{
//每次运行到这里都加上差值除以一定数的量
UpdateBalancedValue += AddValue / ReBalanceSpeed;
}
else
{
//更新3个旋转轴的总值
Update3LocalRotateValue();
//退出平衡调整状态
IsBalancing = false;
break;
}
//每帧一次
yield return new WaitForEndOfFrame();
//平衡调整
transform.localEulerAngles = UpdateBalancedValue;
}
}
/// <summary>
/// 将Angle限制在-limitangle和limitangle之间
/// </summary>
/// <param name="Angle"></param>
/// <param name="limitangle"></param>
/// <returns></returns>
float LimitAngle(float Angle, float limitangle)
{
if (Angle > limitangle)
{
Angle = Angle - 360;
}
else if (Angle < -limitangle)
{
Angle = Angle + 360;
}
return Angle;
}
/// <summary>
/// 飞机向右旋转
/// </summary>
void TurnRight()
{
//本例的左右旋转是绕y轴旋转的,因此这里计算的是y轴旋转总值
yLocalRotateValue += EveryRotateValue;
//对y值做个限制不能超过360
if (yLocalRotateValue >= 360)
yLocalRotateValue -= 360;
//给飞机赋值旋转量来实现旋转
transform.localEulerAngles = new Vector3(xLocalRotateValue, yLocalRotateValue, zLocalRotateValue);
//更新其它旋转轴的总值
zLocalRotateValue = transform.localEulerAngles.z;
xLocalRotateValue = transform.localEulerAngles.x;
}
/// <summary>
/// 飞机向左旋转
/// </summary>
void TurnLeft()
{
//同上
yLocalRotateValue -= EveryRotateValue;
if (yLocalRotateValue <= -360)
yLocalRotateValue += 360;
transform.localEulerAngles = new Vector3(xLocalRotateValue, yLocalRotateValue, zLocalRotateValue);
zLocalRotateValue = transform.localEulerAngles.z;
xLocalRotateValue = transform.localEulerAngles.x;
}
/// <summary>
/// 飞机上挑
/// </summary>
void TurnUp()
{
//类比TurnRight旋转函数
xLocalRotateValue += EveryRotateValue;
if (xLocalRotateValue >= 360)
xLocalRotateValue = xLocalRotateValue - 360;
transform.localEulerAngles = new Vector3(xLocalRotateValue, yLocalRotateValue, zLocalRotateValue);
yLocalRotateValue = transform.localEulerAngles.y;
zLocalRotateValue = transform.localEulerAngles.z;
}
/// <summary>
/// 飞机下挑
/// </summary>
void TurnDown()
{
//类比TurnRight旋转函数
xLocalRotateValue -= EveryRotateValue;
if (xLocalRotateValue <= -360)
xLocalRotateValue += 360;
transform.localEulerAngles = new Vector3(xLocalRotateValue, yLocalRotateValue, zLocalRotateValue);
yLocalRotateValue = transform.localEulerAngles.y;
zLocalRotateValue = transform.localEulerAngles.z;
}
/// <summary>
/// 更新平衡轴的实时坐标值
/// </summary>
void UpdateBalancedValueF()
{
//将360度制的旋转量获取并更新到UpdateBalanceValue
UpdateBalancedValue = new Vector3(transform.localEulerAngles.x, transform.localEulerAngles.y
, transform.localEulerAngles.z);
BalanceAxisValue.y = transform.localEulerAngles.y;
//转-180,180制
UpdateBalancedValue = new Vector3(LimitAngle(UpdateBalancedValue.x, 180),
LimitAngle(UpdateBalancedValue.y, 180),
LimitAngle(UpdateBalancedValue.z, 180));
BalanceAxisValue = new Vector3(LimitAngle(BalanceAxisValue.x, 180),
LimitAngle(BalanceAxisValue.y, 180),
LimitAngle(BalanceAxisValue.z, 180));
}
/// <summary>
/// 三个LocalRotate值对接物体实际旋转值
/// </summary>
void Update3LocalRotateValue()
{
//更新三个旋转总值的值
zLocalRotateValue = transform.localEulerAngles.z;
xLocalRotateValue = transform.localEulerAngles.x;
yLocalRotateValue = transform.localEulerAngles.y;
}
}
主要实现功能
__这个控制器实现了简单的左飞(A),右飞(D),前进(W),后退(S),上升(Space),左旋(Q),右旋(E),上挑(1),下挑(2),自动水平平衡(B),限速,限高,这些功能。
游戏体结构如下图:
游戏体的结构图片
一些设计想法
__接下来说一下旋转和平衡功能,其它功能的实现看注释就ok了。旋转我是设置了三个轴的旋转总值,根据需求固定增``加某总值,在将三个总值赋值给飞机自身的旋转量,在开头初始化对接了自身的旋转量。每次旋转完后也会更新三个旋转量,还有一次就是在退出自动平衡状态时调用了。
__之后是自动平衡。自动平衡就是两个三维向量的运算。一个标准的平衡位置向量和一个当前三个旋转轴值的向量。求出两个向量的差值,并运用协程每一帧增加一个差值/n的量,当两者之间的差值模小于1时,认为达到平衡状态。
__这里需要注意的时标准平衡位置有两个个轴的值是固定的,剩下的一个轴的值要在进行自动平衡时更新,即保留它的水平旋转。而当前三个旋转轴值的向量需要在自动平衡时更新对接一下游戏体实际的旋转值量。
即一个是(0,nowRotatey,0),一个是(nowRotatex,nowRotatey,
nowRotatez)
结尾
接触unity脚本没有多深,可能会觉得代码有些地方写的不好,请见谅。