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.
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.