文/Jerish 專欄:https://zhuanlan.zhihu.com/c_164452593

目錄

一.Mesh組件與物理

二.物理的創(chuàng)建時機

2.1 UStaticMeshComponent的物理創(chuàng)建

2.2 USkeletalMeshComponent的物理創(chuàng)建

三.物理對象的移動

四.UE4與PhysX

4.1 簡單碰撞的物理創(chuàng)建

4.2 復雜碰撞的物理創(chuàng)建

4.3 物理創(chuàng)建的后續(xù)工作

五.物理約束Constraint

5.1 簡單理解物理約束的原理

5.2 物理對象自身約束

5.3 物理約束Actor

5.4 物理約束組件

5.5 SkeletalMesh中的物理約束調(diào)整

5.6 UE中的物理約束

六.物理材質(zhì)

一.Mesh組件與物理

關(guān)于UE物理的基本使用,官方文檔以及我之前CSDN博客里已經(jīng)做了較為詳細的介紹。

這里主要是從代碼方面,簡單分析一下UE4里面的物理是如何使用與生效的,StaticMesh以及SkeletalMesh對應(yīng)的物理都是如何產(chǎn)生與作用的。第四部分會涉及到一些PhyX引擎的內(nèi)容,簡單談?wù)刄E與PhysX間的交互。

這篇文章只討論剛體物理。

首先,在游戲中常見的帶有物理的物體一般有5種,雖然這5種類型本質(zhì)上產(chǎn)生物理的規(guī)則都大同小異,但為了方便我們只針對StaticMesh與SkeletalMesh來總結(jié)。

1.膠囊體一類(USphereComponent,UBoxComponent,UCapsuleComponent)

2.靜態(tài)網(wǎng)格物體StaticMesh

3.骨骼網(wǎng)格物體SkeletalMesh

4.Landscape地形

5.PhysicsVolume(BrushComponent)

對于直接放在場景的石塊等,通常是通過3D建模軟件導入到引擎中的資產(chǎn)。導入到引擎資源文件夾后就石塊模型變成了UStaticMesh,從資源文件夾拖到場景中后就變成了StaticMeshActor。對于帶動畫表現(xiàn)的玩家模型,是通過3D建模軟件導入的帶骨骼信息的資產(chǎn)。導入到引擎資源文件夾后就變成了USkeletalMesh,從資源文件夾拖到場景中后就變成了SkeletalMeshActor。當然,單獨把SkeletalMeshActor放在場景中就如一個StaticMeshActor一樣,沒有任何動畫,也可能沒有任何物理(如果沒有特殊處理的話)。

由于UE4提倡組件式的開發(fā),Actor身上的很多特性都是通過組件提供的,所以物理數(shù)據(jù)都是掛在組件上的而不是Actor上。任何Actor上面都可以掛上N多個組件,因此一個玩家身上就可以有多個UStaticMeshCompnent與USkeletalMeshCompnent(一般還有一個膠囊體作為根組件)。舉個簡單的例子,玩家自身的模型是一個USkeletalMeshCompnent,然后身上的衣服裝備就可以用一個UStaticMeshCompnent來表示。二者最大的差別就是SkeletalMesh可以產(chǎn)生動畫,因此他自身的每個骨骼物理也就需要跟隨動畫而改變,所以相比StaticMesh要復雜不少。

對于一個靜態(tài)網(wǎng)格物體StaticMesh,他的物理一般在建模軟件里面就應(yīng)該創(chuàng)建好,導入到編輯器時UE就會根據(jù)導入的數(shù)據(jù)創(chuàng)建物理信息,當然UE4本身也提供了物理碰撞的創(chuàng)建,如圖1-1所示。不過無論哪種做法,本質(zhì)上都是在編輯器里給UStaticMeshComponent構(gòu)建一個UBodySetup,在開始游戲的時候在創(chuàng)建運行時的基本物理數(shù)據(jù)UBodyInstance。

093238pr4jp4kmfapfoaup.jpg
圖1-1 UE編輯器添加碰撞
?

UBodySetup與UBodyInstance:我個人理解UBodySetup就是一個靜態(tài)的物理數(shù)據(jù),一般在在游戲運行前就已經(jīng)構(gòu)建好了[當然,你在游戲運行時創(chuàng)建也沒什么問題]。你可以理解為一個類,編譯以后就存在了。而UBodyInstance是一個在游戲時真正起作用的物理數(shù)據(jù),可以理解為通過這個類創(chuàng)建的對象,運行時才真正出現(xiàn)。通過一個UBodySetup是可以創(chuàng)建出多個UBodyInstance的

