Unreal provides a much simpler way to accomplish all of this

The last 3 or so videos have been centered around tracing a line from the crosshair to get a usable world location for our aiming mechanics. In a previous video, Ben suggested pseudo-coding a line-tracing function, but also left a “very advanced” challenge which was to make our line-tracing function actually fully functional. I accepted this challenge and discovered a function called GetHitResultAtScreenPosition() which accomplishes everything we need. It has a FHitResult out-parameter which we can use to get detailed information about our line trace, and allows us to filter trace queries by collision channel, object type, etc. just like the other line tracing methods do.

Here is my code for the crosshair line-tracing functions in question, which work flawlessly and even reports what we are looking at:

void ATankPlayerController::AimAtCrosshair()
{
    if (!GetControlledTank()) { return; }

    FVector HitLocation = FVector(0);
    FString ObjectHit = "Nothing";

    GetCrosshairTraceHit(ObjectHit, HitLocation);

    UE_LOG(LogTemp, Warning, TEXT("Targeting: %s  Location: %s"), *ObjectHit, *HitLocation.ToString());
}

bool ATankPlayerController::GetCrosshairTraceHit(FString &ObjectHit, FVector &HitLoc)
{
    // Viewport Size
    int32 ViewportSizeX, ViewportSizeY;
    GetViewportSize(ViewportSizeX, ViewportSizeY);

    bool bHit;
    FVector2D CrosshairPosition = FVector2D(ViewportSizeX / 2, ViewportSizeY / 3);
    FHitResult HitResult;

    bHit = GetHitResultAtScreenPosition(CrosshairPosition, ECollisionChannel::ECC_WorldStatic, false, HitResult);

    if (bHit)
    {
        HitLoc = HitResult.ImpactPoint;
        ObjectHit = HitResult.GetActor()->GetName();
    }

    // Draws a red line for debugging purposes
    DrawDebugLine(GetWorld(), HitResult.TraceStart, HitResult.TraceEnd, FColor::Red);

    return bHit;
}

11 Likes

2 Likes

Great job Ryan. This indeed works fine, and shows how many ways there are of doing the same thing.

Nice thanks.

Thank you for sharing this! Great job in accomplishing the same with much neater and easier to understand code!

nice. I didnt know we could shorten ampersands like that

also did you intentionally leave out the linetracerange?

Sorry it’s been awhile and you’ll have to jog my memory a little bit, what was this Iinetracerange? I’m sure I left it out intentionally but maybe I can explain why if I remember where that came from.

Also, what do you mean by shortening the ampersands?

ampersand, you wrote hitloc instead of hitlocation right? are they not the same thing or does hitloc mean something else?

line trace range lets us set how far the trace goes through the world. your function has no limit right? do you have an idea on how you would shorten the range in your code with the GetHitResultAtScreenPosition function?

HitLocation is different from HitLoc. HitLocation was the vector that I passed into the function as a reference, and HitLoc is the local name of that reference within the GetCrosshairTraceHit function. I gave them similar names so that it would be easy to see what goes where.

As for the trace range, GetHitResultAtScreenPosition doesn’t take any parameters like this, however it would be really simple to filter out objects beyond a certain range by using the Distance property of FHitResult after the trace is completed.

ahh i get it. so the reference doesnt need the same name, as the function is just waiting for a vector to fill the vector reference after the string ones. thanks

Yep exactly!

ye it would have been more clearer if ben named them something like HitLocationRef just for the first few examples as when theyre both identical its hard to see how it all works as a noobie, but now that I think back, I understand what ben was saying about passing in stuff.

Agreed, that’s why I try to always give things unique names to make it clear that they are not the same, even if they are very closely-related. (Although I was kind of inconsistent by giving &ObjectHit the same name as ObjectHit…)

Awesome landscape as well! What material did you use for it?

I looked at this function as well does this actually give accurate results? Based off of the documentation and the settings for the project I think Unreal set’s the cursor by default to be the center of the window, I couldn’t see how to change the default cursor without using a HUD or some other Blueprint. Which could actually mean that your line trace is some distance below your actual pixel out into the world. Correct me if I’m wrong I’m trying to rap my head around it.

Hi Ben,
Can you define the length of the hit ray in each way of doing it?

I.e., in your method using LineTraceSingleByChannel you set a length of ray to ‘shoot’ out. However I’ve tried also using the above given example by RyanPalmer using GetHitResultAtScreenPosition and I can’t seem to see how to set how far out you trace till? - ergo, when I move the curser across the landscape, it reports a hit so far, yet I aim at the distant hills (surrounding the map) and it says nothing hit - as though the line trace using GetHitResultAtScreenPosition isn’t firing out that far.

Thanks,
Phil

1 Like

@DanM can you help here?

It’s a property of APlayerController, you should be able to find it in your TankPlayerController blueprint
2018-06-21%2014_11_16-BattleTank%20-%20Unreal%20Editor

1 Like

Privacy & Terms