最近一直在研究unity3d,很強大的一款3d引擎。本篇介紹基於此引擎的一個物理賽車驅動算法。
建模丑了點...
首先,我們需要先弄懂汽車的工作原理。每個汽車的動力來自引擎。引擎動力的量化我們用力矩來表示。引擎通過變速箱把力傳給輪子,這樣輪子就轉了起來,整個汽車就可以動了。
需要注意的有以下幾個點:
1,加減檔系統。所謂變速箱其實可以抽象為一個數組,每個數表示引擎力與輪子力的線性對應關系。汽車一般為6個檔位,那么我們數組的大小也應該是6。
動力來自引擎,通過當前檔位計算出輪子的力矩,從而使輪子轉動。
另一方面我們捕獲輪子的轉速,通過當前檔位再計算出引擎當前的轉速。我們限定了引擎的最大與最小轉速,規定:引擎達到最大轉速的時候,加檔;減速到最小轉速的時候,減檔。
這樣就實現了汽車的自動加減檔,當然也可以做成手動的。
2,unity3d的wheelCollider(輪子碰撞器)。我們可以通過給wheelCollider一個力,它就可以自動實現滾動的物理效果,同時,我們可以通過代碼捕獲它的轉速,這恰恰符合我們的要求。
更強大的是,我們可以設置輪子的橫向與縱向摩擦力,設置輪子的懸掛系統參數,通過這些設置,可以模擬汽車的避震,漂移等效果。
suspension Spring 懸掛系統;
forwardFriction 輪子的縱向摩擦力(車子前進后退方向的摩擦力);
sideWays Friction 輪子的橫向摩擦力;
Extremun Slip;Extremum Value;Asymptote Slip;Asymptote Value這四個值其實是坐標系上的兩個坐標,他們確定了一個滑動距離與摩擦力的關系曲線。
stiffness Factor可以整體調節摩擦曲線的倍數。
3,unity3d的Rigidbody(剛體)。我們可以賦予剛體質量,並且剛體是受重力影響的。車重的不同也會影響汽車的行駛效果!我們一般按真實世界的數值來,例如汽車的mass我們可以設為6000左右(單位為kg),包括模型的尺寸,也最好與真實尺寸一致(單位為米)。這樣我們才能獲得更真實的物理效果。
下面我們分享代碼(來源csdn):
using UnityEngine; using System.Collections; public class CarControl : MonoBehaviour { //操縱前輪,用於轉向 public WheelCollider FrontLeftWheel; public WheelCollider FrontRightWheel; public WheelCollider BackLeftWheel; public WheelCollider BackRightWheel; //齒輪數組 public float[] GearRatio; //當前檔位 public int CurrentGear=0; public float EngineTorgue=600.0f; public float MaxEngineRPM=3000.0f; public float MinEngineRPM=1000.0f; private float EngineRPM=0.0f; // Use this for initialization void Start () { //設置車的重心,使車更穩定 Vector3 centerOfMass=rigidbody.centerOfMass; centerOfMass.y=-1.5f; rigidbody.centerOfMass=centerOfMass; } // Update is called once per frame void Update () { //限制車的最大速度,調整阻力可能不是最好的做法。但它很簡單,而且不會干擾物理系統的運行。 rigidbody.drag=rigidbody.velocity.magnitude/250; //通過兩個輪子的平均rpm,計算引擎rpm,然后切換檔位 EngineRPM=(FrontLeftWheel.rpm+FrontRightWheel.rpm)/2*GearRatio[CurrentGear]; ShiftGears(); //設置換檔的聲音 audio.pitch=Mathf.Abs(EngineRPM/MaxEngineRPM)+1.0f; if(audio.pitch>2.0) { audio.pitch=2.0f; } //最后設置輪子轉動力矩。引擎力矩除以當前檔位,乘以用戶輸入值。 //輪子力矩提供一個汽車前進的力。輪子的轉動又會提高檔位。 BackLeftWheel.motorTorque=EngineTorgue/GearRatio[CurrentGear]*Input.GetAxis("Vertical"); BackRightWheel.motorTorque=EngineTorgue/GearRatio[CurrentGear]*Input.GetAxis("Vertical"); //轉動角度是任意數乘以用戶輸入值 FrontLeftWheel.steerAngle=20*Input.GetAxis("Horizontal"); FrontRightWheel.steerAngle=20*Input.GetAxis("Horizontal"); } void ShiftGears() { int AppropriateGear=CurrentGear; if(EngineRPM>=MaxEngineRPM) { AppropriateGear=CurrentGear; for(int i=0;i<GearRatio.Length;i++) { if(FrontLeftWheel.rpm*GearRatio[i]<MaxEngineRPM) { AppropriateGear=i; break; } } CurrentGear=AppropriateGear; } if(EngineRPM<=MaxEngineRPM) { AppropriateGear=CurrentGear; for(int j=GearRatio.Length-1;j>=0;j--) { if(FrontLeftWheel.rpm*GearRatio[j]>MinEngineRPM) { AppropriateGear=j; break; } } CurrentGear=AppropriateGear; } } }