GrabTrace Struct Refactor

I also went with a struct approach to remove duplicate code.

.h

#pragma once

#include "CoreMinimal.h"
#include "PhysicsEngine/PhysicsHandleComponent.h"
#include "MyGrabberComponent.generated.h"

struct FGrabTrace
{
	FVector TraceStart;
	FVector TraceEnd;
};

UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class BUILDING_ESCAPE_API UMyGrabberComponent : public UActorComponent
{
	GENERATED_BODY()

public:	
	UMyGrabberComponent();
	virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;

protected:
	virtual void BeginPlay() override;
	virtual void Grab();
	virtual void Release();
	virtual void InitPhysicsHandle();
	virtual void InitBindings();
	virtual void DrawTrace(const FGrabTrace GrabTrace) const;
	virtual void GetFirstPhysicsBodyInReach(const FGrabTrace& GrabTrace, FHitResult& HitResult) const;
	virtual FGrabTrace GetGrabTrace() const;
	
private:
	UPROPERTY(EditAnywhere)
	float Reach = 200.f;

	UPROPERTY(EditAnywhere)
	bool bDebugTrace = false;
	
	UPROPERTY(Transient)
	UPhysicsHandleComponent* PhysicsHandle = nullptr;
	
	UPROPERTY(Transient)
	UInputComponent* InputComponent = nullptr;
};

.ccp

#include "MyGrabberComponent.h"
#include "Engine/World.h"
#include "GameFramework/PlayerController.h"
#include "DrawDebugHelpers.h"

UMyGrabberComponent::UMyGrabberComponent()
{
	PrimaryComponentTick.bCanEverTick = true;
}

void UMyGrabberComponent::BeginPlay()
{
	Super::BeginPlay();

	InitPhysicsHandle();
	InitBindings();
}

void UMyGrabberComponent::InitPhysicsHandle()
{
	PhysicsHandle = GetOwner()->FindComponentByClass<UPhysicsHandleComponent>();

	if (!IsValid(PhysicsHandle))
	{
		UE_LOG(LogTemp, Error, TEXT("MyGrabberComponent requires a PhysicsHandleComponent to be attached to %s"), *GetOwner()->GetName());
	}
}

void UMyGrabberComponent::InitBindings()
{
	UInputComponent* InputComp = GetOwner()->FindComponentByClass<UInputComponent>();
	
	if (IsValid(PhysicsHandle))
	{
		InputComp->BindAction("Grab", EInputEvent::IE_Pressed, this, &UMyGrabberComponent::Grab);
		InputComp->BindAction("Grab", EInputEvent::IE_Released, this, &UMyGrabberComponent::Release);
	}
}

FGrabTrace UMyGrabberComponent::GetGrabTrace() const
{
	FRotator PlayerViewPointRotation;
	FGrabTrace GrabTrace;
	GetWorld()->GetFirstPlayerController()->GetPlayerViewPoint(GrabTrace.TraceStart, PlayerViewPointRotation);
	
	GrabTrace.TraceEnd = GrabTrace.TraceStart + (PlayerViewPointRotation.Vector() * Reach);

	return GrabTrace;
}

void UMyGrabberComponent::Grab()
{
	FHitResult ActorHit;
	const FGrabTrace GrabTrace = GetGrabTrace();

	GetFirstPhysicsBodyInReach(GrabTrace, ActorHit);

	UPrimitiveComponent* ComponentToGrab = ActorHit.GetComponent();

	if (ActorHit.GetActor())
	{
		PhysicsHandle->GrabComponentAtLocation(
         ComponentToGrab,
         NAME_None,
         GrabTrace.TraceEnd);
	}
}

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

void UMyGrabberComponent::DrawTrace(const FGrabTrace GrabTrace) const
{
		DrawDebugLine(GetWorld(),
         GrabTrace.TraceStart,
         GrabTrace.TraceEnd,
         FColor{0, 255, 0},
         false, -1, 0, 3.0f
         );
}

void UMyGrabberComponent::GetFirstPhysicsBodyInReach(const FGrabTrace& GrabTrace, FHitResult& HitResult) const
{
	if (bDebugTrace)
	{
		DrawTrace(GrabTrace);
	}
	
	const FCollisionQueryParams TraceParams{FName(TEXT("")), false, GetOwner()};

	GetWorld()->LineTraceSingleByObjectType(
		HitResult,
		GrabTrace.TraceStart,
		GrabTrace.TraceEnd,
		FCollisionObjectQueryParams(ECollisionChannel::ECC_PhysicsBody),
		TraceParams
	);
}

void UMyGrabberComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
	Super::TickComponent(DeltaTime, TickType, ThisTickFunction);

	if (IsValid(PhysicsHandle) && PhysicsHandle->GrabbedComponent)
	{
		const FGrabTrace GrabTrace = GetGrabTrace();
		PhysicsHandle->SetTargetLocation(GrabTrace.TraceEnd);
	}
}
2 Likes

Smart idea! You are thinking like an engineer.

Privacy & Terms