1. 程式人生 > >關於Rigidbody,Collider和CharacterController三者之間的關系和用法的總結

關於Rigidbody,Collider和CharacterController三者之間的關系和用法的總結

詭異 nor 觸發 回調函數 force body 想要 die 實現

Rigidbody:多用在“物體”上,因為“物體”都是“死”的,他們的運動一般都是靠物理系統。所以對於Rigidbody的移動,不要用Translate(),要用各種“力”, 比如:Rigidbody的AddForce()方法,通過“力”來讓它移動。另外,Rigidbody可以和NavMeshObstacle合用,因為後者也是用在“物”上的,但是一般不要和NavMeshAgent合用,因為這兩個都會控制物體的運動,這樣會有race condition。如果非要合用,請勾選Is Kinematic,由此看來如果Rigidbody的Is Kinematic屬性被勾選了,那麽Rigidbody就可以和任何其他的Component合用了,比如:NavMeshAgent,CharacterController。

Collider:定義了一個物體的形狀,而我們的碰撞檢測就是根據這個形狀來進行的,隨意Collider是碰撞檢測的基礎,幾乎Unity中所有的物體都需要Collider。就算我們使用CharacterController一般會刪掉原來的Collider,不過CharacterController本身自帶了一個Capsule Collider。Collider分為static collider和dynamic collider,前者表示沒有Rigidbody的collider,後者表示有Rigidbody的collider,static collider的物體最好是靜止的物體,不要通過改變他的transform的position來使它運動,這樣不僅會給物理引擎造成很多性能損失,而且會產生一些無法預測的行為。所以如果我們非要一個有collider的物體通過我們的腳本(比如通過Translate()方法)移動,並且不希望被物理引擎控制,那麽我們需要給這個collider配上一個勾選了Is Kinematic的Rigidbody。因為Rigidbody的Is Kinematic就是讓這個Rigidbody不受物理引擎的控制。結論:在遊戲中,對於那些可能將來會移動的物體,我們不僅要加Collider,還要加上Rigidbody,如果希望是被物理引擎控制,就不勾選Rigidbody的Is Kinematic,如果希望不被物理引擎控制,希望通過自己的代碼或者動畫來控制,就勾選Rigidbody的Is Kinematic,而且這個屬性還可以在腳本中隨時改變,從而實現“ragdoll effect”,“ragdoll effect”類似於CS裏面的效果,大部分時間物體的運動是通過腳本和動畫控制的,如果遇到爆炸等,物體的運動就可以被物理系統接管了,這個時候開啟Is Kinematic最合適了。

CharacterController:多用在“角色”上,因為“角色”多是“活”的(比如:遊戲中的主角,NPC什麽的),他們的運動要麽是玩家控制,要麽是腳本控制,所以一般不需要由物理系統來控制,如果由物理系統控制,反而使得遊戲的操作性下降了。由玩家控制的“角色”肯定不要用Rigidbody,要用CharacterController。如果NPC非要用Rigidbody,那麽請勾選Is Kinematic。使用了CharacterController以後,就不要用Translate()來移動物體了,要用它自己提供的Move()方法來移動物體。否則會有一些“詭異”的問題。

CharacterController不對任何“力”產生作用,同時也不對然和物體施加“力”,即使那個物體是Rigidbody。

Rigidbody會對“力”產生響應,如果一個物體加上了Rigidbody,首先會直接對“重力”產生響應,如果再加上Collider,就會對其他“力”產生響應了。同時如果使用Translate()移動Rigidbody,那麽它會對其他的Rigidbody在碰撞的時候自動施加一個“力”。


Collider中Is Trigger的優先級高於Rigidbody(不管這個被標記為Is Trigger的Collider和Rigidbody是不是在同一個GameObject上)。因為Rigidbody是根據Collider來進行物理模擬的,Collider是Rigidbody的基礎,所以如果這個Collider是Trigger,由於Trigger只是通過調用OnTrigger*()函數來響應碰撞這個行為,而不會去響應任何實質性的碰撞(所謂的實質碰撞,就是能被觀察到的碰撞,比如碰撞之後產生運動,碰撞之後阻止物體穿過等等)。所以就算Rigidbody在,也發揮不了作用(OnCollision*()函數不會調用,同時也不會產生物理運動,也不會阻止物體穿過,反正任何可見的碰撞效果都沒有)。

同理,Collider中Is Trigger的優先級也高於CharacterController,不過Collider和CharacterController一般不放在一起,所以這裏指的是對方的物體上Collider的Is Trigger。

CharacterController(不要再加Collider,CharacterController有自帶的Capsule Collider。並且使用Move()方法移動物體)
用CharacterController去碰撞另一個物體,如果被碰撞的物體有Collider,並且Is Trigger沒有勾選,那麽無論這個物體是不是Rigidbody,CharacterController都會被這個物體“擋”下來,並且只會觸發他自己的OnControllerColliderHit()這個回調函數(也就是說,如果被碰撞的物體或者它自己有OnTrigger*(),OnCollision*()這種函數,都不會被觸發)。

所以如果我們想要實現一個CharacterController推箱子的遊戲,那麽首先這個箱子必須是Rigidbody,並且Collider不是Is Trigger,而這個“推力”需要在CharacterController的OnControllerColliderHit()這個函數中“發出”了。

如果被碰撞的物體Collider的Is Trigger方法勾選了,那麽由於Is Trigger只是將Collider當成了一個“觸發器”,而不會響應如何的實質碰撞(所謂的實質碰撞,就是能被觀察到的碰撞,比如碰撞之後產生運動,碰撞之後阻止物體穿過等等),所以CharacterController會穿過被碰撞的物體,並且OnControllerColliderHit()這個回調不會起作用,取而代之的是雙方的OnTrigger*()函數會被調用。

Rigidbody(如果勾選了Is Kinematic,那麽這個Rigidbody就不受物理引擎控制了)
一個物體一旦加上了Rigidbody,那麽就可以立刻受到“重力”的影響,如果再加上Collider,那麽就可以受到別的“力”的影響,由於Collider的Is Trigger的優先級較高,所以,如果Collider是Is Trigger(不管這個Is Trigger的Collider是不是和Rigidbody在一個物體上,反正只要碰撞雙方出現了Is Trigger),那麽這個Rigidbody就不會產生實質性的碰撞(所謂的實質碰撞,就是能被觀察到的碰撞,比如碰撞之後產生運動,碰撞之後阻止物體穿過等等)。並且OnTrigger*()被調用,而不是OnCollision*()。

關於Rigidbody的OnCollision*()回調被調用的補充說明(來自:https://docs.unity3d.com/Manual/CollidersOverview.html):
With normal, non-trigger collisions, there is an additional detail that at least one of the objects involved must have a non-kinematic Rigidbody (ie, Is Kinematic must be switched off). If both objects are kinematic Rigidbodies then OnCollisionEnter, etc, will not be called. With trigger collisions, this restriction doesn’t apply and so both kinematic and non-kinematic Rigidbodies will prompt a call to OnTriggerEnter when they enter a trigger collider.

Rigidbody:OnCollisionEnter(Collision c), OnCollisionStay(Collision c), OnCollisionExit(Collision c)

Collider Is Trigger: OnTriggerEnter(Collider c), OnTriggerStay(Collider c), OnTriggerExit(Collider c)

CharacterController: OnControllerColliderHit(ControllerColliderHit c)

產生碰撞(可以看見效果的碰撞或者像Is Trigger的Collider那樣不可見效果的碰撞)的一般條件(不是必要條件,因為有一個例外,見後面的解釋)是碰撞雙方都要有Collider(不管是否是Is Trigger),並且其中至少有一個有Rigidbody或者CharacterController。這個時候碰撞產生,並且調用相應的回調函數。如果出現多個函數都可以調用的情況,看下面的優先級。

最高優先級:有Is Trigger的Collider參與的碰撞只會調用雙方的OnTrigger*()函數,且不會產生實質碰撞(所謂的實質碰撞,就是能被觀察到的碰撞,比如碰撞之後產生運動,碰撞之後阻止物體穿過等等)。

次高優先級:有CharacterController參與且沒有Is Trigger的碰撞只會調用CharacterController的(如果被碰撞的也是CharacterController,那麽他的OnControllerColliderHit()也會被調用)OnControllerColliderHit()函數。且會產生CharacterController被物體“檔”下來的效果,不過除此之外沒有其他任何物理效果。

最低優先級:有Rigidbody參與的碰撞,並且沒有CharacterController和Is Trigger的Collider,會調用碰撞雙方的OnCollision*()回調,並且由物理系統模擬碰撞之後的物理運動。

關於Rigidbody的OnCollision*()回調被調用的補充說明(來自:https://docs.unity3d.com/Manual/CollidersOverview.html):
With normal, non-trigger collisions, there is an additional detail that at least one of the objects involved must have a non-kinematic Rigidbody (ie, Is Kinematic must be switched off). If both objects are kinematic Rigidbodies then OnCollisionEnter, etc, will not be called. With trigger collisions, this restriction doesn’t apply and so both kinematic and non-kinematic Rigidbodies will prompt a call to OnTriggerEnter when they enter a trigger collider.

為何Collider的Is Trigger的優先級最高?因為Collider是一切碰撞的基礎。

例外:如果一個運動的物體A(通過改變Transform來使得它運動)和靜止的物體B發生碰撞,A只有Collider,並且不是Is Trigger,B是Rigidbody,並且Collider也不是Is Trigger,那麽任何回調函數都不回被觸發,並且沒有物理模擬碰撞效果,A會穿過B。但是如果A和B都是靜止的,並且也開始就“挨著”,那麽這個時候又會發生碰撞了,由於沒有Is Trigger,所以是雙方的OnCollisionEnter()被調用。

這個例外在官方文檔被稱為fail to sleep:
https://docs.unity3d.com/Manual/RigidbodiesOverview.html 中關於Sleeping的第二段。

在官方文檔中提到了static collider,所謂static collider,就是那些不和Rigidbody一起使用的Collider(相反的,那些和Rigidbody一起使用的Collider就叫做dynamic collider)。這些Collider一般用在靜止的物體上,不要試圖去直接改變他們Transform的position(不要使用Translate()方法或者直接給position賦值),因為這樣會對物理引擎造成很大的性能影響。(參見:https://docs.unity3d.com/Manual/CollidersOverview.html)。CharacterController是沒有額外加Collider的,而NavMeshAgent的運動是由Navigation System來掌管的,也不是屬於我們說的情況。

拓展閱讀(非常重要):
https://docs.unity3d.com/Manual/CollidersOverview.html

關於Rigidbody,Collider和CharacterController三者之間的關系和用法的總結