Ah, sorry, I thought that the array would return hits for each time a ray hit a mesh. It looks like it only returns a hit for every actor it hits, and only on the outside of a mesh. Bummer.
This is a pretty tough problem!
There are a couple ways I can think of doing this:
- Create an inner mesh with normals facing inward and fire two line traces. The first one hits the mesh normally and the second starts from the impact point (with a slight offset to not hit the same point) and tries to hit the inner mesh as the line trace is exiting the shape
- If you don’t want to use an inner mesh, you can fire three line traces. The first line trace will hit the outside of the mesh, the second will hit the third point on your mesh (in your second diagram), then fire a third line trace from the second impact point in the opposite direction to get the second point (the actual exit).
I personally don’t like the first approach, so I tried coding up a function for the second one.
void ATestBullet::TraceBullet(FVector Start, FVector End)
{
// Fire first line trace
FHitResult HitResult;
FCollisionQueryParams CollisionParams;
CollisionParams.bTraceComplex = true;
AActor* LastHitActor = nullptr;
GetWorld()->LineTraceSingleByChannel(HitResult, Start, End, ECC_Visibility, CollisionParams);
AActor* HitActor = HitResult.GetActor();
if (HitActor != nullptr && LastHitActor == nullptr)
{
LastHitActor = HitActor;
}
// Show it
DrawDebugLine(GetWorld(),Start,HitResult.ImpactPoint, FColor::Red, true, 5.0, 0, 2.0);
UE_LOG(LogTemp, Warning, TEXT("Hit point: %s"), *HitResult.ImpactPoint.ToString());
// If we hit something, make another line trace
if (HitResult.bBlockingHit)
{
FVector newStart = HitResult.ImpactPoint - HitResult.ImpactNormal;
GetWorld()->LineTraceSingleByChannel(HitResult, newStart, End, ECC_Visibility, CollisionParams);
// Show it
DrawDebugLine(GetWorld(), newStart, HitResult.ImpactPoint, FColor::Green, true, 5.0, 0, 2.0);
UE_LOG(LogTemp, Warning, TEXT("Second hit point: %s"), *HitResult.ImpactPoint.ToString());
// If we have hit the same mesh, fire a third line trace backwards
if (HitResult.bBlockingHit)
{
auto secondHitActor = HitResult.GetActor();
if (secondHitActor != nullptr && secondHitActor == LastHitActor)
{
FVector finalStart = HitResult.ImpactPoint + HitResult.ImpactNormal;
GetWorld()->LineTraceSingleByChannel(HitResult, finalStart, Start, ECC_Visibility, CollisionParams);
// Show it, but render it a bit lower so we can see it
DrawDebugLine(GetWorld(), finalStart + FVector(0, 0, -2.f), HitResult.ImpactPoint + FVector(0, 0, -2.f), FColor::Blue, true, 5.0, 0, 2.0);
UE_LOG(LogTemp, Warning, TEXT("Third hit point: %s"), *HitResult.ImpactPoint.ToString());
}
}
}
}
I tested this with a torus, so it should work.