My Refactored Code

Hi this is my refactored code. I decided to use a struct in the Player Location and Player Reach.
.h

// CopyRight 2020 Mark Vance

#pragma once

#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "Components/InputComponent.h"
#include "PhysicsEngine/PhysicsHandleComponent.h"
#include "Grabber.generated.h"

struct PlayerProperties
{
	FVector PlayerLocation;
	FVector PlayerReach;
};

UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class ESCAPEPLAN_API UGrabber : public UActorComponent
{
	GENERATED_BODY()

public:	
	// Sets default values for this component's properties
	UGrabber();


	// Called every frame
	virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;


protected:

	// Called when the game starts
	virtual void BeginPlay() override;

private:
	//Adjusts the reach of the grabber
	UPROPERTY(EditAnywhere)
	float Reach = 100.f;
	
	UPhysicsHandleComponent* PhysicsHandle = nullptr;
	UInputComponent* GrabInput = nullptr;

	void Grab();
	void Release();
	void FindPhysicsHandle();
	void SetupInputComponent();
	PlayerProperties GetPlayerProperties() const;
	FHitResult GetFirstPhysicsBodyInReach() const;
};



.cpp

// CopyRight 2020 Mark Vance

#include "Grabber.h"
#include "DrawDebugHelpers.h"
#include "Engine/World.h"
#include "GameFramework/PlayerController.h"
#include "NavigationData.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();
	
	FindPhysicsHandle();
	SetupInputComponent();	
}

void UGrabber::Grab()
{
	//Return Hit Result from when Arm Intersects with an Object/Actor
	const FHitResult HitResultFound = GetFirstPhysicsBodyInReach();

	//Retrieve the Resolve the Component Intersected with from the HitResult
	UPrimitiveComponent* ComponentToGrab = HitResultFound.GetComponent();
	
	//Assign the Object to the Arm Using the Physics Handle Component
	if (HitResultFound.GetActor())
	{
		PhysicsHandle->GrabComponentAtLocation
		(
			ComponentToGrab,
			NAME_None,
			GetPlayerProperties().PlayerReach
		);
	}	
}

void UGrabber::Release()
{
	//Release anything currently held by the Physics Handle Component
	PhysicsHandle->ReleaseComponent();
}

void UGrabber::FindPhysicsHandle()
{
	//Assign/Bind the Physics Handle Component
	PhysicsHandle = GetOwner()->FindComponentByClass<UPhysicsHandleComponent>();

	//Check it has bound
	if (!PhysicsHandle)
	{
		UE_LOG(LogTemp, Error, TEXT("Warning Physics Handle Component has not yet loaded for %s"), *GetOwner()->GetName());
	}
}

void UGrabber::SetupInputComponent()
{
	//Bind keys to actions.  I.e. Actions within Editor have names.  Here we associate them with methods/Functions
	
	//Bind the Input Component
	GrabInput = GetOwner()->FindComponentByClass<UInputComponent>();
	if (GrabInput)
	{
		//Bind the specified mappings to functions/methods
		GrabInput->BindAction("Grab", IE_Pressed, this, &UGrabber::Grab);
		GrabInput->BindAction("Grab", IE_Released, this, &UGrabber::Release);
	}
}

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

	PlayerProperties PlayerReach = GetPlayerProperties();
	
	//Detect an intersect and populate the LineHitResult
	GetWorld()->LineTraceSingleByObjectType(
		OUT LineHitResult,
		PlayerReach.PlayerLocation,
		PlayerReach.PlayerReach,
		FCollisionObjectQueryParams(ECollisionChannel::ECC_PhysicsBody),
		TraceParams
	);

	return LineHitResult;
}

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

	if(PhysicsHandle->GrabbedComponent)
	{
		PhysicsHandle->SetTargetLocation(GetPlayerProperties().PlayerReach);
	}
}

PlayerProperties UGrabber::GetPlayerProperties() const
{
	FVector PlayerLocation;
	FRotator PlayerRotation;
	//get a players view point

	GetWorld()->GetFirstPlayerController()->GetPlayerViewPoint(
		OUT PlayerLocation,
		OUT PlayerRotation
	);

	PlayerProperties LineTraceResult;

	LineTraceResult.PlayerLocation = PlayerLocation;
	LineTraceResult.PlayerReach = PlayerLocation + PlayerRotation.Vector() * Reach;

	return LineTraceResult;
}

//For Showing the projection of the ray.
//DrawDebugLine(GetWorld(),PlayerLocation,LineTraceEnd,FColor(0,255,0),false,0.f,0,5.f);
//
2 Likes

Good looking refactored code!

1 Like

Thanks Kevin.

This was one of the options I considered for pulling the player location and reach location, if I were doing this in modern C++, I’d use a structured binding auto [hr, v_end] = FunctionThatReturnsTuple(); but, yeah, a struct is perfect for this, good work!

1 Like

Cheers Aaron. C++ is new to me. More of a C# background.

There are a lot of differences between the more legacy Win32 C/C++ style that Unreal uses and more modern C++ code styles, largely because of changes to the baseline language in the last nine years or so, but implementing some of those changes in an API as huge as Unreal is… impractical :slight_smile: