# [翻譯]Spotlight Team Best Practices: Collision Performance Optimization #### [原文](https://blogs.unity3d.com/2017/07/26/spotlight-team-best-practices-collision-performance-optimization/) by William Armstrong, July 26, 2017 > 在 Spotlight 團隊中, 我們與最有雄心的 Unity 開發者們合作, 嘗試去發揮 Unity 遊戲的極限. 我們看過所有對於複雜圖像、性能和設計問題的創新且出色的解決方案. 我們也看到同樣的問題和解決方案一再地出現. [此系列文章](https://blogs.unity3d.com/author/william-armstrong/)將會介紹我們與客戶合作時最常遇到的一些問題. 這些是和我們合作過的團隊辛苦習得的, 我們很自豪能夠和所有的使用者們分享他們的智慧. 大部分的問題只會當你在遊戲機或手機上處理大量遊戲內容時才會變得明顯. 如果你能在遊戲開發早期就將這些考慮進去, 就可以讓你的生活更輕鬆, 也讓你的遊戲更加不凡. ## Big Problems 有時我們可以追蹤到造成物理的效能問題的某個資源或是設定. 注意這些最好的方法是比較改變前後的運行 Profile 資料. 越早發現到效能的改變, 能越簡單的從最近的變更中找到問題點. 雖然單純的 **物體關節 physics joint** 非常快速, 但其數學理論是很複雜的. 本質上, 關節為你 **剛體 Rigidbody** 的位置、速度、加速度、旋轉等等建立了一套方程式. 如果你配置了很多擁有很多關節的剛體, 他們都要互相碰撞, 要滿足他們關節的需要, 以及不穿透的碰撞 — 這讓開銷變得非常昂貴. 在規劃複雜的關節時, 需要謹慎思考究竟需要多少關節, 碰撞的種類以及多少剛體. 你可以使用 Layers 去隱藏非必要的碰撞, 並謹慎勾選關節的 Allow Collision 選項. 你可以透過限制關節的移動範圍來減少碰撞的發生. 調整關節讓它的碰撞變得不太可能或是不可能發生, 就可以不用偵測他們了. 你可以將關節或剛體視為差值計算中的控制點, 來減少他們的數量. Profile 螢幕可以顯示再給定的時間點有多少剛體在運作. 密切關注這個數字. 剛體數量, 特別是他們靠近彼此的時候, 會對效能造成極大的衝擊. 在執行時放置物件或是產生物件, 可以輕易地放大這個數字. 當一罐汽水的移動沒有任何問題時, 試著使用相同的 prefab 建造一個超市中展示用的飲料金字塔來觸發這個問題. 注意每個加入到你遊戲內的 MeshColliders. 它可以輕易地使用可視 **網格 mesh** 進行碰撞, 但這可能就會導致顯著的性能下降, 並且總是以不顯眼的方式出現. PhysX 有一個很好的地方就是, 只針對它必要的目標才做偵測. 所以如果你將高面數的碰撞加到小型或不合適的物件上時可能運作起來沒啥問題. 但當你放大同樣的 MeshCollider 並放置到常用 RayCasts 的地方時, 就會看到效能急遽下降. 作為常規, 為預設層或是會和很多東西碰撞的物件製作低面數的自訂網格. 如果你發現特定的網格會造成問題, 而你不想或不能製作自訂的網格時, 可以調整 MeshCollider 的 SkinWidth 讓它自動產生低面數的碰撞網格. ![](https://blogs.unity3d.com/wp-content/uploads/2017/07/image2-4.jpg) _多個重疊碰撞的Shadow Tactics NPC_ ## Little Problems 我們的使用者通常會非常聰明地避開巨大且明顯會變慢的東西. 更常見到專案做出一連串理性的決策, 都會略為降低 PhysX 的更新頻率. 當擴大遊戲規模時就很容易發生. 在測試階段可以在空盒子內運作5隻AI. 當你把相同的AI丟進真實環境後, 馬上就遭遇到幀率下降, Physics.Update飆升, 而你搞不清楚究竟為何. 數百個或數千個中的物件的碰撞中, 哪一個是罪魁禍首? 你是否做足你需要做的測試了? Layers 可以在 `ProjectSettings->Physics` 中的複選框中設定 GameObjects 之間要如何碰撞. 在開始針對任特定物件進行昂貴的測試之前, 積極使用這個設定排除任何非必要的碰撞. 很多遊戲使用大型的開啟 `Is Trigger` 的碰撞體來偵測角色或其他特定物件, 通常稱之為 **觸發器 trigger volumes**. 通常這些觸發器是設定和 Default 或是所有 layer 碰撞. 讓角色在特定 layer, 並讓觸發器只在那個 layer 和角色碰撞, 就可以避免觸發器和複雜的世界網格或是地形進行碰撞偵測. 這些觸發器是否會拖慢所有東西? 開啟 `Is Trigger` 的 Collider 仍然被視為一個幾何碰撞. 移動一個觸發器, 其開銷和移動其他碰撞體是一樣的昂貴, 也會觸發大量傳送碰撞和交疊的事件. 如果你需要在每幀移動觸發器, 請確保它只與你需要的對象發生碰撞. 盡量讓觸發器越小越好, 並組合相似或重疊的觸發器. 一個常見的模式是讓 NPC 使用一個大型觸發器來檢測互動目標. 每種 NPC 正在尋找的物件都有自己的 Collider, 並帶有 OnCollision callback 的程式碼以確保該物件是它正在尋找的. 通常先快速地將多個觸發器整合在一塊, 並在 OnCollision callback 中使用 `tag/layer/distance` 篩選物件. 在大多的情況下, 可以因為完全完全繞開碰撞而得到更好的效能. 比起每個幀在碰撞世界不斷球體偵測, 使用一個共享的管理者讓所有物件註冊, 簡單地進行對所有物件進行距離檢測會更好. 如果你有一份潛在的物件清單, 它會比和整個世界偵測碰撞得到更高的效率增長. **階層 Hierarchy** 可能導致 PhysX 做了比需求更多的工作? 在 Recore 中, 我們發現幾個旋轉的環形平台, 每個方形的還都有自己的剛體. 這會導致個片段都和其他片段進行碰撞測試. 透過將平台分群自帶有剛體的共享父物件底下, 並旋轉該父物件, 這樣我們就能夠在 Physics.Update 內省下可觀的幀時間. 請注意, 將 Colliders 併到共享的 Rigidbody 物件的話, 會增加對其進行 Raycast 或 shape cast 的開銷. 從另一個角度來看, 如果你已經有在共享的剛體物件底下放置數個 colliders 的話, 你需要非常小心地不要動到他們相對於父物件的位置/旋轉/縮放. 每當剛體改變形狀時, 它的 **質心 center of mass** & **慣性張量 Inertial Tensor** 都需要重新計算, 這需要很長的幀時間. 當剛體附加在動畫角色的軀幹上時就很容易發生. 你可以使用自訂的質量中心來關閉它. 只要 GameObject 的形狀沒有太明顯的變化, 就應該不會讓人注意到的. 這些微小的系統級的問題很容易快速地累加, 所以在開發時保持緊戒並在規劃去避免它們的發生. 如果你突然在 Physics 的耗時內看到一個大波動的話, 請先查看巨大開銷中常見的問題, 看看是否有任何適用的解法. 感謝 [Mimimi Productions](http://mimimi-productions.de/) 和 [Armature Studio](http://www.armature.com/) 讓我們使用它們的遊戲作為本文的範例. 我們的下一篇會介紹非常前面的階段: 如何建立一個新專案, 讓每個人活得更輕鬆. #### translated by NaCl at 2018/09/12