Inventory Discussion: Object Items vs. Actor Items

I’ve been messing around with Inventory systems recently, watching tutorials and trying to analyze other inventory systems from Marketplace to try and get a better grasp of the how’s and the why’s (which is a slow process because I am, in general, not that bright).

Regardless of the overall style of the inventory (Slot-based, Lists, etc), they seem mostly the same in a lot of ways, and they boil down to arrays and structs, but one large difference I’ve noticed is some people use Objects to store an item’s data, some use Actors.

My question is, what are some of the key differences between these two methods?
Pros and Cons? What sort of situations might one be more suitable than the other?
What if an item needs to be modified, such as enchanting a sword for increased damage or adding an element, or an extended magazine on a firearm?
Is one more suited to Singleplayer vs. Multiplayer?

Why not pawn items? that’d be useful, Well in my case I use actor and you can drop them throw them eat them hit with all of them and that is useful, inherit as much functionality as you can.

I realize that this thread is a few month old by now but man that’s one bad advice, if I ever heard one…
So let me put my 2 cents in there:

As always in programming there’s usually not the one solution for everything and a few things to consider.
If you want to use and object or an actor depends a lot on the game (or inventory) you want to make.
E.g for most single player shooter, Actors should be fine. (Similar to the shootergame example). If you have an RPG with a big inventory, you may want to consider objects, though since they are not that performance heavy. There are probably a few optimizations to be done for unused actors but I guess a simple UObject or struct will always be better. Add network replication and it will only get worse for actors and at that point you may want to consider splitting your items up a little bit.
Personally I like to have my static data in DataAssets that are simply referenced by the Item, otherwise you’ll end up with hundreds of M4s all with the same firerate, max ammo, spread values etc copied everywhere (and with that lots of wasted memory)
Now if you have a e.g. crafting system that allows to craft items with some kind of quality these stats may be different from item to item, so there could still be the need to actually do that for some values. You could always calculate the actual values from the base dataAsset and some quality value but I’d say, just save them in your item and be done with it.
And last but not least, you probably want to have different actors for pickup items anyway. They will most likely not need a skeletal mesh like some weapon or usable device might need and instead can just use a static representation.

In Conclusion, I’d say the bigger and more elaborate the gamedesign the more it makes sense to split all that stuff up. Have DataAssets for static data, item-objects for the inventory items, and actor for pickups-items and anything that the player can equip. But don’t overdo it from the start (always keep Yagni in mind^^)

4 Likes

Man that’s one good advice if I ever heard one.
Thank you for your input and not just ignoring it and letting little ole me find it and be like “yeah I’ll do it that way!”

Well… I think it’s important to break down some things because an inventory is just a menu. Items that spawn in world are actors, plain and simple.

All spawnable objects are Actors. If you can control them say like a vehicle, then a pawn makes sense. A pawn is just an actor that can be “possessed” and characters are an extension of the pawn, but have animations like walking, etc. Granted, you can animate a pawn without it being a character. It just requires extra steps. A key thing to note here is that all Actors are replicatable on the network.

Another type of object in Unreal is the UObject. All actors are UObjects. This is basically as low as you can go on the hierarchy. Just like a class is a struct with more features, a UObject is Unreal’s version of a class with some fancy Unreal-specific features. UObjects are pretty light weight so they are good for things in menus. Unlike Actors, UObjects are not replicated over the network. You can make them do so with some extra work though.

Reuben Ward has a great tutorial series for a multiplayer survival game that involves making an inventory system out of UObjects: https://www.patreon.com/reubenward

Personally, I make my inventories out of widgets because the inventory is just a menu that shows images or icons of an object that you can click/drag, etc. When I want to spawn the object or drop it into the world, I delete the widget from the menu and spawn the actor version of it. You can use buttons if you like, but I find buttons to be a bit heavy. I can make the basic user widget clickable/hoverable/draggable if I need to.

