302 lines
12 KiB
C#
302 lines
12 KiB
C#
using UnityEngine;
|
||
using System.Collections;
|
||
|
||
public class CarScript : MonoBehaviour
|
||
{
|
||
public float maxCornerAccel = 10; //最大转角加速度
|
||
public float maxBrakeAccel = 10;//最大制动加速度
|
||
public float cogY;//重心的高度,影响倾斜
|
||
|
||
public int minRPM = 700; //发动机最小转速,最大转速
|
||
public int maxRPM = 6000;
|
||
public int maxTorque = 300;//最大扭矩
|
||
public int shiftDownRPM = 2000; //自动降档的转速
|
||
public int shiftUpRPM = 3000;//自动升档的转速
|
||
float[] gearRatios; //齿轮比
|
||
float finalDriveRatio = 3.4f;//最终转动比
|
||
//基础的手动调节器
|
||
//1.0 转向不足
|
||
//0.0 转向过度
|
||
float handlingTendency = 0.7f;//处理趋势
|
||
public Transform wheelFR;
|
||
public Transform wheelFL;
|
||
public Transform wheelBR;
|
||
public Transform wheelBL;
|
||
|
||
public float suspensionDistance = 0.3f; //悬挂高度
|
||
int springs = 1000; //弹簧
|
||
int dampers = 200; //减震
|
||
public float[] wheelRadius;
|
||
|
||
public bool queryUserInput; //是否允许用户控制
|
||
public float engineRPM;//发动机转速
|
||
float steerVelo;//转向速度
|
||
|
||
//获取用户输入
|
||
public float brake; //制动
|
||
public float handbrake;//手刹
|
||
public float steer;//转向
|
||
public float motor;//动力
|
||
public float Engine;
|
||
|
||
public bool onGround;//是否在地面上
|
||
private float cornerSlip;//转弯滑行
|
||
private float driveSlip;//驾驶滑行
|
||
private float wheelRPM;//车轮转速
|
||
public int gear; //档位
|
||
|
||
private WheelData[] wheels;
|
||
private float wheelY;//车轮高度,车轮模型Y初始值
|
||
private float rev;
|
||
|
||
Vector3 tempPos;
|
||
public bool ifStop;
|
||
public Vector3 speed;
|
||
float torqueCurve;
|
||
|
||
|
||
void SetEnableUserInput() //允许用户控制
|
||
{
|
||
queryUserInput = true;
|
||
}
|
||
|
||
class WheelData
|
||
{
|
||
public float rotation;//旋转
|
||
public WheelCollider coll; //车轮碰撞
|
||
public Transform graphic; //车轮模型transform
|
||
public float maxSteerAngle;//最大转向角
|
||
public bool powered = false; //是否为驱动轮
|
||
public bool handbraked = false; //是否手刹
|
||
public Quaternion originalRotation; //初始旋转参数
|
||
}
|
||
|
||
|
||
void Start()//创建车轮数据数组
|
||
{
|
||
gearRatios = new float[4] { 1, 0, -1.2f, -2 };//齿轮比初始化
|
||
wheels = new WheelData[4];//4个车轮数据
|
||
for (int i = 0; i < 4; i++)
|
||
wheels[i] = new WheelData();
|
||
|
||
wheels[0].graphic = wheelFL; //车轮模型
|
||
wheels[1].graphic = wheelFR;
|
||
wheels[2].graphic = wheelBL;
|
||
wheels[3].graphic = wheelBR;
|
||
|
||
wheels[2].maxSteerAngle = 45; //两前轮最大转向角度
|
||
wheels[3].maxSteerAngle = 45;
|
||
|
||
wheels[2].powered = true; //后轮驱动
|
||
wheels[3].powered = true;
|
||
|
||
wheels[0].handbraked = true;
|
||
wheels[1].handbraked = true;
|
||
wheels[2].handbraked = true; //四轮可手刹
|
||
wheels[3].handbraked = true;
|
||
|
||
for (int i = 0; i < 4; i++)
|
||
{
|
||
if (wheels[i].graphic == null) //车轮模型为空
|
||
Debug.Log("You need to assign all four wheels for the car script!");
|
||
if (!wheels[i].graphic.transform.IsChildOf(transform))//车轮必须为当前脚本车体子物体
|
||
Debug.Log("Wheels need to be children of the Object with the car script");
|
||
|
||
wheels[i].originalRotation = wheels[i].graphic.localRotation;//初始旋转参数为运行前的状态
|
||
//创建空物体
|
||
GameObject colliderObject = new GameObject("WheelCollider");
|
||
colliderObject.transform.parent = transform;//为车体的子物体,不是车轮
|
||
colliderObject.transform.position = wheels[i].graphic.position; //空物体位置=车轮位置
|
||
wheels[i].coll = colliderObject.AddComponent<WheelCollider>(); //空物体添加车轮碰撞组件并赋值给数组
|
||
wheels[i].coll.suspensionDistance = suspensionDistance; //车轮碰撞的悬挂高度
|
||
|
||
JointSpring js = new JointSpring();//新建关节弹簧
|
||
js.spring = springs;//弹簧
|
||
js.damper = dampers;//减震
|
||
wheels[i].coll.suspensionSpring = js;//碰撞体的弹簧赋值
|
||
wheels[i].coll.radius = wheelRadius[i];//车轮半径
|
||
}
|
||
wheelY = wheels[0].graphic.localPosition.y;
|
||
|
||
//刚体重心设置,高度
|
||
GetComponent<Rigidbody>().centerOfMass.Set(GetComponent<Rigidbody>().centerOfMass.x, cogY, GetComponent<Rigidbody>().centerOfMass.z);
|
||
|
||
float x = (1 * (engineRPM / maxRPM) - 1); //返回基本的扭矩曲线float x = (2 * (engineRPM / maxRPM) - 1);
|
||
torqueCurve = 0.5f * (-x * x + 2);
|
||
}
|
||
|
||
void FixedUpdate()
|
||
{
|
||
if (!ifStop)
|
||
{
|
||
if (GetComponent<Rigidbody>().isKinematic)//是否静止
|
||
GetComponent<Rigidbody>().isKinematic = false;
|
||
if (queryUserInput)
|
||
{
|
||
brake = Mathf.Clamp01(Input.GetAxis("Vertical"));//限制在0-1,上下方向
|
||
handbrake = Input.GetButton("Jump") ? 3 : 0;
|
||
steer = -Input.GetAxis("Horizontal");//水平方向,转向
|
||
motor = Mathf.Clamp01(-Input.GetAxis("Vertical"));//动力
|
||
}
|
||
else
|
||
{
|
||
//motor = 0;
|
||
//steer = 0;
|
||
//brake = 0;
|
||
//handbrake = 0;
|
||
}
|
||
|
||
//如果汽车在接触地面则处理物理,否则只计算动力
|
||
if (onGround)
|
||
HandlePhysics();
|
||
else
|
||
CalcEngine();
|
||
UpdateWheels();//更新车轮数据
|
||
speed = GetComponent<Rigidbody>().velocity;
|
||
}
|
||
else
|
||
GetComponent<Rigidbody>().isKinematic = true;
|
||
}
|
||
void UpdateWheels()
|
||
{
|
||
//给牵引力加入手刹
|
||
float handbrakeSlip = handbrake * GetComponent<Rigidbody>().velocity.magnitude * 0.1f;
|
||
if (handbrakeSlip > 1)
|
||
handbrakeSlip = 1;
|
||
|
||
float totalSlip = 0;
|
||
onGround = false;//初始为悬空状态
|
||
for (int i = 0; i < 4; i++)
|
||
{
|
||
//rotate wheel
|
||
wheels[i].rotation += wheelRPM / 60 * rev * 360 * Time.fixedDeltaTime; //计算车轮旋转角
|
||
wheels[i].rotation = Mathf.Repeat(wheels[i].rotation, 360);//限定取值范围值360之间,赋值到车轮模型上
|
||
wheels[i].graphic.localRotation = Quaternion.Euler(wheels[i].rotation, wheels[i].maxSteerAngle * steer, 0) * wheels[i].originalRotation;
|
||
//检查是否悬空
|
||
if (wheels[i].coll.isGrounded)
|
||
onGround = true;
|
||
|
||
float slip = cornerSlip + (wheels[i].powered ? driveSlip : 0f) + (wheels[i].handbraked ? handbrakeSlip : 0f);
|
||
totalSlip += slip;
|
||
|
||
WheelHit hit;
|
||
WheelCollider c;
|
||
c = wheels[i].coll;
|
||
if (c.GetGroundHit(out hit))//车轮与地面射线检测
|
||
{
|
||
//如果车轮碰到地面,将反馈给弹簧
|
||
tempPos = new Vector3(wheels[i].graphic.transform .position .x , hit .point .y + wheels[i].coll.radius, wheels[i].graphic.transform.position.z);
|
||
wheels[i].graphic.transform .position = tempPos;
|
||
}
|
||
}
|
||
totalSlip = totalSlip / wheels.Length;
|
||
}
|
||
//void AutomaticTransmission()//自动换挡
|
||
//{
|
||
// if (gear > 0)
|
||
// {
|
||
// if (engineRPM > shiftUpRPM && gear < gearRatios.Length - 1)
|
||
// gear++;
|
||
// if (engineRPM < shiftDownRPM && gear > 1)
|
||
// gear--;
|
||
// }
|
||
//}
|
||
float CalcEngine() //根据当前转速计算加速度
|
||
{
|
||
if (brake + handbrake > 0.1)//刹车时没有动力
|
||
motor = 0;
|
||
|
||
//如果悬空
|
||
if (!onGround)
|
||
{
|
||
engineRPM += (motor - 0.3f) * 2500 * Time.deltaTime;//engineRPM += (motor - 0.3f) * 2500 * Time.deltaTime;
|
||
engineRPM = Mathf.Clamp(engineRPM, minRPM, maxRPM);
|
||
return 0;
|
||
}
|
||
else
|
||
{
|
||
//AutomaticTransmission();
|
||
engineRPM = wheelRPM * gearRatios[gear] * finalDriveRatio;
|
||
if (steer != 0)
|
||
engineRPM = engineRPM * 0.5f;
|
||
if (engineRPM < minRPM)
|
||
engineRPM = minRPM;
|
||
if (engineRPM < maxRPM)
|
||
{
|
||
float torqueToForceRatio = gearRatios[gear] * finalDriveRatio*0.5f / wheelRadius[2]; Debug.Log(motor + "," + maxTorque + ',' + torqueCurve + ',' + torqueToForceRatio);
|
||
return motor * maxTorque * torqueCurve * torqueToForceRatio;
|
||
}
|
||
else
|
||
return 0;
|
||
}
|
||
}
|
||
void HandlePhysics()//物理处理
|
||
{
|
||
Vector3 velo = GetComponent<Rigidbody>().velocity;//刚体速度
|
||
wheelRPM = velo.magnitude * 60 * 0.5f;//刚体速度向量的长度
|
||
GetComponent<Rigidbody>().angularVelocity = new Vector3(GetComponent<Rigidbody>().angularVelocity.x, 0, GetComponent<Rigidbody>().angularVelocity.z);//刚体Y向的角速度向量为0
|
||
Vector3 dir = transform.TransformDirection(Vector3.forward);//行驶方向
|
||
Vector3 flatDir = Vector3.Normalize(new Vector3(dir.x, 0, dir.z));//Y为0,成平地上的目标
|
||
Vector3 flatVelo = new Vector3(velo.x, 0, velo.z);//平地上的速度
|
||
rev = Mathf.Sign(Vector3.Dot(flatVelo, flatDir));//返回1或-1
|
||
//当向后或刹车时转换到反方向
|
||
if ((rev < 0 || flatVelo.sqrMagnitude < 0.5) && brake > 0.1)
|
||
gear = 0;
|
||
if (gear == 0)
|
||
{
|
||
//反向时翻转力
|
||
float tmp = brake;
|
||
brake = motor;
|
||
motor = tmp;
|
||
|
||
//切换到驾驶
|
||
if ((rev > 0 || flatVelo.sqrMagnitude < 0.5) && brake > 0.1)
|
||
gear = 1;
|
||
}
|
||
Engine =CalcEngine();
|
||
Vector3 engineForce = flatDir * Engine;
|
||
float totalbrake = brake + handbrake * 0.5f;
|
||
if (totalbrake > 1.0)
|
||
totalbrake = 1;
|
||
Vector3 brakeForce = -flatVelo.normalized * totalbrake * GetComponent<Rigidbody>().mass * maxBrakeAccel;
|
||
flatDir *= flatVelo.magnitude;
|
||
flatDir = Quaternion.AngleAxis(steer * 30, Vector3.up) * flatDir;
|
||
flatDir *= rev;
|
||
float diff = (flatVelo - flatDir).magnitude;
|
||
float cornerAccel = maxCornerAccel;
|
||
if (cornerAccel > diff) cornerAccel = diff;
|
||
Vector3 cornerForce = -(flatVelo - flatDir).normalized * cornerAccel * GetComponent<Rigidbody>().mass;
|
||
cornerSlip = Mathf.Pow(cornerAccel / maxCornerAccel, 3);
|
||
|
||
GetComponent<Rigidbody>().AddForceAtPosition(brakeForce + engineForce + cornerForce, transform.position + transform.up * wheelY);
|
||
|
||
float handbrakeFactor = 1 + handbrake * 4;
|
||
if (rev < 0)
|
||
handbrakeFactor = 1;
|
||
float veloSteer = ((15 / (2 * velo.magnitude + 1)) + 1) * handbrakeFactor;
|
||
float steerGrip = (1 - handlingTendency * cornerSlip);
|
||
if (rev * steer * steerVelo < 0)
|
||
steerGrip = 1;
|
||
float maxRotSteer = 2 * Time.fixedDeltaTime * handbrakeFactor * steerGrip;
|
||
float fVelo = velo.magnitude;
|
||
float veloFactor = fVelo < 1 ? fVelo : Mathf.Pow(velo.magnitude, 0.3f);
|
||
float steerVeloInput = rev * steer * veloFactor * 0.5f * Time.fixedDeltaTime * handbrakeFactor;
|
||
if (velo.magnitude < 0.1)
|
||
steerVeloInput = 0;
|
||
if (steerVeloInput > steerVelo)
|
||
{
|
||
steerVelo += 0.02f * Time.fixedDeltaTime * veloSteer;
|
||
if (steerVeloInput < steerVelo)
|
||
steerVelo = steerVeloInput;
|
||
}
|
||
else
|
||
{
|
||
steerVelo -= 0.02f * Time.fixedDeltaTime * veloSteer;
|
||
if (steerVeloInput > steerVelo)
|
||
steerVelo = steerVeloInput;
|
||
}
|
||
steerVelo = Mathf.Clamp(steerVelo, -maxRotSteer, maxRotSteer);
|
||
transform.Rotate(Vector3.up * steerVelo * 57.295788f);
|
||
}
|
||
}
|