Skip to main content

Quick start — C++

The same result as the Blueprint quick start, reached from code. Both are complete; pick whichever suits how your project is built.

1. Depend on the module

// YourGame.Build.cs
PrivateDependencyModuleNames.AddRange(new string[]
{
"GameplayTags",
"EDInventoryCore",
});

2. Declare the character

// YourCharacter.h
#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "EDInventoryOwnerInterface.h"
#include "YourCharacter.generated.h"

class UEDInventoryManagerComponent;
class UEDContainer;

UCLASS()
class YOURGAME_API AYourCharacter : public ACharacter, public IEDInventoryOwner
{
GENERATED_BODY()

public:
AYourCharacter();

/** Implementing the interface lets anything find this inventory without knowing the class. */
virtual UEDInventoryManagerComponent* GetInventoryManager_Implementation() const override { return Inventory; }

UFUNCTION(BlueprintPure, Category = "Inventory")
UEDContainer* GetMainContainer() const;

protected:
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Inventory")
TObjectPtr<UEDInventoryManagerComponent> Inventory;
};

IEDInventoryOwner is optional. UEDInventoryManagerComponent::Find falls back to searching the actor's components, so implement the interface only when the answer is not the obvious one — a Pawn forwarding to its PlayerState so the inventory survives a respawn.

3. Build the inventory in the constructor

// YourCharacter.cpp
#include "YourCharacter.h"

#include "EDInventoryManagerComponent.h"
#include "Container/EDContainer.h"
#include "Container/EDContainerDefinition.h"
#include "UObject/ConstructorHelpers.h"
#include "YourGameTags.h"

AYourCharacter::AYourCharacter()
{
Inventory = CreateDefaultSubobject<UEDInventoryManagerComponent>(TEXT("Inventory"));

// Which containers this character has is data. The order is the order they are consulted.
static const TCHAR* ContainerAssets[] =
{
TEXT("/Game/Inventory/Containers/DA_Container_Hotbar.DA_Container_Hotbar"),
TEXT("/Game/Inventory/Containers/DA_Container_Main.DA_Container_Main"),
TEXT("/Game/Inventory/Containers/DA_Container_Equipment.DA_Container_Equipment"),
};

for (const TCHAR* Path : ContainerAssets)
{
ConstructorHelpers::FObjectFinder<UEDContainerDefinition> Asset(Path);
if (Asset.Succeeded())
{
Inventory->InitialContainers.Add(Asset.Object);
}
}
}

UEDContainer* AYourCharacter::GetMainContainer() const
{
return Inventory ? Inventory->GetContainer(YourGameTags::Inventory_Container_Main) : nullptr;
}

ConstructorHelpers is convenient but hard-codes paths. For a shipping project prefer UPROPERTY(EditDefaultsOnly) arrays filled in a Blueprint child, or soft references resolved in BeginPlay.

Look containers up by tag, never by index

InitialContainers[1] is true until somebody reorders the array. GetContainer(Tag) is true forever, and it is also what works on a client, where containers arrive in whatever order replication gives them.

4. Give and take items

Every mutating call is authority-only and returns whether it happened.

void AYourCharacter::ServerPickUp_Implementation(UEDItemDefinition* Definition, int32 Count)
{
if (!HasAuthority() || !Inventory)
{
return;
}

// No container named: the item's own preferences decide, then container priority.
if (!Inventory->TryAddItem(Definition, Count))
{
// All-or-nothing. Nothing was added, so say so rather than assuming a partial success.
UE_LOG(LogTemp, Verbose, TEXT("No room for %d x %s"), Count, *GetNameSafe(Definition));
}
}

void AYourCharacter::SpendCoins(UEDItemDefinition* Coin, int32 Amount)
{
// Spans every container, smallest piles first, and takes nothing if the total is short.
Inventory->TryRemoveItems(Coin, Amount);
}

int32 AYourCharacter::CountArrows(UEDItemDefinition* Arrow) const
{
// One number across the hotbar, the pack, and any bag being worn.
return Inventory->GetItemCount(Arrow);
}

To act on one specific container rather than the inventory as a whole, ask for it and call it directly — the container is the thing that acts:

if (UEDContainer* Hotbar = Inventory->GetContainer(YourGameTags::Inventory_Container_Hotbar))
{
Hotbar->TryAddItem(Definition, 1); // authority only
Hotbar->RequestSort(EEDSortMode::ByCategory); // safe from a client; routed to the server
}

5. React to changes

One event covers the whole inventory, including containers gained later:

void AYourCharacter::BeginPlay()
{
Super::BeginPlay();

if (Inventory)
{
Inventory->OnInventoryChanged.AddDynamic(this, &AYourCharacter::HandleInventoryChanged);
Inventory->OnStackCountChanged.AddDynamic(this, &AYourCharacter::HandleTotalChanged);
}
}

void AYourCharacter::HandleInventoryChanged(const FEDInventoryChange& Change)
{
// Fires on the server when it changes something, and on clients when the change replicates.
}

void AYourCharacter::HandleTotalChanged(FPrimaryAssetId DefinitionId, int32 NewCount, int32 OldCount)
{
// "You now have 7 arrows" — summed across every container, so no caller counts bags.
}

If you need to touch the inventory at startup, do not poll for it:

FEDOnInventoryReadyDynamic Ready;
Ready.BindDynamic(this, &AYourCharacter::HandleInventoryReady);
Inventory->WhenReady(Ready); // fires immediately if it already is

6. Read entries

TArray<UEDContainer*> Containers;
Inventory->GetContainers(Containers);

for (const UEDContainer* Container : Containers)
{
for (const FEDContainerEntry& Entry : Container->GetEntries())
{
const UEDItemDefinition* Definition = UEDItemFunctionLibrary::ResolveDefinition(Entry.Item.DefinitionId);
const FText Name = UEDItemFunctionLibrary::GetItemDisplayName(Entry.Item);

// Fragments answer everything else about the item.
if (const FEDStackFragment* Stack = Definition->FindFragment<FEDStackFragment>())
{
const int32 Max = Stack->GetMaxStackSize();
}
}
}

Entries reference definitions; they never copy them. ResolveDefinition is the single resolution path, and it is why the Asset Manager setup matters.

What you have

The same inventory as the Blueprint walkthrough: three containers, data-driven placement, server-authoritative changes, and one event for the whole thing.

Next

  • Transactions — what Try* actually runs, and how to add your own.
  • Replication — what a client may believe, and the two traps.
  • C++ reference — the full surface, module by module.