Grabber.h
// SirCharles1990
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "PhysicsEngine/PhysicsHandleComponent.h"
#include "Grabber.generated.h"
struct FPlayerReach
{
FVector PlayerLocation;
FVector PlayerReach;
};
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:
float Reach = 100.f;
UPhysicsHandleComponent* PhysicsHandle = nullptr;
UInputComponent* InputComponent = nullptr;
void GetPhysicsHandle();
void GetInputComponent();
FHitResult GetFirstPhysicsBodyInReach(FPlayerReach&) const;
FPlayerReach GetPlayerReach(FPlayerReach&) const;
void Grab();
void Release();
};
Grabber.cpp
// SirCharles1990
#include "Grabber.h"
#include "DrawDebugHelpers.h"
#include "Engine/World.h"
#include "GameFrameWork/Actor.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
PrimaryComponentTick.bCanEverTick = true;
}
// Called when the game starts
void UGrabber::BeginPlay()
{
Super::BeginPlay();
GetPhysicsHandle();
GetInputComponent();
}
// Called every frame
void UGrabber::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
if(PhysicsHandle->GrabbedComponent)
{
FPlayerReach PlayerReach;
PhysicsHandle->SetTargetLocation(GetPlayerReach(PlayerReach).PlayerReach);
}
}
void UGrabber::GetPhysicsHandle()
{
PhysicsHandle = GetOwner()->FindComponentByClass<UPhysicsHandleComponent>();
}
void UGrabber::GetInputComponent()
{
InputComponent = GetOwner()->FindComponentByClass<UInputComponent>();
if(InputComponent)
{
InputComponent->BindAction("Grab", IE_Pressed, this, &UGrabber::Grab);
InputComponent->BindAction("Release", IE_Released, this, &UGrabber::Release);
}
}
// Calculate player, location and reach FVectors
FPlayerReach UGrabber::GetPlayerReach(FPlayerReach& PlayerReach) const
{
FVector PlayerViewPointLocation;
FRotator PlayerViewPointRotation;
GetWorld()->GetFirstPlayerController()->GetPlayerViewPoint(
OUT PlayerViewPointLocation,
OUT PlayerViewPointRotation
);
PlayerReach.PlayerReach = PlayerViewPointLocation + PlayerViewPointRotation.Vector() * Reach;
PlayerReach.PlayerLocation = PlayerViewPointLocation;
return PlayerReach;
}
// Calculate which actor is within reach
FHitResult UGrabber::GetFirstPhysicsBodyInReach(FPlayerReach& PlayerReach) const
{
FHitResult Hit;
FCollisionQueryParams TraceParams(FName(TEXT("")), false, GetOwner());
GetWorld()->LineTraceSingleByObjectType(
OUT Hit,
PlayerReach.PlayerLocation,
PlayerReach.PlayerReach,
FCollisionObjectQueryParams(ECollisionChannel::ECC_PhysicsBody),
TraceParams
);
AActor* ActorHit = Hit.GetActor();
return Hit;
}
// Initialize PlayerReach struct and grab component if a component is within reach
void UGrabber::Grab()
{
FPlayerReach PlayerReach = GetPlayerReach(PlayerReach);
FHitResult HitResult = GetFirstPhysicsBodyInReach(PlayerReach);
UPrimitiveComponent* ComponentToGrab = HitResult.GetComponent();
if(ComponentToGrab)
PhysicsHandle->GrabComponentAtLocation(
ComponentToGrab,
NAME_None,
GetPlayerReach(PlayerReach).PlayerReach
);
}
// Release component if a component is being held
void UGrabber::Release()
{
UPrimitiveComponent* HeldComponent = PhysicsHandle->GrabbedComponent;
if(HeldComponent)
{
PhysicsHandle->ReleaseComponent();
}
}
I’m open to constructive criticism, but it seems to work alright!