Using the Physics Handle to Grab Objects in Unreal

Danny Padron
6 min readJul 11, 2021

The time has come to finally put all the preparation work that we have been doing to make sure that this Grab functionality in our game is working properly and that we can actually pick something up. So to start we have the PhysicsHandle itself that is provided through Unreal, and we have to make sure that we are using it correctly. Looking within our GrabFunction() we will create an FHitResult and set it to our GetFirstPhysicsBodyInReach() function.

FHitResult HitResult = GetFirstPhysicsBodyInReach();

Then the next function that we are going to use is one that is provided to us through Unreal called GrabComponentAtLocation() and the syntax for this function based on the Unreal documentation looks like this and we will break down each argument a bit to understand what exactly this function is asking us for.

void GrabComponentAtLocation(
class UPrimitiveComponent * Component,
FName InBoneName,
FVector GrabLocation
);

The first argument here, the UPrimitiveComponent this is simply a pointer to the component that we are going to grab. Which at the moment we don’t have this pointer set up so lets go ahead and add that into our code so that we can use it for the first argument, it will look like this :

UPrimitiveComponent* ComponentToGrab = HitResult.GetComponent();

Now we can use ComponentToGrab as our pointer for the first argument to GrabComponentAtLocation . The second argument in the function is the FName InBoneName which in our case we aren’t using any skeletal meshes within our world that would have bones so we would just set this to NAME_None . Then the last and final argument is asking for the Grab Location of the object, which is going to be our LineTraceEnd . Now with this setup we go into our TickComponent function and we will set the new location for that object using the SetTargetLocation function. We will set this up with a simple if statement so that if the Physics Handle IS attached then move the object we are holding.

if (PhysicsHandle->GrabbedComponent)
{
PhysicsHandle->SetTargetLocation(LineTraceEnd);
}

Now, yes, the only way for this to work is that we have to add some things to our code, because obviously the TickComponent has no idea what LineTraceEnd is because that is declared in another function. For testing purposes we are just going to copy the whole FVector part of our code for LineTraceEnd and then we will refactor it so that we don’t have doubled code everywhere. So our TickComponent will end up looking like this :

void UGrabber::TickComponent(....)
{
Super::TickComponent(....);
FVector PlayerViewPointLocation;
FRotator PlayerViewPointRotation;
GetWorld()->GetFirstPlayerController()->GetPlayerViewPoint
(
PlayerViewPointLocation,
PlayerViewPointRotation
);
FVector LineTraceEnd = PlayerViewPointLocation + PlayerViewPointRotation.Vector() * Reach; if (PhysicsHandle->GrabbedComponent)
{
PhysicsHandle->SetTargetLocation(LineTraceEnd);
}
}

This SetTargetLocation function takes in an argument of an FVector which is why we copied in that bit of code from our GetFirstPhysicsBodyInReach() function. This is just to make sure everything is working properly. With all this setup if it is all working the way we intend for it to work, we should be able to go into our World within Unreal and be able to pick up any objects in our world that have the correct PhysicsBody Type. Now we need to make sure that we can drop the object whenever we release the “Grab” key. In our UGrabber::ReleaseFunction() we do almost the same as for grab and our final ReleaseFunction should look like this :

void UGrabber::ReleaseFunction()
{
PhysicsHandle->ReleaseComponent();
}

Unreal provides us with the ReleaseComponent function already so it is very simple to create the release. Everything should be working as intended now you should be able to go into your game world and pick up any objects in the world, again , given that they have the correct PhysicsBody type.

Our game is finally coming together, we can finally pick up and drop objects within the world. We still have to add weight to these objects to make them trigger the pressure pad to open the door, but first we need to refactor our code and fix all the doubled code that we created and added in to make sure everything was working properly. The first thing in our code that we refactor is our if statement within our FindPhysicsHandle function, we want to make sure that the PhysicsHandle isn’t set to a nullptr because that will crash Unreal. So we set it up like so to give us an error message in case the PhysicsHandle is not attached.

