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")

可以发现这里的ChannelECC_GameTraceChannel1,那么在代码中的自定义通道名就是ECC_GameTraceChannel1

参考资料

  • 中文翻译:《UE5 C++ 游戏开发完全学习教程》

  • 英文原版:《Elevating Game Experiences with UE5》