參考
Cocos 3.x 3D物理系統(tǒng) 射線檢測
[專欄精選]Unity剛體詳解
一、RigidBody
https://docs.unity.cn/cn/2019.4/Manual/class-Rigidbody.html
Cocos 3.x中的RigidBody概念上與Unity是一致的,但是稍有區(qū)別:

1.剛體類型
動態(tài) Dynamic
- 默認類型
- 受重力和附加力的影響,進而改變速度。
- 性能消耗最高的類型。
- 可通過 MovePosition、MoveRotation 來移動。
- 可通過 velocity 來改變速度。
- 用來模擬移動的物體。
運動學 Kinematic
- 不受力的影響。
- 性能消耗比 Dynamic 少,比 Static 多。
- 可通過 MovePosition MoveRotation 來移動。
- 可通過 velocity 來改變速度。
- 用來模擬大多數(shù)時候不動的物體,極少數(shù)時候需要移動。比如門。
靜態(tài) Static
- 不受力的影響,物理系統(tǒng)不會對該物體進行計算。
- 性能消耗最低的類型。
- 不可通過 MovePosition MoveRotation 來移動。
- 不可通過 velocity 來改變速度。
- 用來模擬從不移動的物體。
cocos通過type選擇剛體類型,unity中是這樣的:
參考https://docs.unity.cn/cn/2019.4/Manual/CollidersOverview.html
- 可將碰撞體添加到?jīng)]有剛體組件的游戲?qū)ο?,從而?chuàng)建場景的地板、墻壁和其他靜止元素。這些被稱為靜態(tài)碰撞體。
- 相反,具有剛體的游戲?qū)ο笊系呐鲎搀w稱為動態(tài)碰撞體。靜態(tài)碰撞體可與動態(tài)碰撞體相互作用,但由于沒有剛體,因此不會通過移動來響應碰撞。
參考https://docs.unity.cn/cn/2019.4/Manual/RigidbodiesOverview.html
剛體 (Rigidbody)是實現(xiàn)游戲?qū)ο蟮奈锢硇袨榈闹饕M件。連接剛體后,對象將立即響應重力。如果還添加了一個或多個碰撞體組件,則游戲?qū)ο髸虬l(fā)生碰撞而移動。
由于剛體組件會接管附加到的游戲?qū)ο蟮倪\動,因此不應試圖借助腳本通過更改變換屬性(如位置和旋轉)來移動游戲?qū)ο?。相反,應該施加力來推動游戲?qū)ο蟛⒆屛锢硪嬗嬎憬Y果。
在某些情況下,可能希望游戲?qū)ο缶哂袆傮w,并讓剛體的運動擺脫物理引擎的控制。例如,可能希望直接從腳本代碼控制角色,但仍允許觸發(fā)器檢測角色。腳本產(chǎn)生的這種非物理運動稱為運動學運動。剛體組件有一個名為 Is Kinematic 的屬性,該屬性可以讓剛體擺脫物理引擎的控制,并允許通過腳本以運動學方式來移動剛體。可以通過腳本來更改 Is Kinematic 的值,從而為某個對象開啟和關閉物理引擎,但這會產(chǎn)生性能開銷,應謹慎使用。
可將碰撞體添加到?jīng)]有剛體組件的游戲?qū)ο螅瑥亩鴦?chuàng)建場景的地板、墻壁和其他靜止元素。這些被稱為靜態(tài)碰撞體。相反,具有剛體的游戲?qū)ο笊系呐鲎搀w稱為動態(tài)碰撞體。靜態(tài)碰撞體可與動態(tài)碰撞體相互作用,但由于沒有剛體,因此不會通過移動來響應碰撞。
場景里有一些物體是基本不會動的,比如說一棟大樓,一塊大石頭等等,他們只需要阻擋別的物體別穿過他們就行了,所以它們只需要添加Collider就行。因為添加了Rigidbody組件后會帶來很多的物理計算,所以只需要給會移動的物體添加Rigidbody就行
2.屬性
- Mass 對象的質(zhì)量(默認為千克)。
- Drag 根據(jù)力移動對象時影響對象的空氣阻力大小。0 表示沒有空氣阻力,無窮大使對象立即停止移動。
- Angular Drag 根據(jù)扭矩旋轉對象時影響對象的空氣阻力大小。0 表示沒有空氣阻力。請注意,如果直接將對象的 Angular Drag 屬性設置為無窮大,無法使對象停止旋轉。
- Use Gravity 如果啟用此屬性,則對象將受重力影響。
- Is Kinematic 如果啟用此選項,則對象將不會被物理引擎驅(qū)動,只能通過變換 (Transform)對其進行操作。
Drag和Angular Drag與Cocos 3.x中的Linear Damping 、Angular Damping 是一樣的。
3.Interpolate 如果發(fā)現(xiàn)剛體移動有卡頓,可以嘗試選擇此選項
- None 不應用插值。
- Interpolate 根據(jù)前一幀的變換來平滑變換。
- Extrapolate 根據(jù)下一幀的估計變換來平滑變換。
默認情況下,插值是關閉的。通常對玩家的角色使用剛體插值。 物理以離散時間步長運行,而圖形以可變幀率渲染。 由于物理和圖形并不完全同步,這可能導致對象出現(xiàn)視覺抖動。 這種效果很細微,但通常能夠在玩家角色上看到,特別是當攝像機跟隨主角時。 建議對主角打開插值,對于其他所有內(nèi)容,則禁用它。
4.Collision Detection 用于防止快速移動的對象穿過其他對象而不檢測碰撞。
連續(xù)碰撞檢測是一種阻止快速移動的碰撞體相互穿過的功能。使用正常 (Discrete) 碰撞檢測時,如果對象在一個幀中位于某個碰撞體的一側,而在下一幀中已經(jīng)穿過了碰撞體,便屬于彼此穿過的情況。要解決此問題,可在快速移動對象的剛體上啟用連續(xù)碰撞檢測。
將碰撞檢測模式設置為 Continuous 可防止剛體穿過任何靜態(tài)(即非剛體)網(wǎng)格碰撞體。設置為 Continuous Dynamic 也會防止剛體穿過任何其他支持的剛體(即,碰撞檢測模式設置為 Continuous 或 Continuous Dynamic 的剛體)。 盒型碰撞體、球形碰撞體和膠囊碰撞體支持連續(xù)碰撞檢測。請注意,連續(xù)碰撞檢測的目的是作為一種安全機制,可在對象會相互穿過的情況下捕獲碰撞,但不會提供精確物理碰撞的結果;所以如果遇到快速移動對象的問題,仍然可以考慮在 TimeManager 檢視面板中降低固定時間步長值以使模擬更準確。
- Discrete 對場景中的所有其他碰撞體使用離散碰撞檢測。其他碰撞體在測試碰撞時會使用離散碰撞檢測。用于正常碰撞(這是默認值)。
- Continuous 對動態(tài)碰撞體(具有剛體)使用離散碰撞檢測,并對靜態(tài)碰撞體(沒有剛體)使用基于掃掠的連續(xù)碰撞檢測。設置為連續(xù)動態(tài) (Continuous Dynamic) 的剛體將在測試與該剛體的碰撞時使用連續(xù)碰撞檢測。其他剛體將使用離散碰撞檢測。用于連續(xù)動態(tài) (Continuous Dynamic) 檢測需要碰撞的對象。(此屬性對物理性能有很大影響,如果沒有快速對象的碰撞問題,請將其保留為 Discrete 設置)
- Continuous Dynamic 對設置為連續(xù) (Continuous) 和連續(xù)動態(tài) (Continuous Dynamic) 碰撞的游戲?qū)ο笫褂没趻呗拥倪B續(xù)碰撞檢測。還將對靜態(tài)碰撞體(沒有剛體)使用連續(xù)碰撞檢測。對于所有其他碰撞體,使用離散碰撞檢測。用于快速移動的對象。
- Continuous Speculative 對剛體和碰撞體使用推測性連續(xù)碰
官方文檔有點難懂,可以參考Unity-Rigidbody【剛體】組件-Collision Detection碰撞檢測模式
有時候開發(fā)游戲,對于高速運動的物體(比如:子彈/大炮,或者很高處自由落體的物體),即使再三確認加了Rigidbody組件,檢查了碰撞檢測的代碼完全沒有錯誤,但仍然會出現(xiàn) 物體直接穿過另一個物體 的問題, 則說明這個碰撞檢測的程序?qū)τ诟咚龠\動的物體而言會出錯。
Unity物體的Rigidbody組件提供了一個Collision Detection的屬性,該屬性用于更改物體的碰撞檢測模式————一共有三種模式可以選擇(選擇你想要的模式,底層碰撞檢測的算法均有差別)
- Discrete適用于大部分剛體
- Continuous適用于將有可能會被高速移動物體撞上的物體
- Continuous Dynamic適用于高速移動的物體
另外參考官方文檔:連續(xù)碰撞檢測 (CCD)
5.Constraints 對剛體運動的限制:
- Freeze Position 有選擇地停止剛體沿世界 X、Y 和 Z 軸的移動。
- Freeze Rotation 有選擇地停止剛體圍繞局部 X、Y 和 Z 軸旋轉。
可以對物體在X、Y、Z三個軸上的位置/旋轉進行鎖定,即使受到相應的力也不會改變,但可以通過腳本來修改。否則物體在上升過程中會發(fā)生飄動(不僅y軸變化,X,Z也在變,不想這種現(xiàn)象,就把X,Z鎖定)
6.官方文檔提示
https://docs.unity.cn/cn/2019.4/Manual/class-Rigidbody.html
- 兩個剛體的相對質(zhì)量決定了它們相互碰撞時的反應情況。
- 使一個剛體具有比另一個剛體更大的質(zhì)量并不會使其在自由落體時降落得更快。要實現(xiàn)這一目的,請使用 Drag。
- 較低的 Drag 值會讓對象看起來較重。較高的值會使其看起來較輕。Drag 的典型值為 .001(實心金屬塊)到 10(羽毛)之間。
- 如果要直接操作對象的變換組件,但仍希望獲得物理效果,請附加剛體并將其設為 Kinematic。
- 如果要通過變換組件移動游戲?qū)ο?,但希望接?Collision/Trigger 消息,必須將剛體附加到正在移動的對象。
- 如果直接將對象的 Angular Drag 屬性設置為無窮大,無法使對象停止旋轉。
二、碰撞體
1. 盒型碰撞體
立方體的原始形狀
2. 球形碰撞體
球體的原始形狀
3. 膠囊碰撞體
膠囊體的原始形狀
4. 網(wǎng)格碰撞體
根據(jù)對象網(wǎng)格創(chuàng)建碰撞體,不能與另一個網(wǎng)格碰撞體碰撞。典型的解決方案是對所有移動的對象使用原始碰撞體,而對靜態(tài)背景對象使用網(wǎng)格碰撞體。
5. 車輪碰撞體
專門用于創(chuàng)建汽車或其他移動車輛,可參考官方教程:車輪碰撞體教程
https://docs.unity.cn/cn/2019.4/ScriptReference/WheelCollider.html
6. 地形碰撞體
處理與 Unity 地形系統(tǒng)的碰撞
三、碰撞控制
1.碰撞矩陣

