So a while ago in Blueprint, I made a pretty cool system where you could implement an interface on a blueprint which gave it the ability to have a use action. Now I’ve moved over to C++ and it’s a little more complicated than I did in Blueprint, though simpler in nature I guess…I am brand new to C++ in Unreal so please bear with me if you spot something that makes you want to pull hairs out of your head! I’m also very much open to suggestions on how to perhaps do this better?
So here is my setup:
I have a UseComponent which got an OnUse event on it. Just a public function that can be fired from whatever class is getting the component itself. I attach this component to whatever object I want to have a use effect, such as pressing a button on a console or pulling a lever on a wall. Both of them can benefit from the same Use component but the OnUse event is implemented different on either. But here is where I run into issues, I can’t figure out how to set up my Trace so that I can look at pretty much any kind of object to look for the Use component (if any).
Here is the simple code from my UseComponent:
UseComponent.h:
#pragma once
#include "Components/ActorComponent.h"
#include "UseComponent.generated.h"
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FUseEvent); // Used to make Blueprint Events
UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class BUILDINGESCAPE_API UUseComponent : public UActorComponent
{
GENERATED_BODY()
public:
// Sets default values for this component's properties
UUseComponent();
UPROPERTY(BlueprintAssignable)
FUseEvent OnUse;
// Called when the game starts
virtual void BeginPlay() override;
// Called every frame
virtual void TickComponent( float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction ) override;
void FireUseEvent();
};
Pretty simple. I got the custom event FUseEvent and then I have the function FireUseEvent();
From UseComponent.cpp
void UUseComponent::FireUseEvent()
{
OnUse.Broadcast();
}
Pretty simple right?
In my DefaultPlayerController class I have the following code:
DefaultPlayerController.h
#pragma once
#include "Components/ActorComponent.h"
#include "DefaultPlayerController.generated.h"
UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class BUILDINGESCAPE_API UDefaultPlayerController : public UActorComponent
{
GENERATED_BODY()
public:
// How far ahead of the player can we reach in cm
UPROPERTY(EditAnywhere, BlueprintReadWrite)
float Reach = 100.f;
// Sets default values for this component's properties
UDefaultPlayerController();
// Called when the game starts
virtual void BeginPlay() override;
// Called every frame
virtual void TickComponent( float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction ) override;
private:
UPhysicsHandleComponent* PhysicsHandle = nullptr;
UInputComponent* InputComponent = nullptr;
/*---------------*/
/* Input Section */
/*---------------*/
// Ray-cast and use what's in reach
void Use();
// Ray-cast and grab what's in reach
void Grab();
// Called when grab key is released
void ReleaseGrab();
/*-----------------*/
/* Helper Functions*/
/*-----------------*/
// Find attached physics handle
void FindPhysicsHandleComponent();
// Setup (assumed) attached input component
void SetupInputComponent();
// Return hit for first physics body in reach
const FHitResult GetFirstPhysicsBodyInReach();
// Return hit for first use handle in reach
const FHitResult GetFirstUseHandleInReach();
// Find the vector at the start of the players reach
FVector GetReachLineStart();
// Find the vector at the end of the players reach
FVector GetReachLineEnd();
};
The two methods of interest are:
const FHitResult GetFirstUseHandleInReach(); and void Use();. The first is used to actually do the trace to find an object to interact with. The latter is to try and see if that object got a UUseComponent on it and then call the “FireUseEvent()” method so I can control that event through Blueprint like this:
Now this works great! Except for one detail:
I can only track one type of object through this. At least to my knowledge. How would I check all kinds of objects that could potentially have this component on it?
In my code I do this:
DefaultPlayerController.cpp
void UDefaultPlayerController::Use()
{
if (!PhysicsHandle->GrabbedComponent)
{
FHitResult HitResult = GetFirstUseHandleInReach();
AActor* ActorHit = HitResult.GetActor();
if (ActorHit)
{
UUseComponent* UseComp = ActorHit->FindComponentByClass<UUseComponent>();
if (UseComp)
{
UseComp->FireUseEvent();
}
}
}
}
const FHitResult UDefaultPlayerController::GetFirstUseHandleInReach()
{
/// Line-trace (AKA ray-cast) out to reach
FHitResult HitResult;
// Setup Query parameters
FCollisionQueryParams TraceParameters(FName(TEXT("")), false, GetOwner());
GetWorld()->LineTraceSingleByObjectType(OUT HitResult, GetReachLineStart(), GetReachLineEnd(), FCollisionObjectQueryParams(ECollisionChannel::ECC_PhysicsBody), TraceParameters);
return HitResult;
}
This is the line I need help with:
GetWorld()->LineTraceSingleByObjectType(OUT HitResult, GetReachLineStart(), GetReachLineEnd(), FCollisionObjectQueryParams(ECollisionChannel::ECC_PhysicsBody), TraceParameters);
Here I only look for PhysicsBodies. What if I also wanted to look for WorldDynamic, and WorldStatic?