Hello! This is my first post here.
During this refactoring lesson I decided to make a struct and return that from my function considering we were using the out parameters and LineTraceEnd.
My Code Follows.
// Copyright Justin Robertson 2020
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "PhysicsEngine/PhysicsHandleComponent.h"
#include "Grabber.generated.h"
struct FPlayerReachVector
{
FVector PlayerPosition;
FVector PlayerRotation;
FVector PlayerArm;
};
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;
protected:
// Called when the game starts
virtual void BeginPlay() override;
private:
// My Variables
UPhysicsHandleComponent* PhysicsHandle = nullptr;
UInputComponent* InputComponent = nullptr;
UPROPERTY(EditAnywhere)
float Reach = 100.f;
// My Functions
void Grab();
void Release();
void FindPhysicsHandle();
void SetupInputComponent();
// Return the first Actor within reach with physics body.
FHitResult GetFirstPhysicsBodyInReach() const;
// Return the player's 'Arm' FVector by adding position and (rotation*Reach) vectors together
// also returns players position and rotation FVectors
FPlayerReachVector GetReachVector() const;
};
My Implementation
// Copyright Justin Robertson 2020
#include "Grabber.h"
#include "DrawDebugHelpers.h"
#include "Engine/World.h"
#include "GameFramework/PlayerController.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();
}
// Called every frame
void UGrabber::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
if (PhysicsHandle->GrabbedComponent)
{
PhysicsHandle->SetTargetLocation(GetReachVector().PlayerArm);
}
}
// Grab & Release Physics Objects
void UGrabber::Grab()
{
FHitResult HitResult = GetFirstPhysicsBodyInReach();
UPrimitiveComponent* ComponentToGrab = HitResult.GetComponent();
if (HitResult.GetActor())
{
PhysicsHandle->GrabComponentAtLocation(ComponentToGrab, NAME_None, GetReachVector().PlayerArm);
}
}
void UGrabber::Release()
{
UE_LOG(LogTemp, Log, TEXT("Grabber released."));
PhysicsHandle->ReleaseComponent();
}
// Setup Input Component
void UGrabber::SetupInputComponent()
{
InputComponent = GetOwner()->FindComponentByClass<UInputComponent>();
if (InputComponent)
{
InputComponent->BindAction("Grab", IE_Pressed, this, &UGrabber::Grab);
InputComponent->BindAction("Grab", IE_Released, this, &UGrabber::Release);
}
}
// Check for Physics Handle Component
void UGrabber::FindPhysicsHandle()
{
PhysicsHandle = GetOwner()->FindComponentByClass<UPhysicsHandleComponent>();
if (!PhysicsHandle)
{
UE_LOG(LogTemp, Error, TEXT("%s does not have Physics Handle Component!"), *GetOwner()->GetName());
}
}
FHitResult UGrabber::GetFirstPhysicsBodyInReach() const
{
// Raycasts out to Reach checking for PhysicsObjects
FHitResult Hit;
FCollisionQueryParams TraceParams(FName(TEXT("")), false, GetOwner());
FPlayerReachVector PawnArm = GetReachVector();
GetWorld()->LineTraceSingleByObjectType(OUT Hit, PawnArm.PlayerPosition, PawnArm.PlayerArm, FCollisionObjectQueryParams(ECollisionChannel::ECC_PhysicsBody), TraceParams);
// See what it hits
AActor* ActorHit = Hit.GetActor();
if (ActorHit)
{
UE_LOG(LogTemp, Warning, TEXT("Ray is colliding with %s."), *Hit.GetActor()->GetHumanReadableName());
}
return Hit;
}
FPlayerReachVector UGrabber::GetReachVector() const
{
FVector PlayerViewPointLocation;
FRotator PlayerViewPointRotation;
GetWorld()->GetFirstPlayerController()->GetPlayerViewPoint(OUT PlayerViewPointLocation, OUT PlayerViewPointRotation);
FPlayerReachVector ReachVector = {PlayerViewPointLocation, PlayerViewPointRotation.Vector(), PlayerViewPointLocation + PlayerViewPointRotation.Vector() * Reach};
return ReachVector;
}
I asked my dad about doing it this way and he mentioned that when it comes to this method it is important to make a judgement call on when you can use the function in an argument or go ahead and load it into storage instead of calling it every time you need one variable.
Any thoughts on this approach?