?


而對于骨骼網(wǎng)格物體SkeletalMesh,由于數(shù)據(jù)比較多,他的物理數(shù)據(jù)存儲在PhysicsAsset里面。在游戲運行的時候,SkeletalMeshComponent會讀取物理資產(chǎn)里面的數(shù)據(jù)UBodySetup隨后再通過UBodySetup給角色創(chuàng)建對應(yīng)的基本物理數(shù)據(jù)UBodyInstance。再進一步深入就是NVIDA的PhysX物理引擎了(當然你也可以采用BOX2D物理引擎),這篇文章后面會有簡單的講解。

?

?

UE4里面除了SkeletalMeshComponent.cpp以外還有SkeletalMeshComponentPhysics.cpp,PhysAnim.cpp用來專門處理SkeletalMeshComponent物理相關(guān)的邏輯


093239eerphy8y22hy67h2.jpg
圖1-2 物理資產(chǎn)

下面的圖片描述了Mesh、 Component與物理基本類的基本關(guān)系

093240qauaa6mr4lh60jlu.jpg
圖1-3 物理相關(guān)類圖

如果對BodyInstance還是覺得比較陌生的話,不妨結(jié)合下面我們熟悉的圖來理解。我們知道在給Mesh設(shè)置物理的時候需要設(shè)置準確的碰撞通道,才能讓不同的物理之間有碰撞效果。仔細看一下,CollisionResponses,ObejctType這些其實都是FBodyInstance里面的成員,我們在編輯器里面設(shè)置的這些屬性其實就是在給BodyInstance設(shè)置(進一步還會去給到PhysX里面的PxRigidActor,后面講)。

093239ddkoz0a005edkd26.jpg
圖1-4

如果我們想在編輯器里直觀的看到是否創(chuàng)建了物理就調(diào)用控制臺命令Show Collision既可。下圖就顯示了角色的膠囊體碰撞以及對應(yīng)的骨骼物理碰撞(多個膠囊體組合)。

093238z4emgzd4dx6u43rr.jpg
圖1-5

二.物理的創(chuàng)建時機

前面大致的描述了UE4里面基本組件與物理之間的邏輯關(guān)系,我們看到上面無論是Staticmesh還是SkeletalMesh都會通過BodySetup來創(chuàng)建物理,而BodySetup最終又會調(diào)用BodyInstance來產(chǎn)生真正的物理。下面我們從游戲內(nèi)具體的物理初始化流程分析一下。

2.1 UStaticMeshComponent的物理創(chuàng)建

首先是UStaticMeshComponent,可以看到在場景里面加載Actor并注冊UActorComponent的時候會對UPrimitiveComponent組件進行物理信息創(chuàng)建。其實除了UStaticMeshComponent以外,所有繼承自UPrimitiveComponent的組件(第一部分提到的那5種都是)都會在注冊后就創(chuàng)建物理數(shù)據(jù)(對于直接繼承自UActorComponent的組件,如移動組件就不會執(zhí)行該操作)。因此除了SkeletalMeshComponent以外(這個后面再分析),其他繼承自UPrimitiveComponent的組件物理創(chuàng)建的時機都很明確,也就是UActorComponent被注冊的時候創(chuàng)建物理。(當然還有一些特殊情況也需要更新物理,比如更換模型的時候)

093239tuwcb9bzbczw5mg4.jpg
圖2-1 加載場景StaticMesh物理的創(chuàng)建堆棧圖

093240cz1iz3w92vjvcinm.jpg
圖2-2 玩家出生時膠囊體物理的創(chuàng)建堆棧圖

在注冊組件時是否要創(chuàng)建物理數(shù)據(jù)?可以參考下面代碼。其實很明顯的有三個條件,

1.是否已經(jīng)創(chuàng)建過了

2.是否能獲取到當前的物理場景(物理場景的變量為FPhysScene* PhysicsScene,理解為與游戲世界同時存在的一個物理世界。這個PhysicsScene一般是在初始化World信息,也就是在void InitWorld(const InitializationValues IVS= InitializationValues())時創(chuàng)建)

3.是否應(yīng)該創(chuàng)建 ShouldCreatePhysicsState。很明顯想控制是否給組件創(chuàng)建對應(yīng)的物理數(shù)據(jù),寫在這里最合適不過了。比如,所有繼承自UActorComponent而且沒有重寫該函數(shù)的組件都會直接返回false,而UPrimitiveComponent就重寫了這個函數(shù)。

