Created
October 25, 2025 05:06
-
-
Save windopper/063de331667cae326587a5909aff9bdd to your computer and use it in GitHub Desktop.
Revisions
-
windopper created this gist
Oct 25, 2025 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,107 @@ // reference https://dev.epicgames.com/community/learning/tutorials/pBv0/how-to-perform-cone-shaped-traces-in-unreal-engine // CollisionLibrary.h public: UFUNCTION(BlueprintCallable, Category = "Collision", Meta = (bIgnoreSelf = "true", WorldContext = "WorldContextObject", AutoCreateRefTerm = "ActorsToIgnore", DisplayName = "Multi Cone Trace By Channel", AdvancedDisplay = "TraceColor, TraceHitColor, DrawTime", Keywords = "sweep")) static bool ConeTraceMulti(const UObject* WorldContextObject, const FVector Start, const FRotator Direction, float ConeHeight, float ConeHalfAngle, ETraceTypeQuery TraceChannel, bool bTraceComplex, const TArray<AActor*>& ActorsToIgnore, EDrawDebugTrace::Type DrawDebugType, TArray<FHitResult>& OutHits, bool bIgnoreSelf, FLinearColor TraceColor = FLinearColor::Red, FLinearColor TraceHitColor = FLinearColor::Green, float DrawTime = 5.0f); // CollisionLibrary.cpp bool UCollisionLibrary::ConeTraceMulti( const UObject* WorldContextObject, const FVector Start, const FRotator Direction, float ConeHeight, float ConeHalfAngle, ETraceTypeQuery TraceChannel, bool bTraceComplex, const TArray<AActor*>& ActorsToIgnore, EDrawDebugTrace::Type DrawDebugType, TArray<FHitResult>& OutHits, FLinearColor TraceColor, FLinearColor TraceHitColor, float DrawTime) { OutHits.Reset(); ECollisionChannel CollisionChannel = UEngineTypes::ConvertToCollisionChannel(TraceChannel); FCollisionQueryParams Params(SCENE_QUERY_STAT(ConeTraceMulti), bTraceComplex); Params.bReturnPhysicalMaterial = true; Params.AddIgnoredActors(ActorsToIgnore); UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull); if (!World) { return false; } TArray<FHitResult> TempHitResults; const FVector End = Start + (Direction.Vector() * ConeHeight); const double ConeHalfAngleRad = FMath::DegreesToRadians(ConeHalfAngle); // r = h * tan(theta / 2) const double ConeBaseRadius = ConeHeight * tan(ConeHalfAngleRad); const FCollisionShape SphereSweep = FCollisionShape::MakeSphere(ConeBaseRadius); // Perform a sweep encompassing an imaginary cone. World->SweepMultiByChannel(TempHitResults, Start, End, Direction.Quaternion(), CollisionChannel, SphereSweep, Params); // Filter for hits that would be inside the cone. for (FHitResult& HitResult : TempHitResults) { const FVector HitDirection = (HitResult.ImpactPoint - Start).GetSafeNormal(); const double Dot = FVector::DotProduct(Direction.Vector(), HitDirection); // theta = arccos((A • B) / (|A|*|B|)). |A|*|B| = 1 because A and B are unit vectors. const double DeltaAngle = FMath::Acos(Dot); // Hit is outside the angle of the cone. if (DeltaAngle > ConeHalfAngleRad) { continue; } const double Distance = (HitResult.ImpactPoint - Start).Length(); // Hypotenuse = adjacent / cos(theta) const double LengthAtAngle = ConeHeight / cos(DeltaAngle); // Hit is beyond the cone. This can happen because we sweep with spheres, which results in a cap at the end of the sweep. if (Distance > LengthAtAngle) { continue; } OutHits.Add(HitResult); } #if ENABLE_DRAW_DEBUG if (DrawDebugType != EDrawDebugTrace::None) { // Cone trace. const double ConeSlantHeight = FMath::Sqrt((ConeBaseRadius * ConeBaseRadius) + (ConeHeight * ConeHeight)); // s = sqrt(r^2 + h^2) DrawDebugCone(World, Start, Direction.Vector(), ConeSlantHeight, ConeHalfAngleRad, ConeHalfAngleRad, 32, TraceColor.ToFColor(true), (DrawDebugType == EDrawDebugTrace::Persistent), DrawTime); // Uncomment to see the trace we're actually performing. // DrawDebugSweptSphere(World, Start, End, ConeBaseRadius, TraceColor.ToFColor(true), (DrawDebugType == EDrawDebugTrace::Persistent), DrawTime); // Successful hits. for (const FHitResult& Hit : OutHits) { DrawDebugLineTraceSingle(World, Hit.TraceStart, Hit.ImpactPoint, DrawDebugType, true, Hit, TraceHitColor, TraceHitColor, DrawTime); } // Uncomment to see hits from the sphere sweep that were filtered out. // for (const FHitResult& Hit : TempHitResults) // { // if (!OutHits.ContainsByPredicate([Hit](const FHitResult& Other) // { // return (Hit.GetActor() == Other.GetActor()) && // (Hit.ImpactPoint == Other.ImpactPoint) && // (Hit.ImpactNormal == Other.ImpactNormal); // })) // { // DrawDebugLineTraceSingle(World, Hit.TraceStart, Hit.ImpactPoint, DrawDebugType, false, Hit, FColor::Red, FColor::Red, DrawTime); // } // } } #endif // ENABLE_DRAW_DEBUG return (OutHits.Num() > 0); }