引擎中的物理碰撞检测是我们开发过程中常用的一个功能,最近在学习UE4的过程中尝试初步学习了这部分的模块。遇到Collision无法生效的问题,在这里记录汇总顺便分享下。

在UE4下发生碰撞,需要满足如下两个条件:

  • 碰撞网格: 注意这里是碰撞网格,不是普通的Mesh。一般来说由美术从3D软件中导出FBX时同时携带Convex Collision信息或者从在UE中手动添加。注意:后面我在这里踩了一个坑!
  • 碰撞组件: 这个一般都不会忘记, 我们在添加StaticMesh对象的时候会默认挂载上Collision组件。

讲完先决条件,我们来看下发生碰撞一般会需要注意的点:

1. 配置Collision Presets:

Alt text
一般来说默认配置就可以,如果手动配置过,你应该大概知道这几种区别

Actor A Actor B 结果
Ignore 任何 物体A、B会相互穿过彼此,不会有任何碰撞事件发生
Overlap Overlap 或 Block 物体A、B会穿过彼此,overlap事件会发送给双方
Block Block 双方会被Block住无法移动,并都收到碰撞事件

2. 检查Collision组件的位置:

一个Actor如果Collision主键没有挂载在根节点上,那么他可以作为被碰撞体Block其他物体。但是如果移动该Actor主动和别的物体碰撞,他将不会收到碰撞信息。
这里我们一般只需要把StaticMesh拖动到根节点上:
Alt text

如果上面的几部你都配置对了,但是依然没有碰撞产生。那看下最后一条!我也被这个问题Block了几个小时😢

3. 检查物体的Covex Collision:

文章开始就讲了两个先决条件的第一条,仔细检查下你的mesh文件的Convex Collision是否存在。双击mesh文件,看下这里:
Alt text
如果这里数组为空就要注意啦,说明这个模型自身不带Convex Collision信息,在Collision/Convert Boxes to Convex中手动添加一个并保存。

结论

说下这次碰到问题的解决过程:
首先在上述步骤1、2都反复检查很久之后依然发现无法产生碰撞。既然UE是有源码的,那就开始进去调试下吧(想到用Unity时候只能继续猜了😭),对比下正常能碰撞的工程。 发现大致如下的差异:
如果物体不能碰撞,那移动过程中就不会被阻止。大致猜测UE中物体移动的几个接口,大致断点到在UPrimitiveComponent::MoveComponentImpl函数里进行移动的计算:

1
2
3
4
5
6
bool const bHadBlockingHit = MyWorld->ComponentSweepMulti(Hits, this, TraceStart, TraceEnd, InitialRotationQuat, Params);

// If we had a valid blocking hit, store it.
// If we are looking for overlaps, store those as well.
int32 FirstNonInitialOverlapIdx = INDEX_NONE;
if (bHadBlockingHit || (GetGenerateOverlapEvents() || bForceGatherOverlaps))

在动的过程中会通过射线检测当前物体的在World中的Hit情况,继续看下ComponentSweepMulti

1
2
3
4
5
6
7
FInlinePxShapeArray PShapes;
const int32 NumShapes = FillInlinePxShapeArray_AssumesLocked(PShapes, *PRigidActor);

// calculate the test global pose of the actor
const PxQuat PGeomRot = U2PQuat(Quat);
const PxTransform PGlobalStartPose = PxTransform(U2PVector(Start), PGeomRot);
const PxTransform PGlobalEndPose = PxTransform(U2PVector(End), PGeomRot);

这里是进入物理引擎检测之前的准备,检查当前物体上有几个物理Shapes。调试发现,正常碰撞和非正常碰撞的差异在这里的NumShapes就不一样。继续FillInlinePxShapeArray_AssumesLocked

1
2
3
4
5
6
7
8
int32 FillInlinePxShapeArray_AssumesLocked(FInlinePxShapeArray& Array, const physx::PxRigidActor& RigidActor)
{
//进入Physx物理引擎检测
const int32 NumShapes = RigidActor.getNbShapes();
Array.AddUninitialized(NumShapes);
RigidActor.getShapes(Array.GetData(), NumShapes);
return NumShapes;
}

这里就正式进入物理引擎进行shapes的检测了。 既然这里产生差异,那问题就出现在碰撞物的mesh文件上了。仔细对照该文件和其他正常能碰撞的FBX的文件发现, 原来Convex Collision的数组信息为空 !!!

到这里,遇到的这个诡异的问题也算初步解决了。回头看其实是一个很基本的问题,主要根源还是对UE引擎的不熟悉。这里汇总下来做一个反思,希望后续继续努力深入学习UE引擎。另也备忘下,给遇到同样问题的朋友提供点思路。