void UGrabber::FindPhysicsHandle()
{
// Checking for Physics Handle Component
PhysicsHandle = GetOwner()->FindComponentByClass<UPhysicsHandleComponent>();
if (PhysicsHandle == nullptr)
{
UE_LOG(LogTemp, Error, TEXT("No Physics Handle component found on %s"), *GetOwner()->GetName();
}
}

That will keep it from crashing if anything were to happen where the Physics Handle did not get attached. The next major refactor we need to do is to do something about the double FVector code that we needed to duplicate to use LineTraceEnd . So in our Grabber.h file under our private members we will declare two new functions, one is to be able to return LineTraceEnd for us and the second is to be able to get the Players Position in the World.

private:
// Return LineTraceEnd
FVector GetPlayersReach() const;
// Get Players Position in the World
FVector GetPlayersWorldPosition() const;

This new GetPlayersReach function is what is going to hold the bulk of our doubled code.

FVector UGrabber::GetPlayersReach() const
{
FVector PlayerViewPointLocation;
FRotator PlayerViewPointRotation;
GetWorld()->GetFirstPlayerController()->GetPlayerViewPoint
(
PlayerViewPointLocation,
PlayerViewPointRotation
);
return PlayerViewPointLocation + PlayerViewPointRotation.Vector() * Reach;
}

Creating this function now changes the way that we use our UGrabber::GrabFunction . In our function we call on LineTraceEnd but we now created the function that returns that for us so instead of calling LineTraceEnd we will make a function call instead and it will look like this :

void UGrabber::GrabFunction()
{
FHitResult HitResult = GetFirstPhysicsBodyInReach();
UPrimitiveComponent* ComponentToGrab = HitResult.GetComponent();
AActor* ActorHit = HitResult.GetActor();
// If we hit something then attach the physics handle
if (ActorHit)
{
if (!PhysicsHandle) {return;}
{
PhysicsHandle->GrabComponentAtLocation
(
ComponentToGrab,
NAME_None,
GetPlayersReach()
);
}
}
}

We do a little refactor to our TickComponent as well, there is a lot refactoring going on but in the end it will make our code much more readable and it will be easier to understand what exactly is going on. Our TickComponent currently has our doubled code still, we will be able to remove that all now and make simple function call instead and it will clear up a lot of the clutter within the function as well.

void UGrabber::TickComponent(....)
{
Super::TickComponent(....);
if (PhysicsHandle->GrabbedComponent)
{
// Move the object that we are currently holding
PhysicsHnadle->SetTargetLocation(GetPlayersReach());
}
}

The last thing we are missing to completely have our code nice clean and organized to create our other function that we declared within the header file. The purpose of the GetPlayerWorldPosition() is exactly what the name says it will get the current location of the player. The function will look like this :

FVector UGrabber::GetPlayersWorldPosition() const
{
FVector PlayerViewPointLocation;
FRotator PlayerViewPointRotation;
GetWorld()->GetFirstPlayerController()->GetPlayerViewPoint
(
PlayerViewPointLocation,
PlayerViewPointRotation
);
return PlayerViewPointLocation;
}

The last and final thing that needs to be refactored is our GetFirstPhysicsBodyInReach() now that we created those functions we simply make function calls instead of having all the extra doubled code in here as well. The final result of this function will look like this:

FHitResult UGrabber::GetFirstPhysicsBodyInReach() const
{
FHitResult Hit;
// Ray Cast out to a certain distance (Reach)
FCollisionQueryParams TraceParams(FName(TEXT("")), false, GetOwner());
GetWorld()->LineTraceSingleByObjectType
(
Hit,
GetPlayersWorldPosition(),
GetPlayersReach(), FCollisionObjectQueryParams(ECollisionChannel::ECC_PhysicsBody),
TraceParams
);
return Hit;
}

Now our code is all cleaned up without any double written code and a much simpler way of others to understand exactly what is happening within our code. The last step we need to work on to finish our game is to add weight to our objects so that they can be used as triggers for the pressure pad once we carry them over into the appropriate place. At the current state of our game though we can already pick up and drop our objects within our world and walk around with them as well.

--

--

Danny Padron

Full stack web developer with experience in Ruby on Rails, JavaScript, React and Redux and learning more!