Trace Functions

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 ...

Updated over 4 years ago Edit Page Revisions

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.

Static Function Library Tutorial

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& 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 ActorsToIgnore;
for(TObjectIterator 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 Per-Bone Accurate Sword Collision System!

Summary

I hope you enjoy using my Trace functions, and have enjoyed my examples!

()