簡介
根據前兩篇的敘述與實驗後,是否有更了解Unity中的Collider與Rigidbody呢?!我們再第三章講述比較複雜的Wheel Collider,通常是用在輪子相關的物件,在模擬場景中常會出現的自駕車模擬更會使用上,而如何運用這兩個功能讓車輛行駛的更貼近現實世界,就要細細的微調之中的參數了,讓我們開始吧。
Wheel Collider 細部介紹
Wheel Collider內置碰撞檢測 ,車輪物理特性和基於滑動的輪胎摩擦係數。它可以用於除車輪以外的物體,但專門設計用於帶輪車輛。 下圖是Wheel Collider的Component圖。一樣是在Scene中輪子物件按下Add Component->Wheel Collider。
我們可以參考下方Collider圖片來了解參數解釋,中間有一條綠色的線(位於wheel collider中心點的直徑) 在下方解釋會常看到 ” 車輪懸架 “,就是在說這個詞。
Mass : 輪軸物件的質量。值介於 0 – 無限。
Radius : (下圖紅色箭頭) 輪軸碰撞器的半徑。 值介於 0 – 無限。
Wheel Damping Rate : 避震器,避震值範圍。值介於 0.0001 – 無限。
Suspension Distance : (下圖橙色箭頭)車輪懸架的最大延伸距離,從輪軸中心Y軸向下延伸。 數值越大距離越遠。值介於 0 – 無限。 此值會與Wheel Damping Rate相互作用,數值調整不好會變成彈跳車。
Force App Point Distance : (下圖綠色箭頭)施力點距離 ,預設為0,最好把這個值調整到wheel Collider邊界上。
Center : (下圖青藍色箭頭)輪軸中心離地板的距離。
Suspension Spring : 這邊是更細部設定碰撞器的輪軸避震值的細部設定。懸架試圖達到目標位置,通過增加彈簧和阻尼力。
Spring : 彈力試圖到達目標位置。較大的值可使懸架更快地到達目標位置。 值介於 0 – 無限。
Damper : 降低懸架速度。較大的值會使懸架彈簧的移動速度變慢。 值介於 0 – 無限。
Target Position : 懸架沿懸架距離的靜止距離。 1映射到完全擴展的懸架,0映射到完全壓縮的懸架。默認值為0.5,與常規汽車的懸架狀態一致。
Forward Friction / Sideway Friction:車輪向前和向側面滾動時輪胎的摩擦特性。
在摩擦力的調整,可以說是在調整你輪胎的橡膠的摩擦係數,Extremum Slip / Value:在調整輪胎運動向前與向側邊的摩擦係數,值越高越滑。此參數會跟著加速度的曲線成正比,煞車亦同。Asymptote Slip / Value:曲線的結束點,此參數會與Extremum會同時運算會製成Curve圖(如下圖),當車輛行徑力道與滑動係素會將上方兩個值做即時運算。
Wheel Collider 實現在車輛中
在官方文件中,他們是把輪胎模型跟Collider是分開的,但其實可以把Collider掛載在輪胎的模型物件上(如下圖)。
Step1:你必須有既有的模型,且必須要有輪胎的獨立物件,並將所有物件包成一個GameObject。
Step2:請新增Wheel Collider在各輪胎模型上。並將碰撞器的參數透過上方解釋的各個參數調整好。
Step3:下方我提供了控制車輛移動透過鍵盤來操作。我將程式碼中的的功能都寫在程式碼註記中。先新增Create->C# Script,並將下方的程式碼貼上你新增的C# Script,要注意的一點是,程式碼的名稱要跟C#程式的Class名稱相同,否則會出錯。
Step4:將程式碼掛載在Step1 GameObject之下,並在此GameObject下新增個Rigidbody。掛載好兩個元件會如下圖所示,並將Rigidbody拉到程式碼的Center Of Gravity欄位中。
Step5:調整上圖Car Controller程式碼中的public參數,並將Wheel Collider拖曳到上圖紅框框處,大致上是這樣,剩下的細節可以看到下方程式碼中的註解。
using UnityEngine; using System.Collections; using System.Collections.Generic; using UnityEngine.UI; public class CarController : MonoBehaviour { //輪胎旋轉的標準值與最大值 public float idealRPM = 500f; public float maxRPM = 1000f; //車輛物件的Rigidbody,重量中心點的物件放置於此 public Transform centerOfGravity; //Wheel Collider放置於此 public WheelCollider wheelFR; public WheelCollider wheelFL; public WheelCollider wheelRR; public WheelCollider wheelRL; //輪胎運算參數 public float turnRadius = 6f; public float torque = 25f; public float brakeTorque = 100f; public float AntiRoll = 20000.0f; //車輛的行車方向,向前與向後 public enum DriveMode { Front, Rear, All }; public DriveMode driveMode = DriveMode.Rear; //在程式的生命週期Start時定位車輛中心點的重量 void Start() { GetComponent<Rigidbody>().centerOfMass = centerOfGravity.localPosition; } //車輛速度運算,透過輪子RR的collider參數運算 public float Speed() { return wheelRR.radius * Mathf.PI * wheelRR.rpm * 60f / 1000f; } public float Rpm() { return wheelRL.rpm; } void FixedUpdate () { //鍵盤控制input的按住時間,運算加速度 float scaledTorque = Input.GetAxis("Vertical") * torque; if(wheelRL.rpm < idealRPM) scaledTorque = Mathf.Lerp(scaledTorque/10f, scaledTorque, wheelRL.rpm / idealRPM ); else scaledTorque = Mathf.Lerp(scaledTorque, 0, (wheelRL.rpm-idealRPM) / (maxRPM-idealRPM) ); //開始運算前後排輪胎的轉向與轉速 DoRollBar(wheelFR, wheelFL); DoRollBar(wheelRR, wheelRL); //輪胎左轉右轉鍵盤輸入 wheelFR.steerAngle = Input.GetAxis("Horizontal") * turnRadius; wheelFL.steerAngle = Input.GetAxis("Horizontal") * turnRadius; //開始控制每個輪胎的運動方向 wheelFR.motorTorque = driveMode==DriveMode.Rear ? 0 : scaledTorque; wheelFL.motorTorque = driveMode==DriveMode.Rear ? 0 : scaledTorque; wheelRR.motorTorque = driveMode==DriveMode.Front ? 0 : scaledTorque; wheelRL.motorTorque = driveMode==DriveMode.Front ? 0 : scaledTorque; //鍵盤控制Wheel Collider if(Input.GetButton("Fire1")) { wheelFR.brakeTorque = brakeTorque; wheelFL.brakeTorque = brakeTorque; wheelRR.brakeTorque = brakeTorque; wheelRL.brakeTorque = brakeTorque; } else { wheelFR.brakeTorque = 0; wheelFL.brakeTorque = 0; wheelRR.brakeTorque = 0; wheelRL.brakeTorque = 0; } } //輪胎轉動運算函式 void DoRollBar(WheelCollider WheelL, WheelCollider WheelR) { WheelHit hit; float travelL = 1.0f; float travelR = 1.0f; bool groundedL = WheelL.GetGroundHit(out hit); if (groundedL) travelL = (-WheelL.transform.InverseTransformPoint(hit.point).y - WheelL.radius) / WheelL.suspensionDistance; bool groundedR = WheelR.GetGroundHit(out hit); if (groundedR) travelR = (-WheelR.transform.InverseTransformPoint(hit.point).y - WheelR.radius) / WheelR.suspensionDistance; float antiRollForce = (travelL - travelR) * AntiRoll; if (groundedL) GetComponent<Rigidbody>().AddForceAtPosition(WheelL.transform.up * -antiRollForce, WheelL.transform.position); if (groundedR) GetComponent<Rigidbody>().AddForceAtPosition(WheelR.transform.up * antiRollForce, WheelR.transform.position); } }
小結
要在Unity中模擬出真實車輛移動的狀態,就必須要使用到本篇所介紹的Wheel Collider,因為他這邊所有的Componment係項都是有經過物理運算以及Unity的Project Setting中重力的交互才顯示出每一個Frame的動態效果,雖然參數是有點難懂,但大家可以透過Try Test來感受每個數值變化所帶來的汽車移動效果。而有關程式的部分其實可以先略過,大家只需要知道哪個坑要丟入哪個對應的物件即可,漫漫長路,總有一天會講的更深入的。
Reference:https://docs.unity3d.com/Manual/class-WheelCollider.html