093240hgri394yblb90ycj.jpg
圖2-3 注冊時是否創(chuàng)建物理的條件代碼截圖

2.2 USkeletalMeshComponent的物理創(chuàng)建

USkeletalMeshComponent與其他帶物理組件不同,一般來說我們并不會在玩家一出生就創(chuàng)建出所有的骨骼物理,也不會讓玩家的骨骼物理一直存在著。原因很簡單,就是為了提升性能。對于一般的帶物理的組件,我們只需要給他配置一個簡單的碰撞體既可(包括Sphere,Box,Capsule等)。這樣一個簡單的物理組件在游戲運行時的開銷是很小的,然而對于一個USkeletalMeshComponent,我們?yōu)榱司_幾乎需要給所有的骨骼都創(chuàng)建一個基本的物理單位,一旦玩家或者NPC過多,這個消耗是非??捎^的。然而,我們也不能放棄使用USkeletalMeshComponent的物理,因為一旦我們的游戲想實現(xiàn)精準的打擊,攻擊不同位置的效果不同的時候,就必須要用到骨骼的物理。因此,常見的解決方案就是在需要的時候創(chuàng)建物理,在不需要的時候就拿掉。

?

?

默認引擎的SkeletalMesh物理會一直存在 參考圖1-5。實際上,也不是一定要動態(tài)創(chuàng)建于刪除skeletalMesh組件的物理,要結(jié)合游戲考慮是否要優(yōu)化這一部分。


我們還是從組件的注冊說起,USkeletalMeshComponent的物理的初始化與前面的組件不同,他首先重載了void USkeletalMeshComponent::CreatePhysicsState()函數(shù)。并通過調(diào)用InitArticulated函數(shù)來對所有的骨骼來進行物理的初始化,這是組件初始化時的邏輯代碼。我們簡單分析一下,

093656qzsorfquossihir5.jpg
圖2-4 重載CreatePhysicsState代碼截圖

可以看到USkeletalMeshComponent創(chuàng)建物理有兩個執(zhí)行路徑,一種是和其他組件一樣使用基類UPrimitiveComponent的方法創(chuàng)建物理數(shù)據(jù),另一種是用USkinnedMeshComponent里面的PhyiscsAsset數(shù)據(jù)。(bEnablePerPolyCollision這個變量默認是0,而且引擎沒有修改過)

所以,可以看出,正常的USkeletalMeshComponent初始化物理是通過函數(shù)

void InitArticulated(FPhysScene* PhysScene,bool bForceOnDedicatedServer=false);來對每一個骨骼來初始化物理的(Articulate表示關(guān)節(jié)連接的)。如果開發(fā)者不做任何處理的話,那么USkeletalMeshComponent的物理數(shù)據(jù)就會在注冊時創(chuàng)建并且在游戲過程中一直存在著。

一般來說,USkeletalMeshComponent在每幀TickComponent的時候都會調(diào)用到USkeletalMeshComponent::RefreshBoneTransforms函數(shù),顧名思義就是更新骨骼的坐標旋轉(zhuǎn)等。

093657vdczpx2cokopfpm3.jpg
圖2-5 Tick更新骨骼Transform堆棧圖

RefreshBoneTransforms函數(shù)里面,可以根據(jù)CPU核數(shù)等相關(guān)參數(shù)來決定是否開一個線程來單獨更新動畫以及相關(guān)物理數(shù)據(jù)(最后還是調(diào)用InitArticulated函數(shù)創(chuàng)建物理)。

093657xpkzmhkntu5xlo5o.jpg
圖2-6 開啟單獨線程來處理動畫物理數(shù)據(jù)

下面的堆棧圖就是引擎通過單獨開一個線程來處理物理等數(shù)據(jù)。

093657a6bh5zaakbfarkz9.jpg
圖2-7單獨線程來處理動畫物理數(shù)據(jù)調(diào)用堆棧圖

前面我們提到要選擇讓物理在需要的時候去生成,而在一般狀態(tài)下要拿掉。那這是如何做到的?其實我們可以在USkeletalMeshComponent::UpdateKinematicBonesToAnim 去處理,這個函數(shù)意義是根據(jù)動畫的變換去更新當前的物理數(shù)據(jù),每一幀都需要執(zhí)行?;舅悸肪褪牵繋既z測是否需要骨骼物理數(shù)據(jù),如果需要我們創(chuàng)建對應(yīng)的物理數(shù)據(jù)(已經(jīng)創(chuàng)建過了就直接返回)。如果檢測到當前不再需要更新物理,就調(diào)用USkeletalMeshComponent::TermArticulated()刪除物理數(shù)據(jù)。我們已經(jīng)知道,運行中的物理數(shù)據(jù)全部存儲在BodyInstance里面,而這個函數(shù)就會把我們當前存儲在Bodies里面的所有BodyInstance數(shù)據(jù)全部清除。忘記Bodies的朋友可以回頭看一USkeletalMeshComponent的類圖。

