Reproduce:
Create A as a parent has B & C as children, B is a SkeletalMesh and C is a static mesh and both movable.
Invoke A.AddMovementInput each tick to make A always moving
Move B to overlap C, then every frame B will fire BeginOverlap And EndOverlap with C。over and over.
Notice that the relative location between B and C is not changed and still overlap each other,so expected result IMO is that EndOverlap won’t be invoked.
After investigating the source code,I found that:
bool UPrimitiveComponent::ComponentOverlapMultiImpl(TArray<struct FOverlapResult>& OutOverlaps, const UWorld* World, const FVector& Pos, const FQuat& Quat, ECollisionChannel TestChannel, const struct FComponentQueryParams& Params, const struct FCollisionObjectQueryParams& ObjectQueryParams) const
{
FComponentQueryParams ParamsWithSelf = Params;
ParamsWithSelf.AddIgnoredComponent_LikelyDuplicatedRoot(this);
OutOverlaps.Reset();
return BodyInstance.OverlapMulti(OutOverlaps, World, /*pWorldToComponent=*/ nullptr, Pos, Quat, TestChannel, ParamsWithSelf, FCollisionResponseParams(GetCollisionResponseToChannels()), ObjectQueryParams);
}
bool USkeletalMeshComponent::ComponentOverlapMultiImpl(TArray<struct FOverlapResult>& OutOverlaps, const UWorld* World, const FVector& Pos, const FQuat& Quat, ECollisionChannel TestChannel, const struct FComponentQueryParams& Params, const struct FCollisionObjectQueryParams& ObjectQueryParams) const
{
OutOverlaps.Reset();
if (!Bodies.IsValidIndex(RootBodyData.BodyIndex))
{
return false;
}
const FTransform WorldToComponent(ComponentToWorld.Inverse());
const FCollisionResponseParams ResponseParams(GetCollisionResponseToChannels());
FComponentQueryParams ParamsWithSelf = Params;
ParamsWithSelf.AddIgnoredComponent(this);
bool bHaveBlockingHit = false;
for (const FBodyInstance* Body : Bodies)
{
checkSlow(Body);
if (Body->OverlapMulti(OutOverlaps, World, &WorldToComponent, Pos, Quat, TestChannel, ParamsWithSelf, ResponseParams, ObjectQueryParams))
{
bHaveBlockingHit = true;
}
}
return bHaveBlockingHit;
}
When calling Body->OverlapMulti,UPrimitiveComponent not use WorldToComponent like USkeletalMeshComponent does,which results at
bool FBodyInstance::OverlapMulti(TArray<struct FOverlapResult>& InOutOverlaps, const class UWorld* World, const FTransform* pWorldToComponent, const FVector& Pos, const FQuat& Quat, ECollisionChannel TestChannel, const struct FComponentQueryParams& Params, const struct FCollisionResponseParams& ResponseParams, const struct FCollisionObjectQueryParams& ObjectQueryParams) const
{
SCOPE_CYCLE_COUNTER(STAT_Collision_FBodyInstance_OverlapMulti);
if ( !IsValidBodyInstance() && (!WeldParent || !WeldParent->IsValidBodyInstance()))
{
UE_LOG(LogCollision, Log, TEXT("FBodyInstance::OverlapMulti : (%s) No physics data"), *GetBodyDebugName());
return false;
}
bool bHaveBlockingHit = false;
// Determine how to convert the local space of this body instance to the test space
const FTransform ComponentSpaceToTestSpace(Quat, Pos);
FTransform BodyInstanceSpaceToTestSpace;
if (pWorldToComponent)
{
const FTransform RootTM = WeldParent ? WeldParent->GetUnrealWorldTransform() : GetUnrealWorldTransform();
const FTransform LocalOffset = (*pWorldToComponent) * RootTM;
BodyInstanceSpaceToTestSpace = ComponentSpaceToTestSpace * LocalOffset;
}
else
{
BodyInstanceSpaceToTestSpace = ComponentSpaceToTestSpace;
}
They use the different BodyInstanceSpaceToTestSpace.
To be more clear, A.AddMovementInput will result in USceneComponent::MoveComponentImpl.
USceneComponent::MoveComponentImpl will first InternalSetWorldLocationAndRotation to set B and C’s component transform recursively. And then will update BodyInstance.SetBodyTransform to invoke PRigidDynamic->setKinematicTarget(PNewPose),which will result in a velocity that will move the body into the desired pose,that means at this frame, B&C’s PRigidDynamic are still not changed.
But when checking overlaping, C(UPrimitiveComponent) is using the component location(rendering location,have updated to new position), B(USkeletalMeshComponent) uses GetUnrealWorldTransform to get location, which takes physics body’s location, which remains at last frame.
So OverlapMulti failed and trigger a EndOverlap.
And WeldParent seems seems not working.
It’s a little complicated and I’m a new UE learner,hope it helps to make it better.