|
| 1 | +# UE4实现碰撞体的动态调整 |
| 2 | + |
| 3 | +## PrimitiveComponent的组成 |
| 4 | + |
| 5 | +UE4通过组合场景渲染代理(SceneProxy)和物理模拟实体(UBodySetup)完成PrimitiveComponent的创建。 |
| 6 | + |
| 7 | + |
| 8 | + |
| 9 | +其中SceneProxy主要任务是告诉Renderer如何渲染当前PrimitiveComponent, |
| 10 | +BodySetup是Component实现物理模拟和碰撞检测的代理类,它以简化的几何体来表示实际模型的碰撞几何体(Box/Capsule/Convex)。 |
| 11 | + |
| 12 | + |
| 13 | +因此为实现可动态改变的碰撞体,我们只要实现以下接口即可: |
| 14 | + |
| 15 | +``` cpp |
| 16 | + //-------------- Collision Data Provider -------------------// |
| 17 | + virtual bool GetPhysicsTriMeshData(struct FTriMeshCollisionData* CollisionData, bool InUseAllTriData) override; |
| 18 | + virtual bool ContainsPhysicsTriMeshData(bool InUseAllTriData) const override; |
| 19 | + virtual bool WantsNegXTriMesh() override; |
| 20 | + virtual void GetMeshId(FString& OutMeshId) override; |
| 21 | + //-------------- Collision Data Provider -------------------// |
| 22 | +``` |
| 23 | +
|
| 24 | +## 通过PhysX Cook ConvexMesh完成BodySetup的创建 |
| 25 | +
|
| 26 | +我们在Component完成注册的步骤上增加了程序化的碰撞体生成(见UpdateBodySetup方法): |
| 27 | +
|
| 28 | +``` cpp |
| 29 | +void UCircleTrack::OnRegister() |
| 30 | +{ |
| 31 | + Super::OnRegister(); |
| 32 | + UpdateBodySetup(); |
| 33 | +} |
| 34 | +``` |
| 35 | + |
| 36 | +首先我们要创建新的BodySetup: |
| 37 | + |
| 38 | +``` cpp |
| 39 | +UBodySetup* UCircleTrack::CreateBodySetup() |
| 40 | +{ |
| 41 | + auto BodySetup = NewObject<UBodySetup>(this, NAME_None, (IsTemplate() ? RF_Public : RF_NoFlags)); |
| 42 | + BodySetup->BodySetupGuid = FGuid::NewGuid(); |
| 43 | + BodySetup->bGenerateMirroredCollision = false; |
| 44 | + BodySetup->bDoubleSidedGeometry = true; |
| 45 | + BodySetup->CollisionTraceFlag = bUseComplexAsSimple ? CTF_UseComplexAsSimple : CTF_UseDefault; |
| 46 | + return BodySetup; |
| 47 | +} |
| 48 | +``` |
| 49 | + |
| 50 | +然后程序通过计算生成新的凸包体(ConvexMesh, 由多个四面体组成): |
| 51 | + |
| 52 | +``` cpp |
| 53 | + CollisionConvexElems.Reset(); |
| 54 | + |
| 55 | + ... |
| 56 | + for (int32 i = 0; i < AngleSlice; i++) |
| 57 | + { |
| 58 | + FKConvexElem Elem; |
| 59 | + ... |
| 60 | + Elem.VertexData.Add(...); |
| 61 | + Elem.ElemBox = FBox(Elem.VertexData); |
| 62 | + |
| 63 | + CollisionConvexElems.Add(Elem); |
| 64 | + } |
| 65 | +``` |
| 66 | +
|
| 67 | +最后调用PhysX Cook生成物理引擎使用的几何体: |
| 68 | +
|
| 69 | +``` cpp |
| 70 | + if (bUseAsyncCook) |
| 71 | + { |
| 72 | + AsyncBodySetupQueue.Add(CreateBodySetup()); |
| 73 | + } |
| 74 | + else |
| 75 | + { |
| 76 | + AsyncBodySetupQueue.Empty(); |
| 77 | + if (ArcBodySetup == nullptr) |
| 78 | + { |
| 79 | + ArcBodySetup = CreateBodySetup(); |
| 80 | + } |
| 81 | + } |
| 82 | + UBodySetup* UseBodySetup = bUseAsyncCook ? AsyncBodySetupQueue.Last() : ArcBodySetup; |
| 83 | + UseBodySetup->AggGeom.ConvexElems = CollisionConvexElems; |
| 84 | + UseBodySetup->CollisionTraceFlag = bUseComplexAsSimple ? CTF_UseComplexAsSimple : CTF_UseDefault; |
| 85 | + /* 4.17 Support |
| 86 | + if (bUseAsyncCook) |
| 87 | + { |
| 88 | + UseBodySetup->CreatePhysicsMeshesAsync(FOnAsyncPhysicsCookFinished::CreateUObject(this, &UCircleTrack::FinishPhysicsAsyncCook, UseBodySetup)); |
| 89 | + } |
| 90 | + else*/ |
| 91 | + { |
| 92 | + // New GUID as collision has changed |
| 93 | + UseBodySetup->BodySetupGuid = FGuid::NewGuid(); |
| 94 | + // Also we want cooked data for this |
| 95 | + UseBodySetup->bHasCookedCollisionData = true; |
| 96 | + UseBodySetup->InvalidatePhysicsData(); |
| 97 | + UseBodySetup->CreatePhysicsMeshes(); // Cook Convex Mesh |
| 98 | + RecreatePhysicsState(); |
| 99 | + } |
| 100 | +``` |
| 101 | + |
| 102 | +我们可以使用它来实现弧状的碰撞体,用于一些交互的响应,如下图: |
| 103 | + |
| 104 | + |
| 105 | + |
| 106 | +提供了如夹角、细分段数等参数调节。 |
0 commit comments