When I want to add something to the inventory it’s the reverse. There is an actor in the world that is designated as a pickup item class. When the player steps on it or interacts with it, that Actor is deleted and a widget version of it is added to the inventory. I show how to do this in my “Not Mine Craft” tutorial series: UE4 - Not Mine Craft - YouTube

When it comes to arrays, structs, lists, and slots, they’re kind of all the same thing. An array is just a list of a single type. A struct is an array of multiple types. And a list is just an array with some added features for iteration, adding/removing items, etc. They can still only hold one type as far as I know. But if all your types are of the class “item” then it doesn’t matter. That said, the TArray data type is Unreal’s version of an array which basically does what lists were supposed to do, with almost all the same functionality. You can read about how each works here:

UStructs: Structs, USTRUCTS(), They're Awesome - UE4: Guidebook

TArrays: TArray: Arrays in Unreal Engine | Unreal Engine 4.27 Documentation

Lists: List | Unreal Engine Documentation

I tend to use arrays and structs because I am more familiar with them and like I said, TArrays offer many of the same advantages as List do but are lighter from what I gather. All of my inventories are slot-based. I use structs to define the property of each item displayed and those items are added to an array that is used to populate the slots. The slots are just widgets. If only Unreal made it easy to access the children of the slots, (we) wouldn’t need to store then in an array. Kind of redundant in my opinion.

At the end of the day, it really just depends on what you want out of your inventory and how you want to manage the data. I really like the UObject approach. I might use that on my next project.

2 Likes

The way you are describing your inventory sounds exactly like my current setup. I am using objects to store item specific data on what I call the ItemObjectContainer and it all is working really well. My issue came when I drop an item in the world. I spawn the item in the world and then pass my container with all the variables through a server event to overwrite the newly spawned item container with the modified values. The server cant read the passed object though.
Upon my research after all my testing realized that objects aren’t replicated. I was considering turning my container into a struct. I don’t do anything with C++ but from the sound of it, it wouldn’t be to difficult to allow my object container to replicate? What do you think?

Are you using UObjects or Actors? Actors have replication built in.

What information are you trying to replicate? I ask because most of what is in a item can be kept in a dataAsset like anonymous said or a commonly referenced struct. Ammo I can see and maybe what’s in a player’s corpse.

But in the case of a gun or ammo, I would as you said, pass the data into the item as it spawns.

Im using Uobjects not actors.
I use actors for the physical representation in the world. Its mostly floats that im storing. For amror its durability float. For wespons its accuracy, durabilty, damage, reload speed ect. All of these variables can be changed based on the weapons upgrade level so it wont work for me to keep the stats in something that i can’t modify and pass down once i drop the item/weapon.

in theory using uobjects saves some performance but I’d recommend testing out system just using actors first.

likely it is not going to make any difference, and if you don’t have to convert between actor and uobject you have much simpler code to maintain.

i’d also make a counter point to that earlier post about splitting things up. I would avoid doing that unless there is a clear and present reason why you need to split things up. Arbitrarily splitting code just because you think separation = better code is just going to make harder to maintain and introduce more bugs.

In other words, consider writing the entire program as one massive class as being the default, and only go beyond that if you can say in simple terms why you need to. If you cannot explain why, don’t do it.

You’re in BPs right? I would check to see if the event that spawns the item is being replicated. The actor itself has replication but so does its variables. So if the item has a struct for item data or individual variables you want replicated, go to those and set them. But it sounds like you’re doing it the way I would.

I feel like the difference in coding with lots of small pieces vs larger chunks is very dependent on the programmer themselves and what’s easier for the individual. I find my best workflow to be program the whole function so its easy to read and debug. Then when I have it working the way I want I go in and separate everything into more specific functions. I can read it easier later when I haven’t looked at the code for a while.

If I use actors can I store the items in an array without there being a physical item in the world? That’s one reason I liked objects is that they don’t have a transform. I read a post where someone said they keep all the actors at 0,0,0 transform without a mesh and then when you drop an item in world you give it a mesh and change the transform. That just feels to hacky for my liking.
I need to read about data assets and see if that would better serve my purposes.
Thanks for the reply’s. This is good info.

