Download

Sweep Not Hitting Mesh Where it Should

Sweeps seem to be very acting inconsistently. It has caused me quite a bit of headache thinking things are wrong on my end when it actually seems collision is not acting like it’s supposed to UNLESS I turn on “double sided geometry” for the mesh. I think it would be really bad if I had to turn on double sided geometry for every single one of the meshes that need to accurately get hit by sweeps. I’ve included several pictures to help illustrate what is going on.

The red capsule in the trace indicates a blocking hit.

Using engine content SM_Template_Map_Floor.SM_Template_Map_Floor’ Lit:

Using engine content SM_Template_Map_Floor.SM_Template_Map_Floor’ Wireframe:

Using very basic rock mesh Lit:

Using very basic rock mesh Wireframe:

Rock mesh collision:

This is what I would like to happen. Once I turn on “double sided geometry”, the trace hits exactly where it’s supposed to:

Here is the tracing function:


/**
*  Perform single sweeps along the current MovementSplineComponent for a blocking hit. Starts tracing at CurrentSplineDistance, increments by the Wall Slide Collision X Extent / 2. Ends at CurrentSplineDistance+MaxDistance, CurrentSplineDistance-MaxDistance, spline size and distance = 0. All traces are done using Character World Z.
*  @param FirstHit - ByRef Hit Result to be stored upon trace success
*  @param ClosestSafeDistance - ByRef distance along the spline before the collision occured. Set to -1 if there is no safe distance.
*  @param MaxDistance - Maximum distance to trace forward or backward depending on if SweepForward is true or false
*  @param SweepForward - True to sweep forward along the spline, false to sweep backward
*  @return True if there was a hit, false otherwise
*  
**/
bool SweepSplineCharacterMovementPath(FHitResult &FirstHit, float &ClosestSafeDistance, float MaxDistance, bool SweepForward)
{
    FVector TraceLocation;
    FCollisionShape TraceShape = FCollisionShape::MakeCapsule(GetCapsuleComponent()->GetScaledCapsuleRadius(), GetCapsuleComponent()->GetScaledCapsuleHalfHeight());
    FCollisionObjectQueryParams TraceParams;
    TraceParams.AddObjectTypesToQuery(ECollisionChannel::ECC_WorldDynamic);
    TraceParams.AddObjectTypesToQuery(ECollisionChannel::ECC_WorldStatic);
    TraceParams.RemoveObjectTypesToQuery(ECollisionChannel::ECC_Pawn);

    // sweep every half of the x extent of the wall slide collision box
    float SweepStep = WallSlideBoxCollision->GetScaledBoxExtent().X / 2.f;
    if (!SweepForward) SweepStep *= -1.f;

    ClosestSafeDistance = -1.f;
    bool bHasTracedLast = false;
    for (float i = CurrentSplineDistance; !bHasTracedLast; i += SweepStep)
    {
        if (i >= MovementSplineComponent->GetSplineLength())
        {
            bHasTracedLast = true;
            i = MovementSplineComponent->GetSplineLength();
        }
        else if (i <= 0.f)
        {
            bHasTracedLast = true;
            i = 0.f;
        }
        else if (i >= CurrentSplineDistance + MaxDistance)
        {
            bHasTracedLast = true;
            i = CurrentSplineDistance + MaxDistance;
        }
        else if (i <= CurrentSplineDistance - MaxDistance)
        {
            bHasTracedLast = true;
            i = CurrentSplineDistance - MaxDistance;
        }

        TraceLocation = MovementSplineComponent->GetLocationAtDistanceAlongSpline(i, ESplineCoordinateSpace::World);
        TraceLocation.Z = GetActorLocation().Z;

        bool bTraceHit = GetWorld()->SweepSingleByObjectType(FirstHit, TraceLocation, TraceLocation, FRotator(0.0f, 0.0f, 0.0f).Quaternion(), TraceParams, TraceShape);
        if (bTraceHit && FirstHit.bBlockingHit)
        {
            //trace has hit something
            DrawDebugCapsule(GetWorld(), TraceLocation, GetCapsuleComponent()->GetScaledCapsuleHalfHeight(), GetCapsuleComponent()->GetScaledCapsuleRadius(), FRotator(0.f, 0.f, 0.f).Quaternion(), FColor(255.f, 0.f, 0.f), false, -1.f, 0, 10.f);

            return true;
        }
        else {
            ClosestSafeDistance = i;
            DrawDebugCapsule(GetWorld(), TraceLocation, GetCapsuleComponent()->GetScaledCapsuleHalfHeight(), GetCapsuleComponent()->GetScaledCapsuleRadius(), FRotator(0.f, 0.f, 0.f).Quaternion(), FColor(0.f, 255.f, 0.f), false);
        }
    }

    return false;
}

Please let me know if you would like anything else from me to help diagnose this. The meshes are simple static mobility static mesh actors that have been scaled up in the level.

Thank you so much in advance

The forum wasn’t letting me edit in a new image. But I think this one perfectly demonstrates what is going on. Collision is visible, 2 really basic rock meshes, one collision occurs in the middle of the mesh (left), the other where it is intended (right).

Here is the same trace when rocks have double sided geometry enabled:

So I got it working without double sided geometry by making the the Start and End parameters different values in SweepSingleByObjectType(). I used the last trace location as start and the current trace location as the end.

This all being said… Do sweeps not work properly if I use the same location in the Sweep Start as the Sweep End? Does the function require two locations with distance between them? I often just want to check one location and not a range.

I remember having a similar issue long ago in UE3, and the solution was to switch the Start and End locations