Sweep trace is not returning correct hit locations

Since the radial damage functions don’t have any effect in my game, I have been trying to implement my own version, but I am running into a problem when a perform a sweep. More specifically, when I run a multi sweep, I do get a full array of all the objects that were within the sweep radius, but all the objects impact points return the same value of the first object in the array. This majorly complicates things, as I need the impact point of all the separate objects within the trace to calculate damage falloff and that doesn’t really work when all I get is the same location for every single object in the trace.

I have tried this both with c++ and blueprint, in both version 4.14 and 4.15, and they all have the same effect, so I am submitting this as a bug.

void UPA_BlueprintFunctionLibrary::DealRadialDamageWithFalloff(float MinDamage, float MaxDamage, float MinRadius, float MaxRadius, float FallOff, FVector DamageOrigin, TSubclassOf<UDamageType> DamageType, class AController* InstigatedBy, AActor* DamageCauser, FHitResult HitResult, bool bIgnoreSelf, bool bDebug)
{
	if (MaxRadius != 0.f && DamageCauser)
	{
		FCollisionQueryParams initialTraceParams(TEXT(""), false, DamageCauser);
		initialTraceParams.bTraceAsyncScene = true;
		initialTraceParams.bTraceComplex = true;
		initialTraceParams.bReturnPhysicalMaterial = false;

		TArray<FHitResult> HitResults;

		FCollisionObjectQueryParams ObjectParams;
		ObjectParams.AddObjectTypesToQuery(ECC_Pawn);
		ObjectParams.AddObjectTypesToQuery(ECC_WorldStatic);
		ObjectParams.AddObjectTypesToQuery(ECC_WorldDynamic);
		ObjectParams.AddObjectTypesToQuery(ECC_Visibility);

		bool bHit = DamageCauser->GetWorld()->SweepMultiByObjectType(HitResults, 
			DamageOrigin, 
			FVector(DamageOrigin.X, DamageOrigin.Y, DamageOrigin.Z + 1.f), 
			FQuat::Identity, 
			ObjectParams, 
			FCollisionShape::MakeSphere(MaxRadius), 
			initialTraceParams);

		if (bDebug)
		{
			DrawDebugSphere(DamageCauser->GetWorld(), DamageOrigin, MinRadius, 12, FColor::Red, false, 2.f);
			DrawDebugSphere(DamageCauser->GetWorld(), DamageOrigin, MaxRadius, 12, FColor::Blue, false, 2.f);
		}

		if (bHit)
		{
			for (int32 HitIndex = 0; HitIndex < HitResults.Num(); HitIndex++)
			{
				if (bIgnoreSelf && HitResults[HitIndex].GetActor() == DamageCauser)
				{
					//do nothing
				}
				else
				{
					float ActorDistance = (DamageOrigin - HitResults[HitIndex].ImpactPoint).Size();

					float const ValidatedMinRadius = FMath::Max(0.f, MinRadius);
					float const ValidatedMaxRadius = FMath::Max(MaxRadius, ValidatedMinRadius);
					float const ValidatedDistance = FMath::Max(0.f, ActorDistance);

					// calculate the interpolated scale
					float DamageScale;

					if (ValidatedDistance >= ValidatedMaxRadius)
					{
						DamageScale = 0.f;
					}
					else if ((FallOff == 0.f) || (ValidatedDistance <= ValidatedMinRadius))
					{
						DamageScale = 1.f;
					}
					else
					{
						DamageScale = 1.f - ((ValidatedDistance - ValidatedMinRadius) / (ValidatedMaxRadius - ValidatedMinRadius));
						DamageScale = FMath::Pow(DamageScale, FallOff);
					}

					float ActualDamage = FMath::Lerp(MinDamage, MaxDamage, FMath::Max(0.f, DamageScale));


					DrawDebugPoint(DamageCauser->GetWorld(), HitResults[HitIndex].ImpactPoint, 20.f, FColor::Green, false, 2.f);

					if (ActualDamage > 0.f)
						UGameplayStatics::ApplyDamage(HitResults[HitIndex].GetActor(), ActualDamage, InstigatedBy, DamageCauser, DamageType);
				}
			}
		}
	}
}

Hey -

