通过 Physics Handle 抓取物体
在之前的文章 《FindComponentByClass()》 中,我们已经添加了 Physics Handle 组件,并且能通过代码获取到它。
在这篇文章中,我们学习如何使用 Physics Handle 抓取物体。我们先了解 Physics Handle 的两个成员函数:GrabComponentAtLocationWithRotation() 和 SetTargetLocationAndRotation()。
GrabComponentAtLocationWithRotation() 函数抓取一个物理组件,并将其定位到指定的位置,同时可以附带旋转。其函数定义如下:
- void UPhysicsHandleComponent::GrabComponentAtLocationWithRotation(
- class UPrimitiveComponent* Component,
- FName InBoneName,
- FVector GrabLocation,
- FRotator Rotation);
Component 参数指定要抓取的物理组件;InBoneName 参数指定附带的骨骼组件的名称,没有的话指定为 NAME_None;GrabLocation 参数指定要被抓取的位置;Rotation 参数指定抓取时物体的旋转角度。
SetTargetLocationAndRotation() 函数用于在物体已经被抓取之后,实时调整物体的位置和旋转。其函数定义如下:
- void UPhysicsHandleComponent::SetTargetLocationAndRotation(
- FVector NewLocation,
- FRotator NewRotation);
以上的 Rotation 参数含义都不太清楚。
了解好函数的使用之后,我们开始实现代码。如代码清单 1 所示,我们先调用 GrabComponentAtLocationWithRotation():抓取的位置是扫掠检测发生碰撞的位置。
- void UGrabber::Grab()
- {
- if (PhysicsHandle != NULL)
- {
- FVector Start = GetComponentLocation();
- FVector End = Start + GetForwardVector() * MaxGrabDistance;
- FCollisionShape Sphere = FCollisionShape::MakeSphere(GrabRadius);
- FHitResult HitResult;
- bool HasHit = GetWorld()->SweepSingleByChannel(HitResult,
- Start, End,
- FQuat::Identity,
- ECC_GameTraceChannel2,
- Sphere);
- if (HasHit)
- {
- PhysicsHandle->GrabComponentAtLocationWithRotation(
- HitResult.GetComponent(),
- NAME_None,
- HitResult.ImpactPoint,
- GetComponentRotation()
- );
- }
- }
- }
接着如代码清单 2 所示,我们在场景组件的 TickComponent 函数中,不断调用 SetTargetLocationAndRotation() 更新抓取物体的位置和旋转。更新的位置在当前视角位置向前指定的距离(通过 HoldDistance 变量指定)。
- // Called every frame
- void UGrabber::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
- {
- Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
- if (PhysicsHandle != NULL)
- {
- FVector TargetLocation = GetComponentLocation() + GetForwardVector() * HoldDistance;
- PhysicsHandle->SetTargetLocationAndRotation(
- TargetLocation, GetComponentRotation()
- );
- }
- }
以上的 Rotation 参数含义都不太清楚。目前都设置为场景组件当前的旋转,看着效果是可以的。
在运行程序之前,我们需要把待抓取物体的 移动性 属性设置为 可移动;并把 模拟物理 属性勾选启动。否则物体无法被抓取。
不要忘记设置。无法抓取也没有什么日志提示。
最终的效果如下方视频所示,可以看到抓取的移动过程是满足物理效果的,会被墙等物体挡住。