I didn’t fully agree with you on a number of items in the refactoring, I feel like you added a lot of code that didn’t really do anything particularly useful, and while I do recognize that this is a simple example, there wasn’t a great deal of value in actually refactoring it other than for practice. I did not create two separate functions for player and reach, nor did I encapsulate the setup functions, I only have three private functions, grab, release, and get_reach. While I do agree this results in some code duplication, in this particular case, I don’t find that obnoxious. The main issue is that you need a FVector for the end point of the reach and you need a FHitResult to find what you hit and the hit result is an out parameter, which makes this function call awkward, but… whatevs.
// (C) Aaron Stackpole, 2020
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "PhysicsEngine/PhysicsHandleComponent.h"
#include "GameFramework/PlayerController.h"
#include "Engine/World.h"
#include "Grabber.generated.h"
UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class BUILDINGESCAPE_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;
UPROPERTY(EditAnywhere)
float Reach = 100.f;
protected:
// Called when the game starts
virtual void BeginPlay() override;
private:
void grab();
void release();
FVector get_reach() const;
};
#include "DrawDebugHelpers.h"
#define OUT
UPhysicsHandleComponent* phc = nullptr;
UInputComponent* ic = nullptr;
UGrabber::UGrabber()
{
PrimaryComponentTick.bCanEverTick = true;
}
void UGrabber::BeginPlay()
{
Super::BeginPlay();
// Find Physics Handle
phc = GetOwner()->FindComponentByClass<UPhysicsHandleComponent>();
if (!phc) UE_LOG(LogTemp, Error, TEXT("No UPhysicsHandleComponent found in scene!"));
// Setup Input Component
ic = GetOwner()->FindComponentByClass<UInputComponent>();
if (ic)
{
UE_LOG(LogTemp, Warning, TEXT("UInputComponent found on %s!"), *GetOwner()->GetName());
ic->BindAction("Grab", IE_Pressed, this, &UGrabber::grab);
ic->BindAction("Release", IE_Released, this, &UGrabber::release);
}
else
{
UE_LOG(LogTemp, Error, TEXT("No UInputComponent found on %s!"), *GetOwner()->GetName());
}
}
void UGrabber::grab()
{
UE_LOG(LogTemp, Warning, TEXT("Grab pressed..."));
FVector v_pawn;
FRotator r_pawn;
FHitResult hr;
GetWorld()->GetFirstPlayerController()->GetPlayerViewPoint(OUT v_pawn, OUT r_pawn);
//UE_LOG(LogTemp, Warning, TEXT("Location: %s, Rotation: %s"), *v_pawn.ToString(), *r_pawn.ToString())
//FVector v_end = v_pawn + FVector(0.f, 0.f, 100.f); // Draw line up 1M
//DrawDebugLine(GetWorld(), v_pawn, v_end, FColor(0, 255, 0), false, 0.f, 0, 5.f);
FVector v_end = v_pawn + r_pawn.Vector() * Reach;
auto cqp = FCollisionQueryParams(FName(TEXT("cqp")), false, GetOwner());
GetWorld()->LineTraceSingleByObjectType(OUT hr, v_pawn, v_end, FCollisionObjectQueryParams(ECollisionChannel::ECC_PhysicsBody), cqp);
if (AActor* what = hr.GetActor()) UE_LOG(LogTemp, Warning, TEXT("Grabber has hit: %s"), *what->GetName());
if (UPrimitiveComponent* pc = hr.GetComponent())
phc->GrabComponentAtLocation(pc, NAME_None, v_end);
}
void UGrabber::release()
{
UE_LOG(LogTemp, Warning, TEXT("Grab released..."));
phc->ReleaseComponent();
}
FVector UGrabber::get_reach() const
{
FVector v_pawn;
FRotator r_pawn;
GetWorld()->GetFirstPlayerController()->GetPlayerViewPoint(OUT v_pawn, OUT r_pawn);
return v_pawn + r_pawn.Vector() * Reach;
}
// Called every frame
void UGrabber::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
if (phc->GrabbedComponent)
{
phc->SetTargetLocation(get_reach());
}
}```