Yes. Just create an array of actors. I would use a single actor as the parent and create the items from that parent. Then you can store an array of the item parent and since all the items are children of it, they can be stored there.

EDIT…

For clarification, the array should be of object class reference (the purple option) for easy spawning.

In your actor create a variable and in the variable type selection write in the name of your actor and choose the reference type.

They don’t exist until you spawn them.

I would just use UObjects for the menu.

I’m currently implementing an inventory system for my game using DataAssets - it does make it simple.

I’m using a generic Actor for pickup, that has a DataAsset pointer which I can set to whatever I’m wanting (random etc).

The pickups are created when I’m within a certain distance from them (There is a base “Pickup Manager” that has a small struct of Transform within the world, Type etc for each pickup in the world) - the actors are Pooled so there’s no actual Spawning going on - it’s fast and so far, easy to work with.

The downside is that each time the player moves in the world, it needs to check the pickup list for location proximity, but from what I can tell so far, that’s magnitudes more efficient that having the pickups all residing in the world (even partitioned).

there is a million good reasons to separate things, of course. I just wanted to add a counter point because it is very easy for some beginner to come along and misinterpret something that they think is a “best practice” and then they just make life harder for themselves.

i think you’ll find it very helpful if you consider using a single class for all inventory items and getting all static data from data table. And anything to be updated at runtime can be managed in actor components.

this way you get infinite extensibility that is modularized to components but you also get the ease of tweaking balance from spreadsheets.

based on the type of item you can find its static data from table and then add whatever behavior components needed when it is created.

this can all work with uobject or hybrid approach as well. I just would recommend test if thats necessary or not if you find the additional lifetime management to be a hassle.

this sort of data oriented approach is how I am handling everything in my games these days. minimizes total number of classes you need and moves much of the game to spreadsheets so it is easy to tweak / balance.

1 Like

Data-Oriented-Design is a great paradigm :+1:

A single class is great because you can modify the children as needed while keeping the core functionality on the parent. The individual item info can be kept in a spreadsheet and populated at runtime.

The best part about it is it’s incredibly scalable.

well what i was describing is more of a component based architecture that doesn’t make use of much inheritance.

the base class would do nothing more than the behavior common to all inventory items like setting up its display (static mesh, UI display, etc) and interfacing with a pickup system, for example.

any additional behavior could be handled with actor components. for instance if you have food that should spoil over time, you put that into the data table. then when the food item is created, it knows to add the Food Spoilage component.

You could make a subclass to handle that to but you lose flexibility (you might not need the flexibility, which is perfectly valid).

what i mean by lose flexibility is if you decided that you also have wood which can rot and that is essentially the same behavior as food spoiling, then if you have to make Wood Log derive from Food class that becomes very confusing. But if you just have a component for “measure how long thing has been alive” and a component for “report when item has reached end of lifespan” and these both just read values from data table, now you can cover a broad range of items without getting locked into anything very specific.

lots of ways to skin the cat, I am just adding these ideas here, not to suggest that they beat other approachs in any particular case. Have to look at the project with a wide lens and see what makes most sense.

I wonder if you might elaborate a bit further on this approach… I’m interested in having all my data in spreadsheets but don’t really understand how to pull it off…

just to add to the discussion, one function not represented but usually needed is a save/load system.

You can only save/load structs/data assets not instanced objects so it does make sense to use structs as your inventory.

that said for prototyping i also use actors as its just alot easier and modifiying structs is a nightmare.

another option is actor components since it does kinda make sense as an item ‘belongs’ to something, ie an owner actor or a world spawn.

heres my breakdown.

structs - replicate, savable, instance editable - pain to use

data assets, replicate (kinda since it doesnt change), saveable, NOT instance editble. easy to use.

objects, dont replicate(unless in c++), cant save, instance editable, easy to use.

actors, replicate, cant save, instance editable, easy to use, heavy class

actor components, same as actors but more lightweight