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);
//