?

?

  1. 同時這里還有一段注釋可以參考一下:
  2. // This below code produces some interesting result here
  3. // - below codes update physics data, so if youdont update pose, the physics wont have the right result
  4. // - but if we just update physics bone withoutupdate current pose, it will have stale data
  5. // If desired, pass the animation data to thephysics joints so they can be used by motors.
  6. // See if we are going to need to update kinematics

圖3-1 一般的物理移動調(diào)用堆棧

如果你發(fā)現(xiàn)你的堆棧是圖3-2樣子的也不用擔心,這是由于移動組件開啟了bEnableScopedMovementUpdates屬性。他等一次移動完全成功后再觸發(fā)子對象移動,物理移動等,因為移動過程中可能出現(xiàn)移動不合法重置的情況。這個功能有助于提高移動性能。

093901p2lw22hqrozhz0oz.jpg
圖3-2 開啟延遲更新后的調(diào)用堆棧

前面描述的是膠囊體這種簡單類型的物理碰撞,如果是一個SkeletalMeshComponent組件呢?他的身上有與骨骼數(shù)量相等的BodyInstance,如何移動?

其實本質(zhì)上差不多,通過堆棧可以看出USkeletalMeshComponent重寫了函數(shù)OnUpdateTransform,隨后會調(diào)用UpdateKinematicBonesToPhysics函數(shù)更新所有的物理數(shù)據(jù)。

093953fqw6jq4w67ju5wjc.jpg
圖3-3 SkeletalMeshComponent更新移動

在更新動畫的時候也會觸發(fā)UpdateKinematicBonesToPhysics函數(shù)。

093952k09nm6anmg5u060f.jpg
圖3-4 更新動畫時更新物理

如果開啟了物理托管,那么角色的移動就完全交給物理引擎去處理。通過下面這個接口獲取物理引擎返回的Transform并更新自己的位置。

093952xxkt28ko8tjwfz8t.jpg
圖3-5

對于SkeletalMeshComponent,上面的操作只能讓組件與根骨骼位置匹配。其他的骨骼還需要通過USkeletalMeshComponent::BlendInPhysics進一步計算,

093952wu1p7zj0xet0ssxe.jpg
圖3-6

四.UE4與PhysX

前面我們已經(jīng)了解到BodyInstance在UE邏輯里是一個運行時的物理的基本單位。而實際在PhysX引擎中,也同樣存在一個物理基本單位,這個物理單位就PxRigidActor。一個BodyInstance對應(yīng)一個PxRigidActor(實際上就是BodyInstance::InitBody時創(chuàng)建一個對應(yīng)的PxRigidActor),這樣我們就可以將UE引擎與PhysX引擎結(jié)合起來使用了。

?

?

這個時候,我再提出一個問題,真正的物理碰撞是如何檢測的呢?


這個問題確實值得我們深思,而且不同情況下檢測的方法是不一樣的。舉個例子,想知道兩個球是否產(chǎn)生碰撞,那么只要判斷兩個球心的距離就可以了。而兩個復雜模型的碰撞,可能需要通過判斷兩個三角面是否有交集來判斷。我這里提出這個問題,只是想提醒大家,物理引擎里面的Actor也一樣需要知道其本身的形狀,然后進一步來處理碰撞邏輯。所以,在創(chuàng)建一個基本物理單位PxRigidActor之后,我們還需要給其創(chuàng)建基本的幾何形狀(在引擎里面叫做Shape),這個邏輯的處理就在函數(shù)UBodySetup::AddShapesToRigidActor(新版本叫UBodySetup::AddShapesToRigidActor_AssumesLocked)??吹竭@個函數(shù),我們就知道Shape是通過UBodySetup來創(chuàng)建的,同時這個幾何形狀的數(shù)據(jù)也是存儲在UBodySetup里面的。

PhysX里面提供的類型有下面幾種,官方聲稱前四種是簡單碰撞,第五種是復雜碰撞,而實際上凸面體碰撞的處理與三角面相似,所以也可以理解為復雜碰撞:

1.PxSphereGeometry 球形

2.PxBoxGeometry盒子

3.PxCapsuleGeometry膠囊體(SkeletalMesh常用)

4.PxConvexMeshGeometry 凸面體

5.PxTriangleMeshGeometry三角面

4.1 簡單碰撞的物理創(chuàng)建

這5種類型里面,前4種的生成好的物理數(shù)據(jù)都存儲在UBodySetup的FKAggregateGeom AggGeom里面,按照官方文檔的分類,我們稱他們?yōu)楹唵闻鲎差愋?。實際上,凸面體的碰撞處理并不像前面幾個那樣簡單。

前三種碰撞的添加在代碼實現(xiàn)上也比較簡單,我們在編輯器添加碰撞的時候會通過函數(shù)GenerateSphereAsSimpleCollision,GenerateBoxAsSimpleCollision,GenerateSphylAsSimpleCollision分別將碰撞數(shù)據(jù)添加AggGeom的SphereElems,BoxElems,SphylElems里面。正如我前面所說的那樣,判斷兩個球體是否碰撞很容易,所以這幾種碰撞類型不需要很復雜的數(shù)據(jù)來記錄與處理,PhysX引擎可以很容易的獲取到這些碰撞類型對應(yīng)的數(shù)據(jù)并做處理。

凸面體與前面三種碰撞類型都不同。由于其可以通過配置生成一個較為復雜的碰撞,而且碰撞體的頂點都是通過算法生成的,所以他需要經(jīng)過一個物理Cook的過程。這個過程類似渲染,把所有的三角面的頂點信息和索引提供給PhysX引擎隨后PhysX利用這些數(shù)據(jù)Cook出一個完整的碰撞模型,不過這個過程需要一定的時間來執(zhí)行。一般來說,我們在游戲編輯器添加AutoConvexCollision碰撞并執(zhí)行Apply的時候,就會執(zhí)行這個凸面體的Cook過程。Cook的過程與三角面的Cook過程相似,后面再詳細分析。(下圖是簡單類型碰撞的添加)

094224rxhcbb69bpbgux6c.jpg
圖4-1

我們知道SkeletonMeshComponent里面使用PhysicsAsset來創(chuàng)建骨骼動畫的物理,那PhysicsAsset里面的BodySetup里面的數(shù)據(jù)是如何初始化的?他里面的shape類型是什么?看下面的兩個堆棧,當我們導入一個帶有動畫的骨骼資產(chǎn)時,首先會判斷該SkeletalMesh有沒有物理資產(chǎn)。

如果沒有就會調(diào)用圖3-2創(chuàng)建物理資產(chǎn),隨后執(zhí)行第二步,根據(jù)每個骨骼初始化對應(yīng)的物理。當前引擎中,會針對沒有物理資產(chǎn)的SkeletalMesh的每個骨骼默認初始化一個膠囊體類型的簡單碰撞。這個類型是通過FPhysAssetCreateParams NewBodyData;初始化后作為參數(shù)傳遞給物理資產(chǎn)的,所以一般來說我們的角色的物理資產(chǎn)都是膠囊體的。

094223f3t8uibs8ku4dnnu.jpg
圖4-2

094224oyowbnb9qtn9dbmb.jpg
圖4-3

當然,當你導入模型之后。你也可以根據(jù)你的骨骼資產(chǎn)創(chuàng)建一個新的PhysicsAsset,在創(chuàng)建的時候右鍵骨骼資產(chǎn)文件——Create——CreatePhysicsAsset,隨后會彈出下面的界面,可以根據(jù)需求針對每個骨骼創(chuàng)建一個指定類型的Shape碰撞。如果想單獨調(diào)整個別骨骼的碰撞,就要打開PhysicsAsset在編輯界面里單獨處理了。(SkeletonMeshComponent的物理并不一定就是簡單的膠囊體碰撞,也可能復雜碰撞,下個小結(jié)分析)

094225x90jh97a423xi2rs.jpg
圖4-4

094224tpz7oco660y56865.jpg
圖4-5

4.2 復雜碰撞的物理創(chuàng)建

最后一種碰撞類型是復雜類型,那么有多復雜呢?其實就是根據(jù)Mesh的網(wǎng)格信息(也就是三角面的數(shù)量)來進行物理的生成,所以模型面數(shù)越多那自然就越復雜。生成好的三角面的物理數(shù)據(jù)都存儲在UBodySetup的TriMesh與TriMeshNegX里面。

094225s2400lb0wg09lweo.jpg
圖4-6

看過官方文檔碰朋友肯定知道,我們可以通過StaticMesh里面Collision

Complexity設(shè)置來改變其碰撞的復雜度,當我們標記為UseComplexAsSimple的時候,其實就會在此時去除簡單碰撞,并給對應(yīng)的Mesh資產(chǎn)創(chuàng)建一份復雜的物理碰撞,這個時候就會執(zhí)行三角面的Cook過程。

094902stay3ayle5lze0hz.jpg
圖4-7

094902h45onb5b959xmmmp.jpg
圖4-8

看上面的函數(shù)堆棧,在更新上面的配置的時候需要重新創(chuàng)建PhysicsMeshs并獲取到CookData。GetCookedData就會調(diào)用FDerivedDataPhysXCooker里面的FDerivedDataPhysXCooker:: BuildConvex處理凸面體物理或者FDerivedDataPhysXCooker::BuildTriMesh處理三角面的物理數(shù)據(jù)。更深入一步,在這個兩個函數(shù)里又分別通過PhysX引擎IPhysXFormat接口里面的PxCooking調(diào)用cookConvexMesh函數(shù)以及cookTriangleMesh函數(shù)。

另外,想要執(zhí)行Cook,我們一定要準確的獲取到碰撞模型的所有頂點信息,對于StaticMesh三角面碰撞。這個頂點信息就是通過渲染的Lod信息來取到的,具體的操作在函數(shù)GetPhysicsTriMeshData里面,執(zhí)行堆棧如下:

094901cj77p4yyjkpkjpk4.jpg
圖4-9

復雜物理的生成與渲染很像,如果你需要動態(tài)的去生成與刪除物理,那么一定要慎重考慮這個動態(tài)創(chuàng)建的過程消耗如何?我們平時對StaticMesh物理的Cook過程都是在編輯器里面就完成了。如果游戲中做這個操作很有可能造成卡頓,如果非要這么做也可以考慮使用異步線程FNonAbandonableTask來執(zhí)行這個過程。

官方文檔上可以看到靜態(tài)模型是如何創(chuàng)建復雜物理的,但是好像沒有說骨骼資產(chǎn)如何創(chuàng)建,骨骼模型是不是不可以創(chuàng)建?并不是。在4.8以后的版本里,我們打開骨骼資源文件找到bEnablePerPolyCollision屬性并勾選既可。在舊版本4.5里面需要到角色藍圖找到對應(yīng)的SkeletalMesh組件里勾選bEnablePerPolyCollision屬性。

?

?

注:新版本skeletalMesh組件里也有這個屬性,但是勾選無效,這是引擎的一個Bug


4.3 物理創(chuàng)建的后續(xù)工作

前面的操作是將函數(shù)UBodySetup::CreatePhysicsMeshes()展開,將物理Cook的過程執(zhí)行完畢。隨后,UBodySetup::AddShapesToRigidActor函數(shù)會獲取AggGeom以及TriMesh里面已經(jīng)Cook好的數(shù)據(jù),創(chuàng)建對應(yīng)的物理Shape。

另外,我們在創(chuàng)建物理的時候還分為靜態(tài)與動態(tài)兩種,他們通過組件上的OwnerComponent->Mobility

!= EComponentMobility::Movable來控制。很明顯,靜態(tài)碰撞與動態(tài)碰撞的消耗是不同的。

?

?

//創(chuàng)建靜態(tài)PxRigidActor
GPhysXSDK->createRigidStatic(PTransform);
//創(chuàng)建動態(tài)PxRigidActor
GPhysXSDK->createRigidDynamic(PTransform);


截止到這里,我們已經(jīng)基本上完成了物理數(shù)據(jù)的初始化。然而,我們知道在游戲里面,還有很多詳細的設(shè)置,比如碰撞通道,碰撞類型等。這些數(shù)據(jù)也必須要及時更新與處理,這些邏輯與相關(guān)標記的處理在

?

?

  1. FBodyInstance::UpdatePhysicsFilterData>FBodyInstance::UpdatePhysicsShapeFilterData()

圖4-10

PhysX里面的Shape標記。

?

?

  1. struct PxShapeFlag
    • {
      • enum Enum
        • {
          • eSIMULATION_SHAPE = (1<<0),
            • eSCENE_QUERY_SHAPE = (1<<1),
              • eTRIGGER_SHAPE = (1<<2),
                • eVISUALIZATION = (1<<3),
                  • ePARTICLE_DRAIN = (1<<4)
                    • };
                      • };

圖5-1

5.3 物理約束Actor

他本身是一個Actor對象,利用它可將兩個Actors 連接起來(假定成一個物理模擬體),并應(yīng)用限制和力度。引擎擁有一些默認關(guān)節(jié)類型 (球窩式ball-and-socket、鉸鏈式hinge、棱柱式prismatic),區(qū)別只存在于它們的對Actor的6個自由度的限制差異??扇芜x一種關(guān)節(jié)開始,自行進行調(diào)整試驗。里面的參數(shù)比較多,除了基本的限制操作外還可以通過Motor參數(shù)添加驅(qū)動力,后面會對一些常見的應(yīng)用場景進行簡單進行分析,其他的建議大家查閱相關(guān)資料后多去嘗試。

095324yg73iccyqgjj3k6a.jpg
圖5-2

上圖就是物理約束Actor的配置,一個物理約束Actor能且只能綁定兩個Actor對象,這兩個對象至少有一個要開啟物理模擬。如果需要的話,我們也可以將一個SkeletalMesh的骨骼與另一個Actor綁定,甚至我們還可以指定一個Actor的某個組件與另一個Actor的某個組件綁定。具體的操作草考官方文檔。

我們還可以對一個Actor進行多次約束綁定來模擬更多效果,舉個例子:假如我要模擬一個秋千,剪斷一邊的繩子后,這邊就會下落向另一邊旋轉(zhuǎn)。我們可以用兩個ConstraintActor綁定秋千上面的支架與下面的秋千板,然后設(shè)置秋千支架不可移動。兩個ConstraintActor都設(shè)置允許3個自由度的旋轉(zhuǎn)(Angular Limits都設(shè)置為free),Z值做30cm限制(Linear limit的Zmotion設(shè)置為limited),分別放在繩子與木板相連的位置(很重要,因為兩個ConstraintActor方向不同,導致其兩個方向都不能旋轉(zhuǎn))。當玩家用道具砍掉一邊的繩子后,對一個ConstraintActor解綁,就可以模擬一邊被砍斷的情形了。

5.4 物理約束組件

物理約束組件(Physics Constraint Components)的使用方法和 物理約束 Actors 相同,不同之處是其在藍圖中使用,可在 C++ 中進行創(chuàng)建。物理約束組件結(jié)合了藍圖的靈活和 C++ 的強大,您可利用它對項目中的任意物理形體設(shè)置約束。官方文檔也有案例,不再贅述。

5.5 SkeletalMesh中的物理約束調(diào)整

打開物理資源文件(PhysicsAsset),默認是Body模式,點擊按鈕我們會看到有一個Constraint Mode,點擊就會進入物理約束模式。

095326hada33pupatt1agl.jpg
圖5-3

095324vz0lqmnn5dye5c8l.jpg
圖5-4

進入物理約束模式后,首先注意紅色標記,我們有19個骨骼18個關(guān)節(jié),同時也有18個物理約束。其實,這里可以猜出來,UE在創(chuàng)建對應(yīng)的PhysicsAsset的同時會對每一個骨骼關(guān)節(jié)創(chuàng)建一個對應(yīng)的物理約束,這正好符合我們的常識。

那是不是說,我們每次導入一個SkeletalMesh就會完美的創(chuàng)建一個帶有物理約束的PhysicsAsset?并不是,每當我們根據(jù)一個SkeletalMesh創(chuàng)建一個物理體的時候,是會創(chuàng)建對應(yīng)的物理資產(chǎn),但是這個資產(chǎn)往往問題很多,無法使用,如圖5-5??梢钥吹剿哪z囊體數(shù)量很多,重疊嚴重,而且物理約束都是默認的值。

095322a9el6mksmn4lw6w6.jpg
圖5-5 默認創(chuàng)建的物理資產(chǎn)

在上面的菜單位置,我們可以看到4個默認的物理約束方式,下面一一描述。

095323pxrzqrncwudmm5mn.jpg
圖5-6

球窩式ball-and-socket:類似上面圖片的效果,一個球狀的骨骼塞到凹槽里面,可以在一定空間內(nèi)旋轉(zhuǎn),類似人的肘關(guān)節(jié)。下面完全開放了3個旋轉(zhuǎn)自由度,但通常情況我們經(jīng)常要對各個方向做一定的限制。

095322lfd3pkfdaag5m6mt.jpg
圖5-7

鉸鏈式hinge:類似上面圖片的效果,兩個物體通過鉸鏈相連,只能繞著固定方向旋轉(zhuǎn),如門的開關(guān)。這里完全打開了繞著X軸方向的旋轉(zhuǎn)。

095325wfuu114hwn7fwaqj.jpg
圖5-8

棱柱式prismatic:兩個剛體間的角度是固定的,只能在一個固定的軸上滑動。這里我們看到只能沿著X軸產(chǎn)生位移。

