My Refactor using Members

My approach was to promote LineTraceEnd, ViewPointLocation & ViewPointRotation to Member variables and update them before doing the raycast and while grabbing.
Some names may be different and I cached the results of GetWorld() and GetFirstPlayerController() in order to reduce the amount of frequently called methods in the “hot loop”.
I also exposed the Reach to the editor so it can be adjusted.

Grabber.h (reduced to relevant part)

private:
	void Grab();
	void Release();
	void AssignComponentReferences();
	void SetupInputs();
	void UpdateViewPointAndReach();
	FHitResult GetFirstPhysicsBodyInReach();

private:
	UPROPERTY(EditAnywhere)
	float Reach = 100.f;
	FVector ViewPointLocation{0, 0, 0};
	FRotator ViewPointRotation{0, 0, 0};
	FVector LineTraceEnd{0, 0, 0};

	APlayerController* Player = nullptr;
	UWorld* World = nullptr;
	UPhysicsHandleComponent* PhysicsHandle = nullptr;
	UInputComponent* Input = nullptr;
};

Grabber.cpp

// (c) Blightning 2020
#include "Grabber.h"
#include "Engine/World.h"
#include "DrawDebugHelpers.h"

#define OUT

// Sets default values for this component's properties
UGrabber::UGrabber()
{
	// Set this component to be initialized when the game starts, and to be ticked every frame.  You can turn these features
	// off to improve performance if you don't need them.
	PrimaryComponentTick.bCanEverTick = true;
}

// Called when the game starts
void UGrabber::BeginPlay()
{
	Super::BeginPlay();

	AssignComponentReferences();
	SetupInputs();
}

// Called every frame
void UGrabber::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction)
{
	Super::TickComponent(DeltaTime, TickType, ThisTickFunction);

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

void UGrabber::AssignComponentReferences()
{
	//Cache World and Player to reduce amount of called methods during runtime
	World = GetWorld();
	Player = World->GetFirstPlayerController();

	PhysicsHandle = GetOwner()->FindComponentByClass<UPhysicsHandleComponent>();
	Input = GetOwner()->FindComponentByClass<UInputComponent>();

	if (!PhysicsHandle)
		UE_LOG(LogTemp, Error, TEXT("Grabber Component attached to %s needs a PhysicsHandle component attached aswell, but found none"), *GetOwner()->GetName());
}

void UGrabber::SetupInputs()
{
	Input->BindAction("Grab", IE_Pressed, this, &UGrabber::Grab);
	Input->BindAction("Grab", IE_Released, this, &UGrabber::Release);
}

void UGrabber::Grab()
{
	FHitResult HitResult = GetFirstPhysicsBodyInReach();

	if (!HitResult.GetActor())
		return;

	PhysicsHandle->GrabComponentAtLocation
	(
		HitResult.GetComponent(),
		NAME_None,
		LineTraceEnd
	);
}

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

FHitResult UGrabber::GetFirstPhysicsBodyInReach()
{
	UpdateViewPointAndReach();
	FCollisionQueryParams TraceParams{
		FName(TEXT("")),
		false,
		GetOwner()
	};

	FHitResult Hit;

	World->LineTraceSingleByObjectType(
		OUT Hit,
		ViewPointLocation,
		LineTraceEnd,
		FCollisionObjectQueryParams(ECollisionChannel::ECC_PhysicsBody),
		TraceParams
	);

	return Hit;
}

void UGrabber::UpdateViewPointAndReach()
{
	Player->GetPlayerViewPoint(OUT ViewPointLocation, OUT ViewPointRotation);
	LineTraceEnd = ViewPointLocation + ViewPointRotation.Vector() * Reach;
}

lol my view on this changes as I edit this post haha. It dont think it makes sense imho to store Pawn’s Location (ViewpointLocation), Rotation (ViewpointRotation) and the the line trace end on Grabber component for future use because the pawn is dynamic, it keeps changing it’s orientation and location constantly. So whenever you want the pawn’s position you would want to fetch the value it has at the moment instead of using it’s previous stored position in this case.

But it reduces the number of temporary objects, because you aren’t creating a variable on stack to store the results of calling APlayerController::GetPlayerViewPoint( FVector& out_Location, FRotator& out_Rotation ) and then destroying it immediately once they go out of scope, so I guess it is quite efficient. I would hold onto doing this till the end of the game development phase after tesitng the performace impact, just before shipping.

Privacy & Terms