Unreal Engine Pickupable Items
Table of Contents
Introduction
In this post I'll look at creating a Pickupable item system in Unreal Engine using C++. I will be using JetBrains Rider as my IDE for this guide.
This post is a continuation of my previous post on setting up interfaces in Unreal Engine with C++, and is part of my series of documenting what I learn while creating a hotdog truck simulator game in Unreal Engine 5.
Creating a Pickupable item interface
I will be creating an interface that holds the core actions for a Pickupable item - these will be Pickup and Drop, and will be added to an interface called Pickup.
= 0 (Pure Virtual)
A pure virtual function is a function that must be implemented by any class that inherits from the interface. For this reason, pure virtual functions do not need a default implementation in the interface class.
As there won't be a default implementation for these actions, and they will be handed by a generic actor that can be picked up and dropped, I will be making these functions pure virtual by setting them equal to 0.
public:
virtual void Pickup(AActor* CalledBy) = 0;
virtual void Drop(AActor* CalledBy) = 0;Creating a Pickupable item actor
I created a new actor class called PickupItem that implements the interaction interface created in my previous post. This will allow me to perform a pickup action on the item when it is interacted with.
I also split the logic for the pickup and drop actions into an actor component - called PickupableComponent - to keep code cleaner and more manageable, and as such, the pickup item actor needs a reference to the component in its header file.
Empty UPROPERTY() PickupableComponent
If there was no UPROPERTY() macro above the PickupableComponent property, the Unreal Engine garbage collector would not be aware of the reference, and could potentially clean it up while the actor is still using it, which would result in a stale pointer and unwanted behaviour or crashes.
We can make Unreal Engine aware of the reference by using the UPROPERTY() macro, which will ensure that the garbage collector knows about the reference and will not clean it up while it is still in use.
class FASTFOODGAME_API APickupItem : public AActor, public IInteract {
GENERATED_BODY()
public:
// Sets default values for this actor's properties
APickupItem();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
virtual void Interact_Implementation(AActor* Interactor) override;
private:
UPROPERTY()
TObjectPtr<UPickupableComponent> PickupableComponent;
};To set up the reference I use CreateDefaultSubobject in the constructor of the pickup item actor.
APickupItem::APickupItem() {
// ... existing code
// Add the components
PickupableComponent = CreateDefaultSubobject<UPickupableComponent>(TEXT("PickupableComponent"));
}For now, we will leave the interact implementation empty, as the pickup logic will be handled by the pickupable component.
Creating an attachment point in the player
Before implementing the pickupable component, I need to set up an attachment point inside the player character for holding the picked up items.
I will be using a USpringArmComponent to hold the item at a set distance in front of the player. I create this in the player character's constructor, and set up the state in BeginPlay using a few exposed properties to control the distance and offset of the held item.
AMyPlayerCharacter::AMyPlayerCharacter()
{
// ... existing code
SpringArmComponent = CreateDefaultSubobject<USpringArmComponent>(TEXT("SpringArmComponent"));
SpringArmComponent->SetupAttachment(GetRootComponent());
}
void AMyPlayerCharacter::BeginPlay()
{
// ... existing code
SpringArmComponent->bUsePawnControlRotation = false;
SpringArmComponent->bInheritPitch = true;
SpringArmComponent->bInheritYaw = true;
SpringArmComponent->bInheritRoll = true;
SpringArmComponent->bEnableCameraLag = true;
SpringArmComponent->bEnableCameraRotationLag = true;
SpringArmComponent->TargetArmLength = HeldItemDistanceFromPlayer;
SpringArmComponent->SetRelativeLocation(HeldItemLocationOffset);
SpringArmComponent->SetRelativeRotation(FRotator(0.0f, 180.0f, 0.0f));
// ... existing code
}I made the SpringArmComponent retrievable via a new PlayerUtilities interface, which the player character implements. This will allow other classes to get a reference to the spring arm component on the player, without needing to know about the player character class itself.
Challenge
If you are following along, why not try implementing the PlayerUtilities interface and the GetPlayerSpringArm function yourself?
See my implementation
Here is my implementation of the PlayerUtilities interface and the GetPlayerSpringArm function in the player character class.
UINTERFACE()
class UPlayerUtilities : public UInterface
{
GENERATED_BODY()
};
class FASTFOODGAME_API IPlayerUtilities
{
GENERATED_BODY()
public:
virtual USpringArmComponent* GetPlayerSpringArm() = 0;
};// The main class should inherit from the player utilities interface
class FASTFOODGAME_API AMyPlayerCharacter : public ACharacter, public IPlayerUtilities
// And implement the function in an inline function
virtual USpringArmComponent* GetPlayerSpringArm() override { return SpringArmComponent; }I will be making use of this in the pickupable component to attach the picked up item to the player.
Creating a held item reference in the player
One last thing before implementing the pickupable component is to have the player keep track of their currently held item. This will be used in a number of ways by the pickupable component, such as for preventing the player from picking up multiple items at once.
I just keep track of the pickupable component of the currently held item, as this will allow me to call the Drop function via interface when needed.
UPROPERTY()
TObjectPtr<UActorComponent> HeldItemPickupableComponent = nullptr;More functions were added to the player utilities interface to allow for getting and setting of the held item, as well as including a new input mapping context for dropping items.
The drop item input action has to be registered in the player, which I bound to a new function that calls the Drop function in the held item's pickupable component via the interface if an item is held.
// In the SetupPlayerInputComponent function
if (IADropItem)
EnhancedInputComponent->BindAction(IADropItem, ETriggerEvent::Started, this, &AMyPlayerCharacter::PerformDropOfHeldItem);void AMyPlayerCharacter::PerformDropOfHeldItem()
{
// Double check that something is held
if (!IsAnItemHeld())
return;
// Get the held item and call its drop function, passing in this player as the caller
IPickup* heldItemPickupInterface = Cast<IPickup>(GetHeldItem());
heldItemPickupInterface->Drop(this);
}The set held item function will also be responsible for adding and removing the drop item input mapping context, depending on whether or not an item is currently being held.
virtual void SetHeldItem(UActorComponent* NewHeldComponent) override;
virtual UActorComponent* GetHeldItem() override { return HeldItemPickupableComponent; }
void AMyPlayerCharacter::SetHeldItem(UActorComponent* NewHeldComponent)
{
// Update the held item
HeldItemPickupabbleComponent = NewHeldComponent;
// If an item is held
if (NewHeldComponent)
{
EnhancedInputLocalPlayerSubsystem->AddMappingContext(IMCDropItem, 0);
return;
}
// If no item is held
EnhancedInputLocalPlayerSubsystem->RemoveMappingContext(IMCDropItem);
}Creating a Pickupable component

