Created
October 25, 2025 05:06
-
-
Save windopper/063de331667cae326587a5909aff9bdd to your computer and use it in GitHub Desktop.
ue5 cone-shaped trace multi
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 characters
| // 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); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment