Implementing an inventory system with right click actions
This is really a hypothetical question as I'm not working on anything like this at the moment, but I've wonder in the past what the best way to approach this would be and haven't been able to come up with any good resources.
Let's say I'm making a game, and the game design required an inventory system. The main thing this system should allow for is the following:
1. It should be able to keep track of items, naturally. For simplicity's sake, no item stacking is allowed, so singular items only.
2. Items should have actions that you can execute via a right click menu, e.g. "Consume" for consumables, "Equip" for equippables, and "Drop" for all.
The first solution that comes to mind is creating a base class / interface for
Item
, and for each type of item having a derived type, e.g. Consumable
and Equippable
. These are then kept in a List<Item>
, for example. The drop action could then be added to the right click menu (exact implementation not important) as it would apply to all items, and would perhaps be implemented in the Inventory
class itself.
With this setup, what would be the "right" way to implement actions that are specific to certain types of items? Presumably the right click menu would be populated like this:
Would it make sense to have the classes deriving from item define which actions should be added to the menu? If not, how would you go about fetching valid actions for any given Item
?
Alternatively, does all I've mentioned above make sense, or would an entirely different structure be preferred here?
Any hints on where to start looking for this kind of design would also be appreciated.12 Replies
An issue with this is that an item can't have multiple uses at once like equipable and droppable. An approach I've seen is to have a file containing a table of all items such as in json format that also stores their properties as keys. For simple values like Value and Droppable, these can be made into primitive values. For more complex behaviors, their attributes can also be stored through an object such as You can then load the item and has its stats as component objects. This is also how Unity handles game objects, having things like scripts and rigidbodies as components you query at runtime with GetComponent<>(). This also has the benefit of making it easier for game designer with no programing experience to tweat the values including tweaking at runtime, as well as releasing more lightweight balance patches
Just don't fall in the trap of putting everything in a single file and loading it all at once (why gta online load times were attrocious)
An issue with this is that an item can't have multiple uses at once like equipable and droppablewell in the example I have, all items would at the very least be droppable + something else. but I see what you mean, if I had a
Consumable
and a Wearable
class, I'd run into trouble if I ever wanted to add a candy necklace.
could this not be resolved through interfaces? say all items are IDroppable
(for example), and then I could implement interfaces as needed so something could be both IConsumable
and IWearable
. of course, yeah, I suppose that could get messy having to create a concrete implementation for each combination of behaviours.
so you'd go with a component based approach (whether through the unity component system if working in unity, or some other solution) and to construct the right click menu cycle through components and resolve actions for them?Using interfaces, you would need types for every possible combination of features
yeah, I realized that as I was writing it out
You could also consider creating property wrappers for components to speed up queries and making them nullable for when they're absent
For simple values, you only store it as a nullable primitive under a property, or nullable with a default value if it's absent
The downside is you have no compile time safety as to which component an item has
right, but I feel that a simple
var menuActions = item.Components.SelectMany(c => c.GetComponentActions());
or something along those lines would be pretty clean for the menu side of thingsYou shouldn't really put stats in the code anyway and working with multiple types everywhere can get messy
Indeed, you could even simplify the obtaining of menu actions by making it a method of components, then to get all, you just call it for the components and generate for primitives
yeah that already sounds a lot cleaner than what I was playing around with in my head initially
Just have to do experimenting and benchmark of various details
I think the component / composition based workflow is still one I need to get better at leveraging
Storing components as a collection makes it easier to enumerate but longer to query a particular one, properties make queries instant but you need either need long boilerplate to get them all or use reflection (don't, especially not in the context of a game)
Or do both but then you have to deal with parity and you use more memory
Or if you really want to, use reflection and roslyn to generate the boiler plate as part of the built process
right yeah
that's all implementation details anyway, it was mostly the bigger picture I was scratching my head on