095325r9gjt8rs9rtkrfff.jpg
圖5-9

角色關(guān)節(jié) Skeletal:其實與球窩式很相似,但是在各個旋轉(zhuǎn)方向上都有限制。

095326qfs2xonx3d12m522.jpg
圖5-10

下圖是官方根據(jù)實際情況調(diào)整過后的物理約束:可以看出來他把沿著X軸方向的旋轉(zhuǎn)給禁掉了,這樣更符合現(xiàn)實情況(可以自己試一下)。

當然,我們可以根據(jù)自己的情況做特殊處理,比如角色的骨骼上可能綁定了一個武器,那么這個武器的物理約束就可以設(shè)置成6個自由度的。不過這些全都是我們在開啟角色物理模擬時才會出現(xiàn)的效果。

在實際應(yīng)用中,我們很多情況下需要固定一個物體,然后另一個物體相對進行移動或者旋轉(zhuǎn),比如門,鐘擺等的實現(xiàn)。所以只需要開啟一個物體的simulates即可。

095322zk6m5p6edgklkfpn.jpg
圖5-11

關(guān)于約束限制的驅(qū)動力,可以通過Motor來添加,有位移驅(qū)動與旋轉(zhuǎn)驅(qū)動。具體可以參考官方的ContentExample的PhysicsMap。

095325ewjn97fnhzp5f5nc.jpg
圖5-12

5.6 UE中的物理約束

在UE源碼里面,物理約束Actor的類是APhysicsConstraintActor函數(shù),物理約束組件是UPhysicsConstraintComponent。APhysicsConstraintActor本身也是使用約束組件的功能。

約束組件里面最重要的數(shù)據(jù)就是FConstraintInstance ConstraintInstance,該對象包含了我們在編輯器中所見的各項參數(shù),同時會將相關(guān)的約束數(shù)據(jù)保存到PhysX引擎中的PxD6Joint類型的數(shù)據(jù)里面。具體細節(jié)請查看源碼分析。

100023e5xaxv8lw6ivv6v6.jpg
圖5-13

六.物理材質(zhì)

?

?

物理材質(zhì)在官方上這樣定義:用于定義當物理對象和世界進行動態(tài)交互時它所做出的反應(yīng)。


說白了,就是他在游戲世界應(yīng)該是什么材料的,雖然我們通過材質(zhì)的表現(xiàn)可以看出來他是一塊木頭還是一塊鐵,但實質(zhì)上普通的材質(zhì)只是在視覺上達到了效果。真正要在木頭上跑步,敲擊,采集等等,你肯定還需要其他的邏輯去處理。UE里面提供了物理材質(zhì)便于你去定義你的游戲世界里面的物理類型(即物理材質(zhì)),物理材質(zhì)的創(chuàng)建很簡單,編輯器——AddNew——Physics——PhysicalMaterial。創(chuàng)建之后打開就是這樣的,參數(shù)很少,基本上就是設(shè)置一下摩擦系數(shù)和物理表面類型,具體可以參考官方文檔的介紹。

100024q66bpweodewdmpxv.jpg
圖6-1

關(guān)于表面類型,可以打開編輯器Edit——ProjectSettings——Physics——PhysicalSurface來查看與添加。

100024oy5inqe98z2hmq1l.jpg
圖6-2

物理材質(zhì)添加完之后,需要賦給對應(yīng)的材質(zhì)、材質(zhì)實例、StaticMesh、SkeletonMesh等等,在各個藍圖搜索physicalMaterial既可。另外,對于每一個Mesh(實際上BodyInstance)都存在一個PhysMaterialOverride,可以覆蓋你前面設(shè)置的物理材質(zhì)。

最后,簡單說一下一般游戲里面的使用場景。

第一點是走路與落地的音效,在PrimalCharacter(玩家與動物通用)里面會通過GetFootPhysicalSurfaceType函數(shù)獲取腳下地面的材質(zhì)類型,進而根據(jù)檢測到的類型播放事先預制好的音效。

第二點是武器擊中不同物體對應(yīng)的特效與音效,思路基本上相同。



銳亞教育

銳亞教育,游戲開發(fā)論壇|游戲制作人|游戲策劃|游戲開發(fā)|獨立游戲|游戲產(chǎn)業(yè)|游戲研發(fā)|游戲運營| unity|unity3d|unity3d官網(wǎng)|unity3d 教程|金融帝國3|8k8k8k|mcafee8.5i|游戲蠻牛|蠻牛 unity|蠻牛