SafeMoveUpdatedComponent Reporting An Impact With Itself

I’ve been having some strange issues with my custom Pawn and MovementComponent lately. Since I have seemingly hit a dead end when it comes to understanding this issue, I thought I would ask more about it here. Specifically, it seems that SafeMoveUpdatedComponent is registering some strange HitResults which is then causing the SlideAlongSurface function to push my object through geometry – Even when no collision was supposed to be made.

Here’s an image illustrating the issue.

The yellow vector represents the TraceStart to TraceEnd as reported by the FHitResult that SafeMoveUpdatedComponent fills in. In other words, this should be the attempted movement. The red line represents the impact point and also the impact normal. Lastly, the blue line illustrates the correction made by SlideAlongSurface, which in this case pushes the player through the floor below him.

I’m not positive on this, but it looks as though the SafeMoveUpdatedComponent function is registering a hit result either with itself (the updated component that it is trying to move) or it is colliding with a capsule that I make when checking for the floor below the pawn. Some sample code is below.

void UShellPawnMovement::ProcessAction(FPlayerAction Action)
{
	//Basic PlayerAction -> New Velocity Implementation. 

	FVector NewVelocity = Velocity;
	FRotator TargetRotation = UpdatedComponent->GetComponentRotation();

	// Get A New Target Rotation Using Body Turning Rules...
	TargetRotation = TurnBody(Action.ViewRotation, TargetRotation, Action.DesiredMovementDirection, 50.0f);

	//Basic state handling. Either flying or walking (when on ground)
	if(GroundInfo.TouchingGround)
	{
		NewVelocity = CalculateWalkingVelocity(Action.ViewRotation, NewVelocity, Action.DesiredMovementDirection, GroundInfo.GroundNormal );
	}
	else
	{
		NewVelocity = CalculateFlyingVelocity(Action.ViewRotation, NewVelocity, Action.DesiredMovementDirection);
	}

	//Try to move in the desired direction. 
	FHitResult HitResult(1.0f);
	NewVelocity = TryToMove(NewVelocity, TargetRotation, Action, HitResult);
	Velocity = NewVelocity;
	UpdateComponentVelocity();

	//Find the ground at the end of the frame. Either sampled from an impact, or sampled using capsule-casting.
	CheckForGround(UpdatedComponent->GetComponentLocation(), GroundInfo, &HitResult);
}

....

void UShellPawnMovement::CheckForGround(const FVector& BodyLocation, FGroundCollisionData& outGroundData, FHitResult* SampleFromCollision )
{
	bool AlreadyGrounded = outGroundData.TouchingGround;

	//Reset ground data.
	outGroundData.TouchingGround = false;

	//If collision is disabled or not attached to appropriate pawn
	if (!UpdatedComponent->IsCollisionEnabled() || ShellPawn == NULL)
	{
		return;
	}

	//Get the "down" relative to the actor rotation. 
	FVector BodyDownardDirection = UpdatedComponent->GetComponentRotation().RotateVector((FVector::UpVector * -1.f));

	//If we have an existing collision to sample from, check to see if the hit is elegible as a grounded surface.
	if (SampleFromCollision != NULL && SampleFromCollision->bBlockingHit )
	{
		if ( IsValidGround(SampleFromCollision->Normal, BodyDownardDirection) )
		{
			
			outGroundData.GroundNormal = SampleFromCollision->Normal.GetSafeNormal();
			outGroundData.TouchingGround = true;

			//Early Return if our hit is also considered a grounded surface.
			return;
		}
	}

	float ScaledRadius, ScaledHalfHeight;
	ShellPawn->GetPawnCapsule()->GetScaledCapsuleSize(ScaledRadius, ScaledHalfHeight);
	
	float ShrinkedRadius, ShrinkedHalfHeight;
	ShrinkedRadius = ScaledRadius * 0.998f;
	ShrinkedHalfHeight = ScaledHalfHeight * 0.998f;

	float CastDistance = MAX_FLOOR_DISTANCE + (ScaledHalfHeight - ShrinkedHalfHeight);

	//Initialize Collision Data
	FCollisionShape CollisionCapsule = FCollisionShape::MakeCapsule( ShrinkedRadius, ShrinkedHalfHeight );
	
	ECollisionChannel CollisionChannel = UpdatedComponent->GetCollisionObjectType();
	FCollisionQueryParams QueryParams(NAME_None, false, ShellPawn);
	QueryParams.TraceTag = FName(TEXT("ComputeFloorDistSweep"));
	FCollisionResponseParams ResponseParams;
	
	// As seen in CharacterMovementComponent.cpp, call before casting. 
	InitCollisionParams(QueryParams, ResponseParams);

	FHitResult Hit = FHitResult(1.f);

	UWorld * World = GetWorld();
	World->SweepSingleByChannel(Hit, BodyLocation, BodyLocation + (BodyDownardDirection.SafeNormal() * CastDistance), UpdatedComponent->GetComponentRotation().Quaternion(), 
		CollisionChannel, CollisionCapsule, QueryParams, ResponseParams);

	
	
	if (Hit.bBlockingHit)
	{
		if (IsValidGround(Hit.Normal, BodyDownardDirection))
		{
			outGroundData.TouchingGround = true;
			outGroundData.GroundNormal = Hit.Normal;
		}
		else
		{
			//Further shrink capsule and try again.

			CollisionCapsule.SetCapsule(ShrinkedRadius * 0.5f, ShrinkedHalfHeight * 0.5f);
			CastDistance = MAX_FLOOR_DISTANCE + (ScaledHalfHeight - (ShrinkedRadius * 0.5f));
			World->SweepSingleByChannel(Hit, BodyLocation, BodyLocation + (BodyDownardDirection.SafeNormal() * CastDistance), UpdatedComponent->GetComponentRotation().Quaternion(),
				CollisionChannel, CollisionCapsule, QueryParams, ResponseParams);

			Hit.Reset(1.0f);

			if (Hit.IsValidBlockingHit() && IsValidGround(Hit.Normal, BodyDownardDirection))
			{
				outGroundData.TouchingGround = true;
				outGroundData.GroundNormal;
			}

		}
	}
}

