This is a complete copypaste from what i had in the UE4 beta program, Its a tutorial to create a USE system like the one in UDK, using interfaces. I will polish it a bit and upload to the wiki. But for now, enjoy it.
The system uses a IUsable interface, to check for objects that have that interface implemented, and if they have, you can use them.
Lets start with the IUsable interface itself:
Usable.h
#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)
class UUsable :public UInterface
{
GENERATED_UINTERFACE_BODY()
};
class IUsable
{
GENERATED_IINTERFACE_BODY()
// This function will be called when the user uses the object
virtual void OnUsed(AController * user);
// This function is called each frame from the hud, it should be used to put messages to the screen, like the USE promt in UDK
virtual void DisplayPrompt(UCanvas * Canvas,AController * user);
};
Usable.cpp
#include "Yourgame.h"
//////////////////////////////////////////////////////////////////////////
// ToStringInterface
UUsable::UUsable(const class FPostConstructInitializeProperties& PCIP)
: Super(PCIP)
{
}
//This is required for compiling, and its the base version, you can put something here and it will be the default behaviour
void IUsable::OnUsed(AController * user)
{
}
void IUsable::DisplayPrompt(UCanvas * Canvas,AController * user)
{
}
That was the IUSable interface, to implement it in an actor, you do like this
UCLASS()
class AYourUsable: public AActor, public IUsable
in the class declaration, and then you implement the OnUsed and RenderHud methods in the cpp file of your usable actor
Im using the ShooterGame sample code as base.
Then, you have to make it be used by the rest of the code, so we add a variable to the character to hold the actor you are aiming(for hud), im calling it “UseFocus” here, and its an awful name, tell me if you think of a better name for it .
in YourCharacter.h
IUsable * UseFocus;
Put it on Public, it will make things easier later.
Now, we add the detection code. This code will trace a line from the camera view of the character to the direction you are aiming, effectively picking the object you have under your crosshair.
On YourCharacter.cpp, at the bottom of the Tick() function. (this could also go into the controller if you want, but im using the ****)
if(Controller && Controller->IsLocalPlayerController()) // we check the controller becouse we dont want bots to grab the use object and we need a controller for the Getplayerviewpoint function
{
FVector CamLoc;
FRotator CamRot;
Controller->GetPlayerViewPoint(CamLoc, CamRot); // Get the camera position and rotation
const FVector StartTrace = CamLoc; // trace start is the camera location
const FVector Direction = CamRot.Vector();
const FVector EndTrace = StartTrace + Direction *200; // and trace end is the camera location + an offset in the direction you are looking, the 200 is the distance at wich it checks
// Perform trace to retrieve hit info
FCollisionQueryParams TraceParams(FName(TEXT("WeaponTrace")),true,this);
TraceParams.bTraceAsyncScene = true;
TraceParams.bReturnPhysicalMaterial = true;
FHitResult Hit(ForceInit);
GetWorld()->LineTraceSingle(Hit, StartTrace, EndTrace, COLLISION_WEAPON, TraceParams); // simple trace function
IUsable* usable = InterfaceCast<IUsable>(Hit.GetActor()); // we cast the hit actor to the IUsable interface
if(usable) // we are looking to a usable object
{
UseFocus = usable; // as the actor under crosshairs is a usable actor, we store it for the hud.
}
else
{
UseFocus = NULL; // nothing, so we set the UseFocus pointer to NULL, so it wont give problems
}
}
I added another function to the character, in wich i implemented the Use feature, this will be called when you press the E key( remember to keybind this function or just call it from blueprint)
//YourCharacter.h Declaration
UFUNCTION(BlueprintCallable, Category=Hability) // i like to use blueprints instead of scripbinding it, so i made the Use function accesible from the blueprints
virtual void Use();
//YourCharacter.cpp Implementation
void AYourCharacter::Use()
{
if(Controller == NULL) // we access the controller, make sure we have one, else we will crash
{
return;
}
FVector CamLoc;
FRotator CamRot;
Controller->GetPlayerViewPoint(CamLoc, CamRot);
const FVector StartTrace = CamLoc;
const FVector ShootDir =CamRot.Vector();
const FVector EndTrace = StartTrace + ShootDir *200;
// Perform trace to retrieve hit info
FCollisionQueryParams TraceParams(FName(TEXT("WeaponTrace")),true,this);
TraceParams.bTraceAsyncScene = true;
TraceParams.bReturnPhysicalMaterial = true;
FHitResult Hit(ForceInit);
GetWorld()->LineTraceSingle(Hit, StartTrace, EndTrace, COLLISION_WEAPON, TraceParams);
IUsable* pickup = InterfaceCast<IUsable>(Hit.GetActor());
if(pickup) // we actually hit a picup
{
pickup->OnUsed(this->Controller); // call the interface so the object can do whatever it does when its used
}
}
As you see, the Use function is more or less the same code as above for the Tick function.
The last part of the code, goes in the HUD, and its responsible for calling the DrawHUD function of the interface, so we draw prompts on the screen.
On the equivalent of ShooterHud.cpp in your project,add this to the function DrawHUD
AYourCharacter* My**** = Cast<AYourCharacter>(GetOwning****()); // grab the ****
if (My**** && My****->IsAlive())// check that is correct and alive, we dont want USE prompts when dead
{
if(My****->UseFocus != NULL)
{
My****->UseFocus->DisplayPrompt(Canvas,GetOwningPlayerController()); // call the DisplayPromt function so the Usable actor can draw itself
}
}
And thats all. This system mostly mimics what UDK had for its USE feature, and once implemented, making new Usable actors is really easy, Just make them inherit from IUsable and implement their special functions.
I hope this explanation/code helped you, If you find problems, or want to note some kind of improvement, feel free to tell me, and ill answer questions or modify the code.
ADDITION: courtesy of djmidknight , who adds an appendix that lets this system to work with ShooterGame pickups, as they dont have normal collision, and dont block the use system traces by default. This steps are required if you want to add a volume that lets the system detect it.
- Open the existing pickup blueprint in ShooterGame or create a new blueprint
- In the Components tab under Components Category click Add Component and choose any of the Shapes(note this is a preference but I found sphere and capsule worked best)
- Name the Component Ie:Trigger or you can leave default name
- Select the Component scroll down to Shape and set it around 45 - 128 depending on how precise you want the pickup area to be
- Next scroll down to Collision
- Under Collision Presets choose Custom…
- Under Collision Enabled No Physics Collision
- Under Object type Pickup ( not sure if this will really change anything )
- Under Collision Responses Check the Ignore Box
- Under Trace Responses Check the Weapon Box under Block
- Last but not least Click Compile