If I understand correctly, your hit location and impact point are returning the same value? Can you check if bStartPenetrating is true? Based on your description, it sounds like you’re having the same issue that is described here: Sweep collision doesn't work if the start loc and end loc both collide with the same surface - World Creation - Epic Developer Community Forums . Let me know if the information from Zak M, including the use of UBodySetup::GetClosestPointAndNormal(), helps. If not, can you provide the full setup steps to reproduce the behavior you’re seeing? Either the blueprint setup used to test or the full code / class structure that the function you provided is used in.

My apologies for the late reply.

I doublechecked, and bStartPenetrating is returning true for every object in the array.

I have tried implementing UBodySetup::GetClosestPointAndNormal(), but the function is giving me a warning when being called (“LogPhysics:Warning: GetClosestPointAndNormalImpl ClosestDist for BodySetup BodySetup /Game/Geometry/Meshes/1M_Cube.1M_Cube:BodySetup_5 is coming back as FLT_MAX.”). This is how I implemented it;

FVector NewImpactPoint, NewImpactNormal;
    
if (UBodySetup* BodySetup = HitResults[HitIndex].GetComponent()->GetBodySetup())
{
BodySetup->GetClosestPointAndNormal(DamageOrigin, HitResults[HitIndex].GetComponent()->GetComponentTransform(), NewImpactPoint, NewImpactNormal);

DrawDebugPoint(DamageCauser->GetWorld(), NewImpactPoint, 20.f, FColor::Blue, false, 4.f);	
}

As for how the UPA_BlueprintFunctionLibrary is implemented in code (I removed the unnecessary bits, since the reply was getting too big);

if (BlahBlah)
{
	FCollisionObjectQueryParams ObjectParams;
	ObjectParams.AddObjectTypesToQuery(ECollisionChannel::ECC_Visibility);
	ObjectParams.AddObjectTypesToQuery(ECollisionChannel::ECC_Pawn);
	ObjectParams.AddObjectTypesToQuery(ECollisionChannel::ECC_WorldStatic);
	ObjectParams.AddObjectTypesToQuery(ECollisionChannel::ECC_WorldDynamic);

	FCollisionQueryParams TraceParams(TEXT(""), false, ToolOwner);
	TraceParams.bTraceAsyncScene = true;
	TraceParams.bTraceComplex = true;
	TraceParams.bReturnPhysicalMaterial = true;

	FVector MuzzleLoc = GetSocketLocation(MuzzleSocket);
	FRotator MuzzleRot = GetSocketRotation(MuzzleSocket);
	FVector TargetLoc = MuzzleLoc + (FRotationMatrix(MuzzleRot).GetScaledAxis(EAxis::X) * Range);
	TargetLoc.X = ToolOwner->GetActorLocation().X;

	FHitResult TraceHit(ForceInit);

	bool bHit = GetWorld()->LineTraceSingleByObjectType(TraceHit, MuzzleLoc, TargetLoc, ObjectParams, TraceParams);

	if (bHit)
	{
		UPA_BlueprintFunctionLibrary::DealRadialDamageWithFalloff(0.f,
			Damage,
			CurrentFlintlockBullet.InnerRadius,
			CurrentFlintlockBullet.OuterRadius,
			CurrentFlintlockBullet.FallOff,
			TraceHit.ImpactPoint,
			CurrentFlintlockBullet.DamageType,
			ToolOwner->GetController(),
			ToolOwner,
			TraceHit,
			false,
			true
		);
	}
}

If you’re still having issues with the values being returned, can you provide the blueprint setup you tested with to help ensure my local test matches the results you’re seeing.

This is how I tested the blueprint version (though I should mention that I really need my main implementation to be in C++, rather than blueprint). Something curious I found is that no matter where the trace starts, the Initial Overlap bool always returns true, even when the starting point of the trace is not even remotely close to any mesh.

While it’s probably clear from the screenshot, I should also mention that the ignored actors array is empty.

Hey -

Thank you for the explaination. Using your setup I was able to determine that the issue is a combination of the size of the sphere and how the sphere is being drawn. When the sphere is large enough and/or the object that is casting the sphere (the start location) is close enough to what is being hit, the location returned for that hit is the location that was used as the starting point. I have reported this behavior here: Unreal Engine Issues and Bug Tracker (UE-42934) .

Cheers