....

FVector UShellPawnMovement::TryToMove(const FVector& CurrentVelocity, const FRotator& TargetRotation, const FPlayerAction& PlayerInputs, FHitResult& result)
{
	FVector OldPosition = GetPawnOwner()->GetActorLocation();

	//Get the difference between 
	FRotator DeltaRotation = TargetRotation - UpdatedComponent->GetComponentRotation();
	DeltaRotation = DeltaRotation.GetNormalized();

	//Linear Turn Speed Calculation. 
	float RadianTurnSpeed = FMath::DegreesToRadians(TurnSpeed * PlayerInputs.deltaTime)  ;
	FQuat LinearTurnSpeed = DeltaRotation.Quaternion();
	LinearTurnSpeed.W = RadianTurnSpeed;

	//Time Step Modified Velocity
	FVector AdjustedVelocity = CurrentVelocity * PlayerInputs.deltaTime;
	
	//Rotation Delta adjusted based on whether you're moving or not. 
	FRotator AdjustedRotation;
	if (!PlayerInputs.DesiredMovementDirection.IsNearlyZero())
	{
		AdjustedRotation = DeltaRotation;
	}
	else
	{
		AdjustedRotation = LinearTurnSpeed.Rotator();
	}

	SafeMoveUpdatedComponent( AdjustedVelocity, (UpdatedComponent->GetComponentRotation() + AdjustedRotation), true, result);

	if (result.Time < 1.0f && result.bBlockingHit )
	{
		DrawDebugLine(GetWorld(),result.ImpactPoint, result.ImpactPoint + result.ImpactNormal * 10.f, FColor::Red, true, 5.0f, 0, 1.f);
		DrawDebugLine(GetWorld(), result.TraceStart, result.TraceEnd, FColor::Yellow, true, 5.0f, 0, 1.f);
		
		FHitResult SlideResult = FHitResult(result);
		HandleImpact(SlideResult, PlayerInputs.deltaTime, AdjustedVelocity);

		FVector PosBeforeSlide = UpdatedComponent->GetComponentLocation();
		
		SlideAlongSurface(AdjustedVelocity, 1.f - SlideResult.Time, SlideResult.Normal, SlideResult);
		DrawDebugLine(GetWorld(), PosBeforeSlide, UpdatedComponent->GetComponentLocation(), FColor::Blue, true, 5.0f, 0, 1.f);

		//Calculate and resolve penetration if slide along surface pushes an object through another surface. 
		if (SlideResult.Time < 1.f )
		{
			FVector Adjustment = GetPenetrationAdjustment(SlideResult);
			ResolvePenetration( Adjustment, SlideResult, UpdatedComponent->GetComponentRotation() + AdjustedRotation );

			result = SlideResult;
		}
	}

	//Returns velocity of the movement attempt. 
	return (GetPawnOwner()->GetActorLocation() - OldPosition) / PlayerInputs.deltaTime;
}

Why would the SafeMoveUpdatedComponent function return a HitResult that looks like that when no collision should be made? Is the UpdatedComponent somehow colliding with itself? Or perhaps there’s something wrong with my implementation of my TryToMove function?

Any help would be great!