Hi There!!
In this lecture, we tidy up our code so now it looks really good and readable. We move some blocks of code into new functions.
VS Code-
C file-
// Copyright Ateeb Ahmed 2021
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "PhysicsEngine/PhysicsHandleComponent.h"
#include "Grabber.generated.h"
UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class BUILDING_ESCAPE_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:
UPROPERTY(EditAnywhere)
float Reach = 100.f; // The player's reach, the player can grab anything within 1m 40cm.
UPhysicsHandleComponent* PhysicsHandle = nullptr;
UInputComponent* InputComponent = nullptr;
void Grab(); // This function is called when LMB is pressed. Also grabs the objects with ECC_PhysicsBody.
void Release(); // This function is called when LMB is released. Releases the currently held actor.
void FindPhysicsHandle(); // Finds Physics handle on the actor.
void SetupInputComponent(); // This function takes all the inputs from the action mapping.
// Return the first Actor within reach with physics body.
FHitResult GetFirstPhysicsBodyInReach() const;
// Return the Line Trace End.
FVector GetPlayersReach() const;
// Get Players Position in World.
FVector GetPlayerWorldPos() const;
};
C++ file-
// Copyright Ateeb Ahmed 2021
#include "DrawDebugHelpers.h"
#include "Engine/World.h"
#include "GameFramework/PlayerController.h"
#include "Grabber.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();
}
// This function takes all the inputs from the action mapping.
void UGrabber::SetupInputComponent()
{
InputComponent = GetOwner()->FindComponentByClass<UInputComponent>();
if(InputComponent)
{
UE_LOG(LogTemp, Warning, TEXT("Input component found on %s"), *GetOwner()->GetName());
InputComponent->BindAction("Grab", IE_Pressed, this, &UGrabber::Grab);
InputComponent->BindAction("Grab", IE_Released, this, &UGrabber::Release);
}
}
// This function is called when LMB is pressed. Also grabs the objects with ECC_PhysicsBody.
void UGrabber::Grab()
{
FHitResult HitResult = GetFirstPhysicsBodyInReach();
UPrimitiveComponent* ComponentToGrab = HitResult.GetComponent();
// If we hit something then attach the physics handle.
if(HitResult.GetActor())
{
PhysicsHandle->GrabComponentAtLocation
(ComponentToGrab,
NAME_None,
GetPlayersReach()
);
}
}
// This function is called when LMB is released. Releases the currently held actor.
void UGrabber::Release()
{
UE_LOG(LogTemp, Warning, TEXT("Grabbed Released!"));
PhysicsHandle->ReleaseComponent();
}
// Finds Physics handle on the actor.
void UGrabber::FindPhysicsHandle()
{
PhysicsHandle = GetOwner()->FindComponentByClass<UPhysicsHandleComponent>();
if (PhysicsHandle == nullptr)
{
UE_LOG(LogTemp, Error, TEXT("NO PHYSICS HANDLE COMPONENT FOUND ON %s!!!"), *GetOwner()->GetName());
}
}
// Called every frame
void UGrabber::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
// If the physics handle is attached.
if(PhysicsHandle->GrabbedComponent)
{
PhysicsHandle->SetTargetLocation(GetPlayersReach());
}
// Move the object we are holding.
}
// Return the first Actor within reach with physics body.
FHitResult UGrabber::GetFirstPhysicsBodyInReach() const
{
FHitResult Hit;
// Ray-cast out to a certain distance (Reach)
FCollisionQueryParams TraceParams(FName(TEXT("")), false, GetOwner());
GetWorld()->LineTraceSingleByObjectType(
OUT Hit,
GetPlayerWorldPos(),
GetPlayersReach(),
FCollisionObjectQueryParams(ECollisionChannel::ECC_PhysicsBody),
TraceParams
);
return Hit;
}
// Get Players Position in World.
FVector UGrabber::GetPlayerWorldPos() const
{
// Get player's viewpoint
FVector PlayerViewPointLocation;
FRotator PlayerViewPointRotation;
GetWorld()->GetFirstPlayerController()->GetPlayerViewPoint(
OUT PlayerViewPointLocation,
OUT PlayerViewPointRotation
);
return PlayerViewPointLocation;
}
// Return the Line Trace End
FVector UGrabber::GetPlayersReach() const
{
FVector PlayerViewPointLocation;
FRotator PlayerViewPointRotation;
GetWorld()->GetFirstPlayerController()->GetPlayerViewPoint(
OUT PlayerViewPointLocation,
OUT PlayerViewPointRotation
);
return PlayerViewPointLocation + PlayerViewPointRotation.Vector() * Reach;
}
Be very careful while refactoring your code. One small mistake and you’ll have to deal with some errors. Nothing changed in the UE Editor(I just shortened my reach, it looked really weird carrying the cube from so far.)
Previous Post: My Building Escape after Using Physics Handles lecture
Thanks for reading,
BYE!!