2 - 射线检测入门
本文将初步介绍UE5中射线检测功能的相关概念,例如单/多射线检测的使用和可视化,扫掠检测,检测通道和检测响应等。
射线检测
概念
射线检测(Line Traces),也称光线投射(Ray Casts),用于判断游戏世界中是否存在物体位于两点之间。这个功能很重要,一般通过游戏引擎的物理系统部分调用。
射线检测用于许多游戏功能,例如:
- 检查武器开火时是否击中物体;
- 突出显示玩家视线内可以互动的物体;
- 在玩家角色绕过拐角时自动旋转围绕它的摄像机。
如上图所示,从A点射出一根射线,然后到B结束,途中接触了箱体1和2。但结果显示只击中了箱体2,箱体1没被击中是因为它的检测通道被射线检测忽略了。
使用单射线检测
要想使用单射线检测,首先需要获取当前Actor所在的世界信息。这需要我们使用GetWorld()
函数获取当前的World对象,要包括"Engine/World.h"
头文件。
我们需要通过射线检测来了解SightSource
组件和TargetActor
之间是否存在障碍物,于是需要准备下述参数:
// 射线检测
FHitResult HitResult; // 射线检测的结果
FVector Start = SightSource->GetComponentLocation();// 射线检测的起点和终点
FVector End = TargetActor->GetActorLocation();
ECollisionChannel Channel = ECC_Visibility; // 设定要进行比较的检测通道
FCollisionQueryParams QueryParams; // 设定射线检测的参数
QueryParams.AddIgnoredActor(this); // 需要忽略自身, 否则检测会提前结束
QueryParams.AddIgnoredActor(TargetActor); // 也要忽略目标Actor, 因为仅检测二者间是否有障碍物
准备好参数后便能进行按检测通道的单射线检测了:
// 进行射线检测
GetWorld()->LineTraceSingleByChannel(HitResult, Start, End, Channel, QueryParams);
射线检测的结果将会保存在HitResult
结构体中,常用到的信息如下:
bBlockingHit
:是否有阻挡命中,即在起点和终点之间有阻挡的障碍物。Location
:击中的位置。Distance
:击中点距起点的位置。ImpactNormal
:击中点的方向。GetActor()
:获取被射线击中的Actor。GetComponent()
:获取被射线击中的Actor的组件信息。
使用多射线检测
单射线检测在遇到阻挡它的障碍物后就停止检测了,也就是说,单射线检测会忽略通道设置为忽略Ignore
或重叠Overlap
的对象,碰到通道设置为阻挡Block
的对象才停止。
多射线检测则能够检查同一射线上被击中的任何对象。对于通道设置为重叠Overlap
的对象,多射线检测不会忽略它,而是将它添加到射线前进时击中的对象列表中,并且碰到通道设置为阻挡Block
的对象时才停止。
如上图所示,射线一共击中3个目标,前两个目标的通道状态是重叠,而最后一个目标的通道状态是阻挡。
使用多射线检测需要用到LineTraceMultiByChannel()
,它需要接受一个TArray<FHitResult>
作为输入,而不是单个FHitResult
。
除了通过检测通道(Trace Channel)进行射线检测外,还能通过对象类型(Object Type)进行射线检测,这需要使用LineTraceSingle/MultiByObjectType()
函数。
可视化射线检测
UE5还提供了可视化Debug功能,它支持在运行时动态绘制Debug用的对象,例如线、正方体、球体等。
要想使用可视化Debug,首先得添加DrawDebugHelpers.h
头文件。接下来便能使用了,这里可视化射线:
// Debug: 可视化射线检测
DrawDebugLine(GetWorld(), Start, End, FColor::Red);
在引擎中的效果如下:
扫掠检测
概念
除了有射线检测外,还有扫掠检测(Sweep Traces)这一工具。和射线检测相比,扫掠检测模拟在两点之间扔一个看不见的物体,该物体可被设置为各种形状,如果在检测过程中该物体和其他物体相撞,就说明扫掠检测打到了物体。
例如投掷手雷的落点预测就能通过扫掠检测完成。
使用
扫掠检测的使用如下:
// 扫掠检测
FHitResult HitResult;
FVector Start = SightSource->GetComponentLocation();
FVector End = TargetActor->GetActorLocation();
// 扫掠物体的旋转
FQuat Rotation = FQuat::Identity;
ECollisionChannel Channel = ECC_Visibility;
// 扫掠物体的形状
FCollisionShape Shape = FCollisionShape::MakeBox(FVector(20.f));
GetWorld()->SweepSingleByChannel(HitResult, Start, End, Channel, Shape, QueryParams);
检测通道
概念
检测通道(Trace Channels)是射线检测的一个重要特点,它允许我们指定在执行射线检测时使用的过滤器,这样就不会被不需要的对象阻挡。例如:
- 检查是否有可见的对象,这种对象的
Visibility
通道应该是阻挡的。而不可见的空气墙则不会在Visibility
通道上阻挡。 - 检查可交互的对象,这种对象应该在
Interaction
通道上是阻挡的。 - 检查可移动的Pawn对象,这种对象应该在
Pawn
通道上是阻挡的。
可以给一个对象指定不同的射线检测通道,以获得正确的射线检测结果:
要想修改一个Actor的检测通道,可以将Object Type处将PhysicsBody更改为Custom。
自定义通道
除了引擎内置的检测通道外,引擎还为我们预留了一些自定义的检测通道栏位。在“项目设置”,“引擎”,“碰撞”中进行设置:
设置好后需要在项目配置文档Config/DefaultEngine.ini
中找到新建的自定义通道:
+DefaultChannelResponses=(Channel=ECC_GameTraceChannel1,DefaultResponse=ECR_Block,bTraceType=True,bStaticObject=False,Name="EnemySight")
可以发现这里的Channel
是ECC_GameTraceChannel1
,那么在代码中的自定义通道名就是ECC_GameTraceChannel1
。
参考资料
中文翻译:《UE5 C++ 游戏开发完全学习教程》
英文原版:《Elevating Game Experiences with UE5》