Physics Handle 释放物体
在上一篇文章中,我们已经通过 Physics Handle 实现了物体的抓取。在这篇文章中,我们会实现物体的释放。
在实现之前,我们先介绍一种特殊情况。在 Unreal Engine 中,如果一个刚体静止久了,为了节省性能,它会被暂停计算,进入“沉睡”状态。为了能继续进行运动模拟,需要调用该物体的 UPrimitiveComponent::WakeAllRigidBodies() 函数,使其重新参与物理计算,即“唤醒”它。
如代码清单 1 所示,我们完善了之前写的抓取逻辑。在抓取物体之前,我们先唤醒它,以免抓取无效。
自己本地环境没有复现出抓取不了的情况。不过增加唤醒逻辑肯定是好的。
- 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)
- {
- UPrimitiveComponent* HitComponent = HitResult.GetComponent();
- HitComponent->WakeAllRigidBodies();
- PhysicsHandle->GrabComponentAtLocationWithRotation(
- HitComponent,
- NAME_None,
- HitResult.ImpactPoint,
- GetComponentRotation()
- );
- }
- }
- }
接着,我们实现释放逻辑。在之前的文章 《输入操作映射》 中,我们已经定义了动作逻辑:鼠标左键按下抓取物体,左键弹起释放物体。
如代码清单 2 所示,我们首先调用 UPhysicsHandleComponent::GetGrabbedComponent() 函数,判断是否有抓取的物体。如果有抓取物体的话,我们还是先唤醒它,然后调用 UPhysicsHandleComponent::ReleaseComponent() 释放它。
- void UGrabber::Release()
- {
- UE_LOG(LogTemp, Display, TEXT("Released grabber"));
- if (PhysicsHandle != NULL)
- {
- UPrimitiveComponent* GrabbedComponent = PhysicsHandle->GetGrabbedComponent();
- if (GrabbedComponent != NULL)
- {
- GrabbedComponent->WakeAllRigidBodies();
- PhysicsHandle->ReleaseComponent();
- }
- }
- }
我们已经知道有函数可以判断是否有抓取物体。如代码清单 3 所示,在没有抓取物体的时候,我们就不需要更新抓取物体的位置和旋转信息。
- // Called every frame
- void UGrabber::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
- {
- Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
- if (PhysicsHandle != NULL)
- {
- if (PhysicsHandle->GetGrabbedComponent() != NULL)
- {
- FVector TargetLocation = GetComponentLocation() + GetForwardVector() * HoldDistance;
- PhysicsHandle->SetTargetLocationAndRotation(
- TargetLocation, GetComponentRotation()
- );
- }
- }
- }
此时我们可以运行程序进行查看了。可以发现还有需要完善的点:如果我们抓着物体靠着墙,会发现物体的行为非常奇怪,这是因为抓取的物体和我们控制的角色也发生了碰撞。
为了修复这个问题,如图 1 所示,我们来到雕塑组件的碰撞预设选项。在文章 《射线追踪与扫掠检测》 中,我们已经创建了一个自定义的碰撞预设。修改其下和 Pawn 类型物体的响应方式为 重叠。

之前为了防止雕塑底部看着镂空,在其底部加了一块粘土。为了方便,我们将这块粘土的碰撞预设直接修改为内置提供的 NoCollision。
最终运行的效果如下方视频所示,我们按下左键抓取物体;然后按住左键不弹起,可以让物体跟随移动;最后弹起左键可以释放物体。