FHitResult:Location 和 ImpactPoint

在上一篇文章中,我们已经添加了抓取组件。在这一篇文章中,我们了解一下抓取的点如何获取。

我们先回顾一下之前的扫掠检测实现,如代码清单 1 所示,通过调用 SweepSingleByChannel 函数实现。如果发生碰撞,则可以进一步使用 FHitResult 里面的碰撞信息。

代码清单 1 FindComponentByClass
  1. void UGrabber::Grab()
  2. {
  3.     FVector Start = GetComponentLocation();
  4.     FVector End = Start + GetForwardVector() * MaxGrabDistance;
  5.  
  6.     DrawDebugLine(GetWorld(), Start, End, FColor::Red);
  7.  
  8.     FCollisionShape Sphere = FCollisionShape::MakeSphere(GrabRadius);
  9.     FHitResult HitResult;
  10.     bool HasHit = GetWorld()->SweepSingleByChannel(HitResult,
  11.         Start, End,
  12.         FQuat::Identity,
  13.         ECC_GameTraceChannel2,
  14.         Sphere);
  15.  
  16.     if (HasHit)
  17.     {
  18.         AActor* HitActor = HitResult.GetActor();
  19.         UE_LOG(LogTemp, Display, TEXT("Hit actor: %s"), *HitActor->GetActorNameOrLabel());
  20.     }
  21.     else
  22.     {
  23.         UE_LOG(LogTemp, Display, TEXT("No actor hit"));
  24.     }
  25. }

我们可以把发生碰撞的点当成抓取的点。查看 FHitResult 官方文档,通过成员名字,我们猜测 LocationImpactPoint 成员是我们想要的。

根据官方的定义:

FHitResult.Location 是指在碰撞检测时,运动形状(此处是球体)最终停留的位置。即如果发生碰撞,运动形状在世界空间中停留的位置。

FHitResult.ImpactPoint 是指运动形状与目标物体实际接触的点。即碰撞发生时的接触点。

上述介绍可能看着还不够直观,我们可以可视化一下。如代码清单 2 所示,我们通过 DrawDebugSphere() 函数绘制发生碰撞时的 Location 和 ImpactPoint 的位置。

代码清单 2 DrawDebugSphere
  1. void UGrabber::Grab()
  2. {
  3.     FVector Start = GetComponentLocation();
  4.     FVector End = Start + GetForwardVector() * MaxGrabDistance;
  5.  
  6.     DrawDebugLine(GetWorld(), Start, End, FColor::Red);
  7.     DrawDebugSphere(GetWorld(), End, 10, 10, FColor::Blue, true);
  8.  
  9.     FCollisionShape Sphere = FCollisionShape::MakeSphere(GrabRadius);
  10.     FHitResult HitResult;
  11.     bool HasHit = GetWorld()->SweepSingleByChannel(HitResult,
  12.         Start, End,
  13.         FQuat::Identity,
  14.         ECC_GameTraceChannel2,
  15.         Sphere);
  16.  
  17.     if (HasHit)
  18.     {
  19.         DrawDebugSphere(GetWorld(), HitResult.Location, 10, 10, FColor::Green, true);
  20.         DrawDebugSphere(GetWorld(), HitResult.ImpactPoint, 10, 10, FColor::Red, true);
  21.         AActor* HitActor = HitResult.GetActor();
  22.         UE_LOG(LogTemp, Display, TEXT("Hit actor: %s"), *HitActor->GetActorNameOrLabel());
  23.     }
  24.     else
  25.     {
  26.         UE_LOG(LogTemp, Display, TEXT("No actor hit"));
  27.     }
  28. }

我们之前使用过 DrawDebugLine() 函数。DrawDebugSphere() 和它一样也是绘制调试函数,不过画的是球体。其函数定义如下:

  • void DrawDebugSphere(const UWorld* InWorld,
  •     FVector const& Center,
  •     float Radius,
  •     int32 Segments,
  •     FColor const& Color,
  •     bool bPersistentLines = false,
  •     float LifeTime = -1.f,
  •     uint8 DepthPriority = 0,
  •     float Thickness = 0.f);

此处我们把 bPersistentLines 参数设置为 true,方便调试查看。

运行程序,可视化的结果如图 1 所示,绿色是 FHitResult.Location,红色是 FHitResult.ImpactPoint。可以看到 ImpactPoint 是我们平常理解的碰撞点;而此处 Location 是发生碰撞时,扫掠球体中心点所在的地方,因为球体是有体积的。

图1 可视化