I made a few important changes to the Use system i made for the tutorial, now it works both in c++ and blueprints, and you can use blueprint interfaces to implement it, or just implement the IUsable interface in your c++ class. Both work.
Here you can see a video of how it works purely on blueprints
You can also implement the IUsable interface in a c++ class, giving you the OnUsed and OnFocus functions.
Im doing a c++ normal cast in the case the class has the IUsable interface, and then a blueprint style cast for the cases where the blueprint has the interface, but not the c++ class.
You can see the example code, using the FPS template, here GitHub - vblanco20-1/Use-System: Very simple use system for use in both c++ and blueprints, on top of the example FPS template
you are free to do whatever you want with this code.
**
How it works**
The most important part, is the Usable.h and Usable.cpp, if you see Usable.h, you can see this
#pragma once
#include "Usable.generated.h"
/**This interface has to be added to every Actor that can be used, you have to implement the OnUsed function */
UINTERFACE(MinimalAPI,Blueprintable)
class UUsable :public UInterface
{
GENERATED_UINTERFACE_BODY()
};
class IUsable
{
GENERATED_IINTERFACE_BODY()
virtual void OnUsed(AController * user);
virtual void OnFocus(AController * user);
// This will be called by the HUD if you want to display a name in the USE message, leave it empty if you dont want any message
virtual FString GetNameToDisplay();
//This event will be called when the character gets close to an object and presses the Use key.
UFUNCTION(BlueprintImplementableEvent, meta = (FriendlyName = "On Used"))
void BTOnUsed(AController * user);
//This event will be called every frame if the object is being watched by the Use System in the HUD
UFUNCTION(BlueprintImplementableEvent, meta = (FriendlyName = " On Focus"))
void BTOnFocus( AController * user);
};
The UUsable is the UE4 interface, while the IUsable is the normal c++ interface, as you can see, i have the 2 functions and the 2 blueprint events there.
When you go to the cpp file, you see this
#include "MyProject.h"
#include "Usable.h"
UUsable::UUsable(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
}
//This is required for compiling, would also let you know if somehow you called
//the base event/function rather than the over-rided version
void IUsable::OnUsed(AController * user)
{
BTOnUsed(user);
}
void IUsable::OnFocus(AController * user)
{
BTOnFocus(user);
}
FString IUsable::GetNameToDisplay()
{
return FString("");
}
the 2 inteface functions call the blueprint events by default.
The character part is this
void AMyProjectCharacter::UseTarget()
{
FHitResult HitTarget;
TraceViewTarget(HitTarget);
if (HitTarget.GetActor())
{
// Cast to IUsable for the C++ implementation of the interface
IUsable* usable =Cast<IUsable>(HitTarget.GetActor());
if (usable)
{
usable->OnUsed(GetController());
}
// pure blueprint interfaces wont be casted to IUsable, so we do it with the ImplementsInterface method and call the blueprint directly
else if (HitTarget.GetActor()->GetClass()->ImplementsInterface(UUsable::StaticClass()))
{
IUsable::Execute_BTOnUsed(HitTarget.GetActor(), GetController());
}
}
}
void AMyProjectCharacter::Tick(float DeltaSeconds)
{
Super::Tick(DeltaSeconds);
// trace usable
FHitResult HitTarget;
TraceViewTarget(HitTarget);
if (HitTarget.GetActor())
{
// Cast to IUsable for the C++ implementation of the interface
IUsable* usable = Cast<IUsable>(HitTarget.GetActor());
if (usable)
{
usable->OnFocus(GetController());
}
// pure blueprint interfaces wont be casted to IUsable, so we do it with the ImplementsInterface method and call the blueprint directly
else if (HitTarget.GetActor()->GetClass()->ImplementsInterface(UUsable::StaticClass()))
{
IUsable::Execute_BTOnFocus(HitTarget.GetActor(), GetController());
}
}
}
void AMyProjectCharacter::TraceViewTarget(FHitResult &HitResult)
{
// do a trace from view location, in the direction you are aiming, to get the object under the crosshair
FCollisionQueryParams TraceParams("UseTrace", false, this);
FVector TraceStart = GetPawnViewLocation();
FVector TraceVector = GetBaseAimRotation().Vector();
TraceVector.Normalize();
FVector TraceEnd = TraceStart + (TraceVector * UseRange);
HitResult = FHitResult(ForceInit);
GetWorld()->LineTraceSingle(HitResult, TraceStart, TraceEnd, ECC_Visibility, TraceParams);
}
The TraceViewTarget does a trace from the camera and gives the hitresult, for use the functions. The function USeTarget is what calls the OnUsed functions on the Interface, and the Tick calls the OnFocus.
The important part is this:
// Cast to IUsable for the C++ implementation of the interface
IUsable* usable = Cast<IUsable>(HitTarget.GetActor());
if (usable)
{
usable->OnFocus(GetController());
}
// pure blueprint interfaces wont be casted to IUsable, so we do it with the ImplementsInterface method and call the blueprint directly
else if (HitTarget.GetActor()->GetClass()->ImplementsInterface(UUsable::StaticClass()))
{
IUsable::Execute_BTOnFocus(HitTarget.GetActor(), GetController());
}
First, i do an InterfaceCast to do the normal c++ interface, but that does not get the blueprint interface, so it wont detect if the interface is implemented only on blueprint, so i check with ImplementsInterface, and then call the blueprint event directly, with Execute_BTOnFocus() functions, wich takes wich actor to execute the event, and the parameter of the event. This way this system works both in c++ and blueprints, without a problem.