C
C#3y ago
Aeon Vex

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:
var menu = new RightClickMenu();
menu.AddAction("Drop", () => Drop(item));
// OR
menu.AddAction(new DropAction(item));
var menu = new RightClickMenu();
menu.AddAction("Drop", () => Drop(item));
// OR
menu.AddAction(new DropAction(item));
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
TheBoxyBear
TheBoxyBear3y ago
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
"attack": {
"damage": 10
}
"attack": {
"damage": 10
}
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)
Aeon Vex
Aeon VexOP3y ago
An issue with this is that an item can't have multiple uses at once like equipable and droppable
well 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?
TheBoxyBear
TheBoxyBear3y ago
Using interfaces, you would need types for every possible combination of features
Aeon Vex
Aeon VexOP3y ago
yeah, I realized that as I was writing it out
TheBoxyBear
TheBoxyBear3y ago
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
Aeon Vex
Aeon VexOP3y ago
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 things
TheBoxyBear
TheBoxyBear3y ago
You 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
Aeon Vex
Aeon VexOP3y ago
yeah that already sounds a lot cleaner than what I was playing around with in my head initially
TheBoxyBear
TheBoxyBear3y ago
Just have to do experimenting and benchmark of various details
Aeon Vex
Aeon VexOP3y ago
I think the component / composition based workflow is still one I need to get better at leveraging
TheBoxyBear
TheBoxyBear3y ago
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
Aeon Vex
Aeon VexOP3y ago
right yeah that's all implementation details anyway, it was mostly the bigger picture I was scratching my head on
Want results from more Discord servers?
Add your server