Last time, in history's most impromptu blog post I complained that the resources surrounding Unreal and C++ specifically can be a bit lacking. Never let it be said that I complain about things and don't do anything about it. I've decided to outline how I eventually built my own item pickup system through reading the documentation, translating to C++ from Blueprint, and interpreting some existing tutorials listed below.
Goal
In this tutorial we are going to modify the provided C++ First Person Template. When the player centres their crosshairs on an item that can be used it will become highlighted with a flashing outline. After that, we will bind a key as a "Use" key which, when pressed and centred on a usable object will destroy the static mesh of the object so it is no longer visible. Though the system we are going to develop here is quite simple I hope people following it will see how easy it is to expand upon and fit into existing games. The highlighting will be made using Post-Processing Effects in order to generate the visible outline of any object on the fly.
We'll start by getting some infrastructure in place in the Unreal Editor and then move into Visual Studio to pull all of those pieces together in C++. Just like last time at the end of the tutorial will be a link to gists of the code, so if you'd rather just see the code, scroll down to the bottom.
Background
In order to run this tutorial I assume you have Unreal Engine installed, and fully functional. I am writing this tutorial assuming you have created a fresh "First Person C++" project -- I've called mine 'ItemSample' though I have tried to keep the steps generic enough that they should slot into other types of project, as well as existing ones that you may have without too much difficulty. I didn't include any Starter Content, if you wanted to that would not be a problem.
Though the C++ we use is pretty elementary, some programming know-how would not go amiss, if you are looking to build your project in Blueprint this is not your tutorial.
Unreal Editor
Importing the Highlight
In order to highlight the looked at item we need a Highlighting Material, which works similar to other materials, that will be applied to the Usable Item being looked at when all the post processing effects are applied. You can get some Highlight Materials from the internet, or you can borrow a material from the Content Examples, which is what we will do as developing one of our own is out of the scope of this tutorial.
I'm quickly going to walk you through the process of importing the Material Unreal Engine includes in its Content Examples, you can skip this step if you developed you own Material, or found one online somewhere.
Open an existing, or start a new "Content Examples" Project, find the material in it called "M_Highlight" (when this tutorial was developed it was located at /ExampleContent/Blueprint_Communication/Materials
) and right click on it. In the context menu that pops up click "Asset Actions" to expand the menu and then select 'Migrate' which will show you the path it is exporting the material to then let you select a project to migrate to, select your working project (ItemSample if you are following the tutorial exactly). You can now close (and delete if you like) the Content Example project you just created.
Finally, this step is optional, but when materials are imported from other projects they move to a path based on where they were in the original project, personally, I like to move the material in with all the other materials for my project.
Applying the Post-Processing Volume
In order to apply our highlight we need to use a Post-Processing Volume which allows effects to be applied to the scene after everything else has been built thus calculating the outline of the object we are looking at at the very end.
A Post-Processing Volume can be dragged from the 'Volumes' tab of the 'Modes' panel on the left of the screen into the scene, then look to the "Details" panel to the right to customize the volume.
Within the details panel the first thing we need to do is check the "Unbound" box under the "Post-Processing Volume" heading, this will apply our Highlight material globally within the level there will still be a box in the scene that represents the volume and it can be dragged around but doing so has no real effect, I tend to just move it up out of the way.
After applying the effect globally scroll down to the 'Misc' header and add a 'Blendable' by clicking the '+' beside where it says '0 elements', once added, in the 0th element click on the dropdown find and select the M_Highlight material.
Adding a 'Use' Keybinding
Click on the edit menu and open up the 'Project Settings...' window (located near the bottom of the list). In the window that opens click on the 'Input' tab to the left and select 'Bindings' and add a new 'Action Mapping' by clicking on the '+' sign beside the title -- I set mine to the keyboard letter 'E' but you can use whatever key seems most appropriate. You can also name the key whatever you like, I picked 'Use' as it seemed reasonably descriptive. There is no need to save anything here just close the panel when you are done.
Visual Studio
UsableItem Superclass
In the Unreal Editor we can click the 'File' menu and select the option to "Add Code to Project...", we will add a new 'Actor' type class, you can name this Actor whatever you like I called mine 'UsableActor', click 'Create Class'. Keep clicking next until it prompts you to or opens the file in Visual Studio. The superclass we're building here is really simple and easy to add to. I deleted the references to the Tick
and BeginPlay
methods in the source at the end to make it easier to pick out the relevant information, if you want or need these methods feel free to leave them in the source.
First, adjust the header (UsableItem.h
) so its parent is a StaticMeshActor
not simply an Actor
. Remember to #include "Engine/StaticMeshActor.h"
at the top of the header.
In the source file (UsableItem.cpp
) add the following line into the constructor SetMobility(EComponentMobility::Movable);
which sets the object as 'Movable' to ensure (among other things) that when you pick up the item its shadow also disappears.
In the constructor of UsableItem
in both the header and the source adjust the arguments that the constructor takes in so it includes this argument const class FObjectInitializer& PCIP
which is a built in Unreal construction used to finalize the object. In the source you will also need reference the super class by appending : Super(PCIP)
to the end of the method declaration.
Modify the Protagonist
In Visual Studio, open up your character's .cpp file (those following exactly, this is ItemSampleCharacter.cpp
) and include your newly built 'UsableItem' class with the following line #include "UsableItem.h"
. First we will build two auxiliary methods to determine what our character is looking at, and whether or not to apply the Post Processing Volume respectively, then we will add, or modify an existing Tick method to use these methods.
GetItemFocus
The first thing we need to do with the character is figure out what she is looking at, so we can determine if we need to apply post-processing effects to that item. We use raycasts to trace vision from our protagonist to objects. So, first we declare a new method that takes no arguments but returns a AUsableItem*
AUsableItem* AItemSampleCharacter::GetItemFocus(){}
Declare variables to hold the location and rotation of the camera, and one to set up how far into the distance we want to be able to use items.
FVector CameraLocation;
FRotator CameraRotation;
int howFar = 300;
Then fill these values with the ones we can get from the character's viewpoint.
Controller->GetPlayerViewPoint(CameraLocation, CameraRotation);
Now we can build the start, end and direction for the trace line.
const FVector StartTrace = CameraLocation;
const FVector Direction = CameraRotation.Vector();
const FVector EndTrace = StartTrace + Direction * howFar;
And set some parameters for the line we're going to draw.
FCollisionQueryParams TraceParams(FName(TEXT("")), true, this); TraceParams.bTraceAsyncScene = true; TraceParams.bReturnPhysicalMaterial = true;
Now we draw the line and get back whatever we were looking at.
FHitResult Hit(ForceInit);
GetWorld()->LineTraceSingle(Hit, StartTrace, EndTrace, COLLISION_VIEW, TraceParams);
Where COLLISION_VIEW
is the collision channel for the raycast, it must be defined in your project header before you can use it so open up your project's .h
file for this tutorial ItemSample.pro
and add the following line in the file:
#define COLLISION_VIEW ECC_GameTraceChannel1
Finally, back in the ItemSampleCharacter.cpp
's GetItemFocus()
we return whatever we observed casted to a UsableItem
so it will return null
if there wasn't one in view.
return Cast<AUsableItem>(Hit.GetActor());
Make sure you add this new method to your character's header(ItemSampleCharacter.h
) by including the following line
class AUsableItem* GetItemFocus();
ApplyPostProcessing
As before, create a new method, this one should also return AUsableItem*
, but take in two arguments, a AUsableItem* itemSeen
and AUsableItem* oldFocus
-- using these we will be able to turn highlighting on and off based on whether it is a new object in our field of view. The method declaration should look like this:
AUsableItem* AItemSampleCharacter::ApplyPostProcessing(AUsableItem* itemSeen, AUsableItem* oldFocus){}
Let's pause here briefly to look at what we want to do on the high level -- we want to see if on this tick our focus is the same as it was on the last tick, if it is we don't need to change anything but if it isn't we need to apply the highlight to the new object and if we were looking at something on the previous tick we need to turn its highlighting off.
Let's build a conditional to check to see if an item is being looked at on this tick:
if(itemSeen){
}
else{
}
We'll first work through the nested conditional for the case where an item is being looked at on this tick, so within the first case of the previous conditional we will add another if statement, it will check if the item being looked at is the same as the one on the last tick. If it is, it resets the highlight on the item in view by applying the CustomRenderDepth or post processing effects to the static mesh of the object being looked at:
if (itemSeen == oldFocus || oldFocus == NULL){
UStaticMeshComponent* mesh = itemSeen->GetStaticMeshComponent();
mesh->SetRenderCustomDepth(true);
}
If the object being looked at is not the same as the previous object we were looking at we first set the CustomRenderDepth on the new item and then turn the CustomRenderDepth off on the old item that we aren't looking at anymore
else if (oldFocus != NULL){
UStaticMeshComponent* mesh = itemSeen->GetStaticMeshComponent();
mesh->SetRenderCustomDepth(true);
UStaticMeshComponent* oldMesh = oldFocus->GetStaticMeshComponent();
oldMesh->SetRenderCustomDepth(false);
}
Finally, we return the item seen so that it can be our 'oldFocus
' on the next tick.
return oldFocus = itemSeen;
In the else
of our first conditional we just need to make sure that we turn off post-processing if we were looking at something on the previous tick, to that end we add conditional that checks if we were looking at something else last tick if we were, we turn off the CustomRenderDepth on that item's static mesh component.
if (oldFocus != NULL){
UStaticMeshComponent* mesh = oldFocus->GetStaticMeshComponent();
mesh->SetRenderCustomDepth(false);
}
Finally, still inside the else we return oldFocus = NULL
so on the next tick we know we weren't looking at anything.
Make sure you add this new method to your character's header(ItemSampleCharacter.h
) by including the following line
class AUsableItem* ApplyPostProcessing(AUsableItem* itemSeen, AUsableItem* oldFocus);
Tick
Some templates will already have a Tick(float DeltaSeconds)
and some will not. If you do, you can append the next bits of code and if you don't, add a new method with the following signature void AItemSampleCharacter::Tick( float DeltaTime ){}
if you just created the method add the line Super::Tick( DeltaTime );
as the first line of your new method -- if there was already a Tick method in your character that line should already be there.
After the setup, we want to check whether or not there is an item in focus right now and we want to set the current itemSeen
to that value, so we call our GetItemFocus()
like so:
AUsableItem* itemSeen = GetItemFocus();
Then we want to declare a static variable so its value will persist across separate callings of this method, this will hold the oldFocus
variable, used to track whether or not we were looking at something on the previous tick.
static AUsableItem* oldFocus = NULL;
Finally, we apply the Post Processing and we reset the oldFocus
so it is set to whatever we were looking at on this tick.
oldFocus = ApplyPostProcessing(itemSeen, oldFocus);
If you didn't already have a Tick method in your character, make sure you add it to the header:
virtual void Tick( float DeltaTime ) override;
Adding a Key Listener
This step is pretty easy, it just binds the key we set up in Unreal Editor to the character. In the character class find the SetupPlayerInputComponent
method and at the bottom add the following line:
InputComponent->BindAction("Use", IE_Pressed, this, &AItemSampleCharacter::Use);
If you called the key something other than 'Use' just make sure you change it up there as well.
Applying Action to the 'Use' Key
A more complex use of items is outside the scope of this article, so we are just going to destroy the static mesh of any object that we use. This method can be expanded to fit what you need in your game.
First, make a method for the key binding:
void AItemSampleCharacter::Use(){ }
Then, if the use key is pressed when there is an item in focus, destroy the static mesh for that object.
if (GetItemFocus()){
GetItemFocus()->GetStaticMeshComponent()->DestroyComponent();
}
Be sure to add this method to your header as well
void Use();
Putting it all Together
Save and compile the project in Visual Studio and go back to the Unreal Editor.
Drag an object into the scene, I just grabbed a cube from the Modes bar on the left. Select it, to the right in its details panel you can add a blueprint to the the object, do so and double click to open the blueprint. In the file menu of the Blueprint you can re-parent the object, select our UsableItem class as the new parent, 'Compile' and 'Save' your cube. Save and Play within the scene, when your crosshairs are over the cube it should have a flashing outline (if you used the M_Highlight material), or be outlined if you used a different material. If you press your 'Use' key within the range you set earlier while the item is flashing the static mesh will be destroyed and the object will disappear. (If the shadow remains make sure that you set the item as "Movable" in the UsableItem class).
And there you have it, an item pickup system in C++ for Unreal Engine.
References
- Tom Looman's UsableActor System is what mostly got me on the right track. The tutorial itself can be a bit confusing to a complete Unreal newbie, but his code is exceptionally clear and I highly recommend looking around his GitHub.
- Tesla Dev's tutorial was the first to really make clear exactly what was going on with the object outline. If this tutorial had been in C++ instead of Blueprint it would have been perfect for me!