I know this probably comes down to very elementary design, but I’m having a heck of a time wrapping my head around it: I’m building a very basic crafting system, where crafting components may either be picked up from the ground, looted from containers, or added to the inventory programmatically (as a quest reward or whatnot), and later discarded or recombined into an item at will. What I’m struggling with a bit is understanding where in code it actually exists: I have some AActor subclass MyItem, and a TList<MyItem> myInventory which contains every item in my inventory- when an item is consumed, sold, or otherwise removed from the inventory, what do I actually need to do in order to clear it from memory and ensure it never bothers me again? Is it as simple as calling myInventory(x)::Destroy(); and never thinking about it again, or is there something more involved I need to do in order to keep my memory clean shiny?
Destroy() should be enough, though having your item as Actor is a bit wasteful in my opinion. A simple struct is better suited for this task. And you can actualy ‘store’ it in array on your character, so that your inventory management is basically TArray’s .Add() and .Remove() methods.
Hmm that sounds smarter, but it raises a related question: what is the difference between structs and classes within ue’s framework? By default I’ve always used classes as my default data holder, but I never considered the design ramifications of one over the other.
If I start on this topic it’s going to be a very long post Have you checked the docs? Programming with CPP in Unreal Engine | Unreal Engine 5.1 Documentation
This whole page (meaning, the whole “Unreal Architecture” tab with its sub-tabs) has most of the answers and it’s a must-read for anyone doing programming inside of UE.
Holy cow, that’s an incredibly useful reference- thank you!
This is a larger topic I have been struggling with as well. I am an experienced programmer and yet still having trouble wrapping my head around it because I want to keep things properly divided between programming in C++ and designing in blueprint. In other words, how do I allow for easy level design on the part of my team mates. I have a similar setup to the OP where I have items that are created and converted and destroyed in the world. I have made a class that represents an Item, and the intention was that a designer can then make a blueprint class based off that BaseItem class, and then using BP define the properties of a specific item. Repeat for all the items. The for example there are other classes where I wanted to store multiple items, and the intention was that a designer could just drag and drop the BP Items they made to slot them into this class, but that doesn’t seem to be working. They can’t be slotted in.
The idea of storing the data in simple structs is clean and efficient, but then how is a designer supposed to do their job?
Not sure I understood this correctly. Do you mean the editor behaviour? Or runtime?
If I have a UItem class and then a UContainer class then I can easily have a TArray<UItem*> property on this container to store multiple UItems.
well, i dont think it is wasteful, because there are times when you want to spawn items in the world dont you?
So without an actor as baseclass how is that supposed to be done?
I never saw someone spawns a struct in the world, so the “real” item should be a class in the end.
However using a struct for an inventory isnt a bad idea, since you could have a struct holding AItem* and f.e. the amount of those items inside an
Array of a character. And when you drop something out of your inventory you would use the item inside the struct and spawn it into the world.
thats my opinion^^
In the editor. My problem is grasping how to write code that the rest of the team can easily work with in the editor for the design level stuff. When I say drag and drop I mean I make a TArray<UBaseItem*> items
but in the editor, if they make a BP from a BaseItem, then try to assign the BP to the array in the editor, they can’t. It appears to ONLY accept a UBaseItem, not a BP derived from UBaseItem.
That said, I just stumbled upon TSubclassOf<> so I am going to try that soon as I get a chance.
Sure, items you spawn in the world are ‘pickups’. And for that you’d definetely want to use an Actor class. But I was referring to items you ‘store’ on the character, like after you’ve interacted with some pickup. You don’t want this whole pickup anymore, you only want its class and runtime properties, like quantity and etc. So for that you’re better off having a struct with several fields to hold just the information you might need in the future (when you decide to drop it or save to some persistent storage in a database).
TSubclassOf is what you want, yes. When you create a blueprint off some C++ class it is now a BlueprintGeneratedClass, which has a base class of that C++ class. This is why you don’t see blueprints in the picker when trying to add items to your array.
But again, if you use structs to define an “item” then how does a level designer, in the editor, make different types of items and define their properties? That is the part I don’t understand. With Actors – or in my case i’m trying it with ActorComponents – the designers in the editor can make a BP from it and set its properties. If I instead made my items simple structs, how would the designers then work with them?
well we can discuss it more if you like, but i think it is really game specific.
And I agree with you, in case of PickUps which dont need to be unique in some ways.
edit: I forgot the ( )
for each item you want to have you can either have one blueprint/struct or DataAsset in your project
I’m just trying to wrap my head around it, like the OP. I’ve spent a lot of time in dead ends trying to find the “right” way to compose everything and properly bridging the gap between code written in C++ and others being able to work with that in the Editor and Blueprint.
Take a simple, contrived, example. You have a game where the player has to take items from a bin, then put them in another bin. Heck let’s be really silly and say its a recycling game The player has to take items from one bin, plastics, cardboard boxes, and cans, and they have to run across the room and place each into the proper bin on the other side.
Now how would we handle the items? They never appear in the world in any way. The “exist” in multiple places, starting in a common bin’s “inventory” then being moved to the player’s inventory, then into a final bin’s inventory.
We define a base item definition, say something like “itemName”, “itemWeight”, “itemType”. Now we want the level designers, in the editor, to be able to make specific items and assign the properties. Then define a bin, and use blueprint to randomly assign items to the starting bin, etc etc. Also in BP they would need to define the logic for what bins specific items can go to. Couple ways of doing it but in this contrived example let’s say we define a bin in code, and one of its properties is an array of items, and in the editor the designer will assign specific items to that array to indicate the allowed items.
How would you structure this? Using a struct as you say to define what an Item is makes sense, but then how would a designer in the editor actually make the specific items and set their properties? And how would they then assign them to the bins?
I’m really curious to see how to approach something like this.
But a designer can’t, to my knowledge, make a blueprint class derived from a struct. A struct isn’t an entity on its own, so how would a designer create and define the items from that base struct?
If in your game you don’t have a world representation of an item then you should go with UObjects. Actor + Struct works great when you can pick up items, store them, drop and move around the world. If everything happens in the UI then you just need a blueprintable UObject which designers can derive from and create various items. Then you can have a TArray<TSubclassOf<UItem>> on your ‘bin’ object and designers can again populate it with items they want.
He can’t indeed. In my case you store this struct on a pickup. So designer creates an object ONE time, both for pickup and for item but to him it looks like a pickup. His inventory is a TArray<FInventoryItem> and when he picks up an item you just generate it using the data you have. In my case it’s just a Generate() method on a pickup which creates an item from itself which I then can add to player’s inventory.
Here is a snippet of how I have this whole inventory thing set up:
There is a pickup class from which designers can derive and then fill with data. Its properties look something like this:
&stc=1Inventory data is a struct containing immutable data (like an item’s specification of some sort). Designer can create its own templated items (e.g. equipable items, containers, consumables etc.) and its just one actor to him. He does not need to know what happens behind the scenes.
These properties do not change at runtime. Then I have an item struct:
USTRUCT(BlueprintType)
struct FInventoryItem
{
GENERATED_USTRUCT_BODY()
UPROPERTY(VisibleDefaultsOnly, BlueprintReadOnly, Category = "Immutable Data")
FGuid UUID;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Immutable Data")
FStringAssetReference AssetReference; // This is a reference to the pickup actor
UPROPERTY(VisibleDefaultsOnly, BlueprintReadOnly, Category = "Immutable Data")
FInventoryItemData ItemData; // This is just for easier data management at runtime, we get everything we need from AssetReference if needed.
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Runtime Data")
bool bIsEquipped;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Runtime Data", meta = (EditCondition = "!bIsEquiped"))
FGuid ParentUUID;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Runtime Data", meta = (ClampMin = "1")
int32 ItemQuantity;
FInventoryItem();
bool operator==(const FInventoryItem& Other) const
{
return UUID == Other.UUID;
}
bool operator!=(const FInventoryItem& Other) const
{
return UUID != Other.UUID;
}
FArchive& operator<<(FArchive& Ar, FInventoryItem& InventoryItem)
{
Ar << InventoryItem.UUID;
Ar << InventoryItem.bIsEquipped;
Ar << InventoryItem.ParentUUID;
Ar << InventoryItem.ItemQuantity;
return Ar;
}
};
I have a copy of inventory data so that it’s easier to manipulate its properties at runtime but I really only care about the runtime data which I serialize to the database.
Thanks for all the insight gang. Some things to absorb.