參考
Unity的選擇碰撞--碰撞矩陣(Collision Matrix)
用代碼也可以控制:
https://docs.unity3d.com/cn/2019.4/ScriptReference/Physics.IgnoreLayerCollision.html
使碰撞檢測系統(tǒng)忽略 layer1 中的任何碰撞體與 layer2 中的任何碰撞體之間的所有碰撞。
注意,IgnoreLayerCollision 將重置受影響的碰撞體的觸發(fā)器狀態(tài),因此,您可能會收到調(diào)用該函數(shù)導致的 OnTriggerExit 和 OnTriggerEnter 消息。
您可以在 Physics Inspector 中為任何層組合設置項目默認值。
using UnityEngine;
public class Example : MonoBehaviour
{
//Set the speed number in the Inspector window
public float m_Speed;
Rigidbody m_Rigidbody;
void Start()
{
//Fetch the Rigidbody component from the GameObject
m_Rigidbody = GetComponent<Rigidbody>();
//Ignore the collisions between layer 0 (default) and layer 8 (custom layer you set in Inspector window)
Physics.IgnoreLayerCollision(0, 8);
}
void Update()
{
//Press right to move the GameObject to the right. Make sure you set the speed high in the Inspector window.
if (Input.GetKey(KeyCode.RightArrow))
{
m_Rigidbody.AddForce(Vector3.right * m_Speed);
}
//Press the left arrow key to move the GameObject to the left
if (Input.GetKey(KeyCode.LeftArrow))
{
m_Rigidbody.AddForce(Vector3.left * m_Speed);
}
}
//Detect when there is a collision
void OnCollisionStay(Collision collide)
{
//Output the name of the GameObject you collide with
Debug.Log("I hit the GameObject : " + collide.gameObject.name);
}
}
2.IgnoreCollision
https://docs.unity3d.com/cn/2019.4/ScriptReference/Physics.IgnoreCollision.html
使碰撞檢測系統(tǒng)忽略 collider1 與 collider2 之間的所有碰撞。
這在某些情況下很有用。例如,防止飛彈與發(fā)射飛彈的對象發(fā)生碰撞。
注意,IgnoreCollision 不是持久性的。這意味著保存場景時忽略碰撞狀態(tài)將不會存儲在編輯器中。
如果 ignore 為 false,可能發(fā)生碰撞。將 ignore 設置為 true,可忽略碰撞。
using UnityEngine;
using System.Collections;
public class ExampleClass : MonoBehaviour
{
public Transform bulletPrefab;
void Start()
{
Transform bullet = Instantiate(bulletPrefab) as Transform;
Physics.IgnoreCollision(bullet.GetComponent<Collider>(), GetComponent<Collider>());
}
}
四、事件
當兩個對象碰撞時,可能會發(fā)生許多不同的腳本事件,具體取決于碰撞對象的剛體配置。以下圖表詳細列出了根據(jù)附加到對象的組件來調(diào)用的事件函數(shù)。某些組合僅會使兩個對象之中的一個對象受到碰撞的影響,但一般規(guī)則是物理特性不會應用于沒有附加剛體組件的對象。
發(fā)生碰撞檢測并在碰撞后發(fā)送消息
| 靜態(tài)碰撞體 | 剛體碰撞體 | 運動剛體碰撞體 | 靜態(tài)觸發(fā)碰撞體 | 剛體觸發(fā)碰撞體 | 運動剛體觸發(fā)碰撞體 | |
|---|---|---|---|---|---|---|
| 靜態(tài)碰撞體 | 是 | |||||
| 剛體碰撞體 | 是 | 是 | 是 | |||
| 運動剛體碰撞體 | 是 | |||||
| 靜態(tài)觸發(fā)碰撞體 | ||||||
| 剛體觸發(fā)碰撞體 | ||||||
| 運動剛體觸發(fā)碰撞體 |
碰撞后發(fā)送觸發(fā)器消息
| 靜態(tài)碰撞體 | 剛體碰撞體 | 運動剛體碰撞體 | 靜態(tài)觸發(fā)碰撞體 | 剛體觸發(fā)碰撞體 | 運動剛體觸發(fā)碰撞體 | |
|---|---|---|---|---|---|---|
| 靜態(tài)碰撞體 | 是 | 是 | ||||
| 剛體碰撞體 | 是 | 是 | 是 | |||
| 運動剛體碰撞體 | 是 | 是 | 是 | |||
| 靜態(tài)觸發(fā)碰撞體 | 是 | 是 | 是 | 是 | ||
| 剛體觸發(fā)碰撞體 | 是 | 是 | 是 | 是 | 是 | 是 |
| 運動剛體觸發(fā)碰撞體 | 是 | 是 | 是 | 是 | 是 | 是 |
1.碰撞事件
using UnityEngine;
using System.Collections;
public class PhysicalTest : MonoBehaviour {
// Use this for initialization
void Start () {}
void OnCollisionEnter(Collision collisionInfo)
{
Debug.Log ("enter");
Debug.Log (collisionInfo.gameObject.name);
Destroy (collisionInfo.gameObject);
}
void OnCollisionStay(Collision collisionInfo)
{
Debug.Log ("stay");
}
void OnCollisionExit(Collision collisionInfo)
{
Debug.Log ("Exit");
}
}
當兩個物體發(fā)生碰撞的時候,可以看到函數(shù)進行了調(diào)用。其中OnCollisionEnter函數(shù)在剛體進入的時候,調(diào)用一次,只要兩個物體接觸到一起,會一直調(diào)用OnCollisionStay函數(shù)直到靜止。當兩個剛體離開的時候,調(diào)用OnCollisionExit函數(shù)??梢酝ㄟ^參數(shù)獲得與之發(fā)生碰撞的物體,并且進行對應的處理。
2.觸發(fā)事件
觸發(fā)器在生活有很多例子,假設進入到一些門口時,當我們踏入到一定范圍,門會自動打開,這其實就是觸發(fā)器在現(xiàn)實生活中的例子,游戲中依然也是,例如,人物走回家的時候會自動的恢復血量,這其實就是在使用觸發(fā)器
將BoxCollider上的IsTrigger進行勾選,使其成為一個觸發(fā)器,并且修改腳本如下:
void OnTriggerEnter(Collider colliderInfo)
{
Debug.Log ("Trigger"+
colliderInfo.GetComponent<Collider>().name);
}
void OnTriggerStay(Collider colliderInfo)
{
//Debug.Log ("Trigger"+ colliderInfo.GetComponent<Collider>().name);
}
void OnTriggerExit(Collider colliderInfo)
{
Debug.Log ("Trigger");
}
可以看到,勾選之后,雖然沒有發(fā)生碰撞反應,但是能夠監(jiān)測到物體的碰撞。