Trace Functions
Contents
Overview
Author: ( )
Dear Community,
I wrote these functions for myself to simplify the process of doing Traces in UE4 C++.
I hope you enjoy them!
I also demonstrate the use of the Distance propery of FHitResult, which I submitted as a pull request that Epic accepted as of 4.9.0
Static
If you are just starting out and want to test these functions you can remove the word static,
and put these in the .h file of your choosing.
I recommend that you make a Static Function Library for yourself though and put these functions there.
Overloads
Two of the functions below have the same name because one is an overload of the other.
This means that the two functions differ only by parameters, and the compiler will choose which one you mean based on the parameters that you supply.
Trace
static FORCEINLINE bool Trace(
UWorld* World,
AActor* ActorToIgnore,
const FVector& Start,
const FVector& End,
FHitResult& HitOut,
ECollisionChannel CollisionChannel = ECC_Pawn,
bool ReturnPhysMat = false
) {
if(!World)
{
return false;
}
FCollisionQueryParams TraceParams(FName(TEXT("VictoreCore Trace")), true, ActorToIgnore);
TraceParams.bTraceComplex = true;
//TraceParams.bTraceAsyncScene = true;
TraceParams.bReturnPhysicalMaterial = ReturnPhysMat;
//Ignore Actors
TraceParams.AddIgnoredActor(ActorToIgnore);
//Re-initialize hit info
HitOut = FHitResult(ForceInit);
//Trace!
World->LineTraceSingle(
HitOut, //result
Start, //start
End , //end
CollisionChannel, //collision channel
TraceParams
);
//Hit any Actor?
return (HitOut.GetActor() != NULL) ;
}
Example Usage
//In player controller class
//location the PC is focused on
const FVector Start = GetFocalLocation();
//256 units in facing direction of PC (256 units in front of the camera)
const FVector End = Start + GetControlRotation().Vector() * 256;
//The trace data is stored here
FHitResult HitData(ForceInit);
//If Trace Hits anything
if( UMyStaticFunctionLibrary::Trace(GetWorld(),GetPawn(),Start,End,HitData) )
{
//Print out the name of the traced actor
if(HitData.GetActor())
{
ClientMessage(HitData.GetActor()->GetName());
//Print out distance from start of trace to impact point
ClientMessage("Trace Distance: " + FString::SanitizeFloat(HitData.Distance));
}
}
FHitResult Now Returns Exact Distance
I made a pull request that Epic has accepted as of 4.9.0 to add a Distance parameter to FHitResult that lets you know the distance from the start of the trace to the ImpactPoint!
PhysX was always returning this information, I did not cause any extra performance impact by implementing this change to the Engine code.
Example Code
//In player controller class
//location the PC is focused on
const FVector Start = GetFocalLocation();
//2000 units in facing direction of PC (in front of the camera)
const FVector End = Start + GetControlRotation().Vector() * 2000;
//The trace data is stored here
FHitResult HitData(ForceInit);
//If Trace Hits anything (ignore the controlled pawn)
if( UMyStaticFunctionLibrary::Trace(GetWorld(),GetPawn(),Start,End,HitData) && HitData.GetActor() )
{
ClientMessage(HitData.GetActor()->GetName());
//Print out the distance from the trace start to the impact point!
ClientMessage("Distance from Trace Start to Impact: " + FString::SanitizeFloat(HitData.Distance));
}
Trace With Ignore Actors Array
//Trace with an Array of Actors to Ignore
// Ignore as many actors as you want!
static FORCEINLINE bool Trace(
UWorld* World,
TArray<AActor*>& ActorsToIgnore,
const FVector& Start,
const FVector& End,
FHitResult& HitOut,
ECollisionChannel CollisionChannel = ECC_Pawn,
bool ReturnPhysMat = false
) {
if(!World)
{
return false;
}
FCollisionQueryParams TraceParams(FName(TEXT("VictoryCore Trace")), true, ActorsToIgnore[0]);
TraceParams.bTraceComplex = true;
//TraceParams.bTraceAsyncScene = true;
TraceParams.bReturnPhysicalMaterial = ReturnPhysMat;
//Ignore Actors
TraceParams.AddIgnoredActors(ActorsToIgnore);
//Re-initialize hit info
HitOut = FHitResult(ForceInit);
World->LineTraceSingle(
HitOut, //result
Start, //start
End , //end
CollisionChannel, //collision channel
TraceParams
);
return (HitOut.GetActor() != NULL) ;
}
Example Usage
//In player controller class
//location the PC is focused on
const FVector Start = GetFocalLocation();
//256 units in facing direction of PC (256 units in front of the camera)
const FVector End = Start + GetControlRotation().Vector() * 256;
//The trace data is stored here
FHitResult HitData(ForceInit);
//Actors to Ignore
// Ignore all AFlowers
TArray<AActor*> ActorsToIgnore;
for(TObjectIterator<AFlower> It; It; ++It)
{
ActorsToIgnore.Add(*It);
}
//Ignore the player character too!
ActorsToIgnore.Add(GetPawn());
//If Trace Hits anything
if( UMyStaticFunctionLibrary::Trace(GetWorld(),GetPawn(),Start,End,ActorsToIgnore) )
{
//Print out the name of the traced actor
if(HitData.GetActor())
{
ClientMessage(HitData.GetActor()->GetName());
//Print out distance from start of trace to impact point
ClientMessage("Trace Distance: " + FString::SanitizeFloat(HitData.Distance));
}
}
Trace Component
//Component-level trace, do a trace against just 1 component
static FORCEINLINE bool TraceComponent(
UPrimitiveComponent* TheComp,
const FVector& Start,
const FVector& End,
FHitResult& HitOut
) {
if(!TheComp) return false;
if(!TheComp->IsValidLowLevel()) return false;
//~~~~~~~~~~~~~~~~~~~~~
FCollisionQueryParams TraceParams(FName(TEXT("VictoreCore Comp Trace")), true, NULL);
TraceParams.bTraceComplex = true;
//TraceParams.bTraceAsyncScene = true;
TraceParams.bReturnPhysicalMaterial = false;
//Ignore Actors
//TraceParams.AddIgnoredActors(ActorsToIgnore);
//Re-initialize hit info
HitOut = FHitResult(ForceInit);
return TheComp->LineTraceComponent(
HitOut,
Start,
End,
TraceParams
);
}
Example Usage
//In player controller class
ACharacter* CharacterToTrace = //set to some character
if(!CharacterToTrace) return;
if(!CharacterToTrace->IsValidLowLevel()) return;
//~~~~~~~~~~~~~~~~~~~~~~~~~~
//location the PC is focused on
const FVector Start = GetFocalLocation();
//256 units in facing direction of PC (256 units in front of the camera)
const FVector End = Start + GetControlRotation().Vector() * 256;
//The trace data is stored here
FHitResult HitData(ForceInit);
//If Trace Hits any part of the Mesh of the Character To Trace
if( UMyStaticFunctionLibrary::Trace(CharacterToTrace->GetMesh(),Start,End,HitData) )
{
//Print out the location of the impact on the Character's Mesh
ClientMessage(HitData.ImpactPoint.ToString());
//Print out distance from start of trace to impact point
ClientMessage("Trace Distance: " + FString::SanitizeFloat(HitData.Distance));
}
Why Use Trace Component?
Regular traces, like the first two, will hit collision capsules, which is not very precise if you are doing sword collision or per-bone accurate hits of any kind.
Once you've identified the actor that has been hit with a regular trace, you can use a TraceComponent on the hit Character's Mesh to get bone-level accurate traces!
Video of Component Level Tracing
I used component traces in my
<youtube> https://www.youtube.com/watch?v=725u770QM4Q
Summary
I hope you enjoy using my Trace functions, and have enjoyed my examples!
( )