细说UE的碰撞与检测

本文从应用角度分析一下UE的碰撞和检测系统, 模型主要看静态模型即StaticMesh, 骨骼模型涉及到物理资产的使用和编辑, 本文暂不做分析

目录

碰撞类型

每一个参与物理世界的对象都必须配置一个碰撞类型(见下图), 在引擎中这个类型叫ObjectChannel, 与其翻译成物体通道或者物体类型不如理解为碰撞类型更为确切

image-20220429095531095

这里先要看一下UE默认的类型, 如下图

image-20220428175137478

一般的理解

  • WorldStatic: 场景中的静态物体
  • WorldDynamic: 场景中的动态物体
  • Pawn: 角色(默认包含了外层Capsule和骨骼模型)
  • PhysicsBody: 开启物理的物体
  • Vehicle: 载具
  • Destructible: 可破碎物体

但是这个也只是建议, 很多时候根据项目需求可以视情况变动, 而且一般情况下, 这几个碰撞类型是远远不够的, 那么在哪里添加呢?

找到 ProjectSettings -> Collision -> ObjectChannels -> NewObjectChannel 添加即可, 也可以看下图

image-20220428190023906

通道有数量限制, 自定义的所有Object和TraceChannel总数只能有18个, 系统已经带了7个(5个Object,2个Trace), 也就是自定义加起来最多只有11个

  • 预设

下面可以设置预设, 预设非常有用, 可以方便我们给物体设置碰撞的时候直接使用, 毕竟custom还是有点麻烦的, 也不容易维护, 预设可以设定这个碰撞类型对其他类型的响应类型(Responses), 以及通道响应类型

image-20220428190834969

配置预设的时候记得要设置碰撞是否开启, 是否开启物理或者查询

image-20220428204620707

同一种碰撞类型有时候会设置成多种预设, 比如引擎自带的Pawn和ChacterMesh

两者的碰撞类型都是Pawn(还有一个不常用的Spectator也是Pawn), 但是这里的预设Pawn是给角色的Capsule用的, 而后者是给SkeletalMesh用的, 所以ChacterMesh的预设里就把对Pawn的响应设置成了Ignore了

image-20220429100239397

碰撞响应

上面解释了物体的碰撞类型, 那么两个物体之间是怎么来处理这个相互间的碰撞的呢?

我们在编辑模型的碰撞的时候肯定会看到这3个CollisionResponses即碰撞响应类型

image-20220428191835387

  • ignore: 忽略
  • overlap:重叠
  • block: 阻挡

那么如果A对B的响应与B对A的响应不相同怎么办呢?

直接上图

image-20220428174622511

简单总结

  1. 两者有ignore的那么一定ignore
  2. 没有ignore的情况那么有overlap一定是overlap

那么然后怎么使用呢?

image-20220428192919782

首先碰撞事件接收方需要开启对应的事件, Overlap默认是开启的, Hit默认关闭

如果这个物体你确定不用参与overlap那么建议关闭这个选项, 开启是有开销的

image-20220428193353529

image-20220428193258284

录制_2022_04_28_19_32_08_376

上面的案例是2个Dynamic类型的物体, 相互间是Block的, 而且开启了Hit事件, 那么为什么没有收到Hit消息?

Hit事件的触发需要参与碰撞的2者中的任意一者开启了物理模拟效果, 当然角色是个例外, 角色的胶囊体也能触发Hit事件


如果是Overlap呢?

我们测试的2个对象改成相互Overlap

录制_2022_04_28_19_41_40_56

在这个情况下, 即使我们开启了接受Hit事件同时开了物理, Hit事件也是没有触发的. 然后两个对象都有BeginOverlap和EndOverlap事件

射线通道和射线检测

在上面也说到 碰撞类型和这个TraceChannel加起来一共能自定义11个, 那么这个TraceChannel又是什么?

这个Trace很容易造成一定歧义,很多新手同学会想当然的理解这个就是射线检测的类型, 这句话也对也不对, 这个确实是为射线准备的, 但是射线检测又不只是用这个TraceChannel.

对于射线检测, 如果简单直白的讲, 可以把TraceChannel和ObjectChannel理解为2个维度

下面用2个简单案例解释

image-20220428195223269

先设定3个碰撞类型, 正方形,菱形和梯形

2个射线通道 四边形和平行四边形

设定3个预设, 已梯形举例

image-20220428200120426

射线检测只接受Block

Overlap基本等价于Ignore

显而易见, 梯形是四边形但是不是平行四边形, 正方形和菱形都是四边形和平行四边形

所以我们把梯形的预设里面对平行四边形设置成Ignore, 对四边形设置成Block

image-20220428195455248

我们选择通道平行四边形来查询

image-20220428200214222

结果显而易见如上图所示, 射线打到了第一个对平行四边形Block的菱形


再来一个案例

image-20220428200937006

如图中, 设置5个碰撞类型对应5种动物

3个射线通道对应的是另外的3种维度

image-20220428201302154

我们还是用一样的方式查询四足动物

image-20220428201331131

同学肯定会有疑问了, 猫不是四足动物吗?

因为我们调用射线API的时候勾选了 bTraceComplex(细节请看下面简单和复杂碰撞), 射线从猫的脚中间穿了过去

如果勾选了简单碰撞, 我们再结合看猫的简单碰撞, 结果就正确了

image-20220428202422956

image-20220428202437498

如果是查询爬行动物呢?

直接上结果

image-20220428202646484

下一个问题, 如果我们不想射线打到前面的动物, 直接想打鲸鱼, 但是通过TraceChannel我们没法避开其他动物直接打到鲸鱼

这就要用到我们另外一种射线查询方式了, 也就是通过另外一种维度来查询

image-20220428202840311

如上图, 我们就指定射线只查询碰撞类型:鲸鱼

image-20220428204153309

另外, 这个通过ObjectType来查询的, 这个类型可以是数组, 我们可以用下图这种方式来同时查询多个碰撞类型

image-20220428204301667

简单碰撞和复杂碰撞

前面对猫进行射线的时候已经遇到了这个问题, 那么什么是简单碰撞, 什么是复杂碰撞呢?

这个要进入模型碰撞编辑页面

image-20220428205030072

上图红色框内的选项生成的都是简单碰撞, 复杂碰撞就是我们模型本身的多边形组成的形体(图中浅蓝色)

在右侧编辑栏有一个选项

image-20220428205154803

一般保持默认或者SimpleAndComplex(简单和复杂共存)

简单和复杂碰撞是可以共存的!!!!!!

碰撞编辑

image-20220429102921726

静态模型在导入的时候会有选项是否自动生成碰撞, 自动生成的碰撞对于一般的规则物体还凑合, 但是如果是遇到下图的这种就比较尴尬了

image-20220429103053221

UE静态物体的编辑界面可以直接像控制碰撞体做移动/旋转/缩放编辑, 但是不能调整点线面

可以添加多个, 所以如果有必要也可以放进去很多个Box慢慢的调整

也可以通过引擎的几种不同算法生成碰撞体, 如下图

image-20220429103505419

可以视情况尝试, 但是对于我们这个模型这里都不能提供较为满意的结果

这种情况可以打开ConvexDecomposition工具(默认关闭)

image-20220429103541568

image-20220429103617906

用默认参数点击Apply, 效果如下图, 已经不错了

image-20220429103718821

如果把参数拉满, 则是下图的结果

image-20220429103754227

  • 外部软件的碰撞编辑

碰撞体也可以在外部的美术软件中编辑好以后导入到UE, 只需要遵循一个命名规范即可

image-20220429110401447

用Box堆起来的碰撞体, 命名用 UCX_模型名字_序号 这种形式

导入UE的时候要去掉自动生成碰撞, 导进来以后就生成的跟maya中一样的碰撞

image-20220429110510189

这种方式的优势也是有的, 毕竟在maya中编辑模型比UE中是要容易的多

Sweap和其他射线检测(美术同学请跳过)

前面的射线检测都是用Line的形式, UE其实封装了很多种类型的射线检测方式

搜索Trace可以看到下图中的检测方式

image-20220429111256427

cpp中都放在UKismetSystemLibrary

可以看到不仅只有通道的选择, 还有各种形状的区别

这里要看一下源码

以LineTrace举例, 蓝图的LineTraceByChannel在cpp中其实叫 LineTraceSingle, LineTraceForObjectsLineTraceSingleForObjects

两者的实现如下

image-20220429113225626

image-20220429112320036

通过UWorld的中间层接口, 最终调用的都是物理层的接口

1
2
3

bool FGenericPhysicsInterface::RaycastSingle(const UWorld* World, struct FHitResult& OutHit, const FVector Start, const FVector End, ECollisionChannel TraceChannel, const struct FCollisionQueryParams& Params, const struct FCollisionResponseParams& ResponseParams, const struct FCollisionObjectQueryParams& ObjectParams);

所以如果你熟悉UWorld的接口, 也可以自己构造这些参数, 直接用UWorld的接口也是可以的, 好不好用另说, 理论上可以支持同时支持TraceChannel和ObjectChannel的查询, 自己封装即可

使用起来还是Kismet的直观, 也方便


再看各种带形状的Trace

都是构造好一个定义形状的数据结构FCollisionShape, 然后调用 UWorld::SweepSingleByChannel()来做Sweap查询的


  • 关于Multi和Single

Multi即字面意思多重, 查询会返回所有检测范围内的数据

Overlap检测

image-20220429151938533

我们看物理层的接口就可以看到, 除了上面的射线和带形状的Sweap检测, 实际上还有一种Overlap检测, 到上层接口就是下图

image-20220429152037605

顾名思义, 用Box的形状就检测这个空间的Actor或Components, 这个在做一些空间检查的时候还是蛮有用的

鼠标和触摸射线

image-20220505112004536

如上图, 鼠标和手指触摸直接有封装好的API

其他

image-20220429152205828

其实SetLocation本质上也给物体一个速度做瞬间偏移, 这个也可以打开Sweap检查, 也能收到

image-20220429152540682

录制_2022_04_29_15_26_19_341


关于Custom

对于场景中的对象的碰撞类型是可以使用Custom来自定义碰撞类型和响应等, 此举一般情况下是解决一些特殊的对象的碰撞检测, 实际情况一般还是推荐使用预设

image-20220505144414298