This pickupable component will implement the Pickup and Drop functions from the Pickup interface.
class FASTFOODGAME_API UPickupableComponent : public UActorComponent, public IPickup
{
GENERATED_BODY()
public:
// Sets default values for this component's properties
UPickupableComponent();
protected:
// Called when the game starts
virtual void BeginPlay() override;
public:
// Called every frame
virtual void TickComponent(float DeltaTime, ELevelTick TickType,
FActorComponentTickFunction* ThisTickFunction) override;
// IPickup interface
virtual void Pickup(AActor* CalledBy) override;
virtual void Drop(AActor* CalledBy) override;
};The pickup function will first check that the calling actor (the player) implements the PlayerUtilities interface, and if it does, we can retrieve the spring arm component to attach the item to after disabling physics and collisions on the item if no other item is currently held.
void UPickupableComponent::Pickup(AActor* CalledBy) {
const bool doesActorImplementInterface = UKismetSystemLibrary::DoesImplementInterface(CalledBy, UPlayerUtilities::StaticClass());
if (!doesActorImplementInterface)
return;
IPlayerUtilities* playerUtilities = Cast<IPlayerUtilities>(CalledBy);
if (playerUtilities->IsAnItemHeld())
return;
playerUtilities->SetHeldItem(this);
USpringArmComponent* springArm = playerUtilities->GetPlayerSpringArm();
if (!springArm) {
UE_LOG(LogTemp, Warning, TEXT("Attempted to pick up item, but there was no spring arm component found in the calling actor"));
return;
}
AActor* owner = GetOwner();
// Disable physics and collision on all static meshes
TArray<UStaticMeshComponent*> staticMeshes;
owner->GetComponents<UStaticMeshComponent>(staticMeshes);
for (UStaticMeshComponent* mesh : staticMeshes) {
mesh->SetSimulatePhysics(false);
}
// Attach and reset transform
owner->AttachToComponent(springArm, FAttachmentTransformRules::SnapToTargetNotIncludingScale);
owner->SetActorRelativeLocation(FVector::ZeroVector);
owner->SetActorRelativeRotation(FRotator::ZeroRotator);
owner->SetActorEnableCollision(false);
}The drop functionality will be mostly the reverse of the pickup functionality, first checking that the calling actor implements the PlayerUtilities interface, and also that an item is actually held. If an item is held, we can call the SetHeldItem function with a null pointer to clear the held item reference, and then re-enable physics and collisions on the item before detaching it from the player.
void UPickupableComponent::Drop(AActor* CalledBy)
{
IPlayerUtilities* playerUtilities = Cast<IPlayerUtilities>(CalledBy);
if (!playerUtilities || !playerUtilities->IsAnItemHeld())
return;
playerUtilities->SetHeldItem(nullptr);
// Detach from spring arm
AActor* owner = GetOwner();
owner->DetachFromActor(FDetachmentTransformRules::KeepWorldTransform);
// Re-enable physics and collision on all static meshes
TArray<UStaticMeshComponent*> staticMeshes;
owner->GetComponents<UStaticMeshComponent>(staticMeshes);
for (UStaticMeshComponent* mesh : staticMeshes)
{
mesh->SetSimulatePhysics(true);
}
owner->SetActorEnableCollision(true);
}Integrating with the interaction system
Finally returning to the pickup item actor mentioned earlier, the interact implementation can now be filled out to call the pickup function in the pickupable component via the interface.
void APickupItem::Interact_Implementation(AActor* Interactor)
{
IInteract::Interact_Implementation(Interactor);
// Attach this actor to the interactor
IPickup* pickupInterface = Cast<IPickup>(PickupableComponent);
pickupInterface->Pickup(Interactor);
}Taking this further
This system could be expanded in many ways, such as:
- Adding a sound effect when picking up or dropping an item
- Adding a UI prompt to indicate that the player can pick up an item, why not try and make it designer friendly and allow the prompt text to be set in the editor? Or add prompt text on a per-input action basis to allow for different prompts based on the performed action?
- Allowing multiple items to be picked up at once and having a hotbar system to switch between them
- Adding an outline or highlight effect to the item when the player looks at it to indicate that it can be picked up
My Reflection
This post has taught me a lot of really useful skills in Unreal Engine to do with interfaces, components - and how they can be used to keep code cleaner and more manageable, and this will be beneficial going forward to ensure I maintain good communication practices between the systems in my game.
Using actor components and interfaces is something that I will continue to utilise going forward as it keeps my code cleaner and more manageable, but also allows for reusability on actors.
I think that in the future I will create an actor more generic than the PickupItem, perhaps an InteractableItem actor that the pickup item can inherit from, which will allow for more generic interactions that do not rely on pickup and drop functionality. In terms of my game idea, this could be useful for adding toppings to hotdogs, or interacting with other objects in the world - like opening doors.