this is really a question of 2 parts:
with the inheritance, or interface approach you will need to at some point cast, though in reality you would need to cast with the template approach anyways, where every time you call a template function, class you are casting to the type, most of it is just being done on the back end.
for your ActorInventory I am kind of assuming you mean like some kind of AInventoryItems : AActor
which you might be going to “code re-usability” too quickly where code can be re-used without making sudo-generics either through Template or either flavor of inheritance, because you will be incurring the cost of casting at some point, which will be traversing either a Virtual Function Table (inheritance), or resolving/checking types (Templates).
when you can keep what looks to be a serviceable class and just make another class that is similar.
what I end up doing is that the inventory is a TArray<FGameItem>
then it has pointers to the like the equipment slots. the FGameItem holds a TSoftClassPtr<ASpawnableItem>
to save on memory footprint through dynamic or even Async loading (really the TArray
is for FSimpleItem
that holds like an IDNumber, Level, Quantity the Instance value stuff. then the FGameItem is held in a repository for more Archetype only stuff so that I don’t have a too much data running around per item instance in each inventory instance). When an item is Equip(ASpawnableItem* EquippingItem)
I use the inventory to determine what slot to put it on, and then just put it there.
the reason to avoid Arrays “at times” is because they can be random accessed (by index, iterated over for a key) and modified, if you have say your character’s helmet at index = 5 at some random point in the programs execution index =5 might not be the helmet but rather a sword and then what?
- if there is to be some random amount of things, that it is OK to be in a random order then use a TArray (to get a specific one you will need to know the index which might have changed, or traverse the array looking for a key)
- if there is to be some random amount of things, that shall be in a defined order then maybe a TMap (to get a specific one you will need the Key, but to get multiple different keys you will have to do a C++ foreach which is iterating over the whole map anyways)
- if there is to be a set number of things, that shall always be in the same order, then probably a struct, or just member references/pointers (especially if you want to call them by specific name)
if you want to move forward with inheritance approach for these things
UENUM(BlueprintType)
enum class EContainerType : uint8
{
NONE = 0,
EquipmentStructs = 1,
ActorItems= 2
};
UCLASS(BlueprintType, ClassGroup=(Custom), meta = (BlueprintSpawnableComponent))
class [ProjectName]_API UGenericContainer : public UActorComponent
{
GENERATED_BODY()
public:
UGenericContainer();
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Container)
EContainerType ContainerType = EContainerType::NONE;
UFUNCTION(BlueprintCallable, Category = Container)
virtual int32 GetQuantity() = 0;
UFUNCTION(BlueprintCallable, Category = Container)
virtual bool IsEmpty() = 0;
protected:
int32 Qty = 0;
int32 CurrentIndex = 0;
private:
};
UCLASS()
class [PROJECTName]_API UStructInventoryComponent : public UGenericContainer
{
GENERATED_BODY()
public:
UStructInventoryComponent()
{
ContainerType = EContainerType::EquipmentStructs;
}
UFUNCTION(BlueprintCallable, Category = Container)
virtual int32 GetQuantity() override
{
Qty = Array.Num();
return Qty;
}
FItemStruct* GetCurrent()
{
if(!Array.IsEmpty() && Array.Num() < CurrentIndex)
{
return Array[CurrentIndex];
}
return nullptr;
}
UFUCTION(BlueprintCallable, Category = Container)
void AddItem(const FItemStruct& inItem);
protected:
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Container)
TArray<FItemStruct> Array;
private:
};
then to access it you could
TObjectPtr<UStructInventoryComponent> StructItems;
// or
TObjectPtr<UGenericContainer> StructItems;
// but to say get an item you would need to
switch (StructItems->ContainerType)
{
case EContainerType::EquipmentStructs:
{
FItemStruct Item = Cast<UStructInventoryComponent>(StructItems)->GetCurrent();
// do stuff with Item here
break;
}
}