Is my item system bad? Will it haunt me in the long run?

Hello, I have implemented an item system, but after looking at other examples, I feel like my item system is somehow flawed. I was wondering if someone can give some input.

So, here is how it works. There is a list of ItemData instances which is the ItemLibrary. This Class contains data of the item which is id, name, description, GameLogic, and Assets (Sprite, Mesh, Material).

GameLogic is a Type that derives from BaseItem. There are many Types Deriving from BaseItem like ClothingItem, ConsumableItem, etc.

Each item has it’s own class that derives from one of these items, for example, “Banana” derives from ConsumableItem while “Jacket” derives from ClothingItem.

Now using the Item Class mentioned at the start, we put it into a SpawnItem method that adds the GameLogic Type as a component to the newly created GameObject.

I would appreciate anyone’s feedback. Thanks in advance!

Are your ItemData’s scriptable objects? Almost sounds like from your description that they’re just plain classes.

Nonetheless you will find that with inheritance structures that you’ll have issues having something be more than one thing. What if you want something that is both wearable and consumable?

I fell into that pitfall working on my own item system. Had a base item scriptable object class, and would derive from it to make different types of items (craftable items, refinable items, castable items (ie, cast from metal)) by use of Interfaces. But when I needed to make an item that was multiple things, the amount derived classes got silly, eg, RefinableCastable, RefinableCastableCraftable… you get the idea.

Switching to a component structure proved to be the solution, when I learned that OdinInspector could expose SerializeReference. Thus I just have the one item class, that have a list of components in them instead to define all their behaviour.

In your case, the simplest solution could be to have a List instead of a single field, so as to define multiple behaviours, though you’d have to ensure they don’t conflict with one another.

Though really the item system depends heavily on the game. The game I’m working on is all about items and crafting, so my solutions are heavily engineered but would be totally unnecessary in a game with a much smaller focus on said mechanics.

Hello, first of all, thanks for your detailed response.

No, I try to stay away from scriptable objects, they look confusing to me.

As for your response, I have seen a lot of posts on the unity forums and on stack overflow which recommend multiple behaviors so I might have to try this out. But I do feel like that might get complicated real fast.

I feel stuck because my current method allows for extremely customizable item behaviors , but as a con, even if they are very similar, they each have their own class. Apple and Banana consumables do the same thing for example. The only difference between them is their assets and the amount of hunger they fill. Yet they both take separate behaviors. This is probably what is bothering me most, because it doesn’t feel efficient in the long run to have like 20 or so consumables behaviors for that amount of consumables. Do you think that this might be a problem, or am I just worrying too much?

Also If you don’t mind me asking, how do you separate your item data from your item logic, if you do so? At first I thought I could just store the items ids in a text file for persistency, but for items with durability, special states, and etc. an item id wont do the trick.

You’re missing out man! They’re your best friend when implementing these sorts of systems. As sources of immutable data they’re perfect and I highly recommend you play around with them.

I admit I’m still trying to get my head around how you organise your items. So is ItemData a monobehaviour then? Are you making lots of prefabs with various components attached to them? Or are they plain C# classes stored in a big list somewhere? Some examples of your code won’t hurt.

And by separate behaviours, do you mean multiple instances of a monobehaviour component on a game object(s), or multiple different scripts that do more or less the same thing? There’s nothing wrong with the former, but definitely something wrong with the latter. Ideally for a basic consumable you shouldn’t have to write more than one script (that changes when you have more complicated forms of consumables).

More than happy to divulge in my methods! So I’ll preface this by saying I heavily use OdinInspector to make what I do possible, and again as my game heavily revolves around items it gets a bit complicated perhaps. But the core structure is a but like this:

|-ItemBase
|--ItemBaseWrapper
|---ItemWrapperListing

ItemBase being a scriptable object with all the immutable data and components. The item itself just outlines the core data, and the list of components each define what can be done with that item, alongside the data that coincides with that purpose. As of yet I don’t have any items that define any particular game logic themselves, that’s inferred depending on whether or not an item has a certain component. But if I did I would be utilising SerialiseReference for such a purpose.

Each item scriptable object in its core data has a unique GUID as well, more on that later.

ItemBaseWrapper is a plain C# wrapper class that stores a reference to an item, and has a list of meta-components that define unique or mutable data (such as name changes, durability, etc), as a way to have data that doesn’t affect the source SO. This is the data that’s written to a save file.

The complication that occurs with this, is that because all the meta components are class type objects, I have to overload the == and != operator when comparing two ItemBaseWrapper’s, alongside for each meta-component I add as well.

And as I don’t have a grid style inventory, ItemWrapperListing is basically another plain C# wrapper class which contains an ItemBaseWrapper, and a quantity of said item, and lists of this class are used in my inventory scriptable object, or wherever I need an item and a quantity associated with it.

For saving, both ItemBaseWrapper and ItemWrapperListing (and the inventory scriptable object) have serialisation surrogates, the former of which saves its item by storing the item’s GUID and all the meta components of the normal wrapper it’s copying. When loading, the item is loaded via it’s GUID from a database, and the meta components are all reconstructed from the data written to file.

Of course not every game needs a system this involved. If items are a lighter part of your game, you could, for example, just have prefabs that you mix and match monobehaviour components (though this probably wouldn’t work for storing items in an inventory).

Alright, enough rambling from me.

2 Likes

Woah, sorry for keeping you waiting so long. We must be in different time zones. I was sleeping when you posted this.

Since my game is going to be a bit more procedural and customizable, I try not to use that much Immutable data that much. As for Prefabs, I don’t like using those either :smile:. I don’t know why but I feel like things will get messy real fast if I use Prefabs in my game. My plan is to initialize the assets at the start of the game, that come from a file. (I probably wont be using the Unity Resource file for this)

Examples? No problem! I will give a simple example:

The data of each item is stored in a class called Item

public class Item
{
    private string id;
    private string name;
    private string description;
    private Type gameLogic; // This is the Item Behavior

    private bool inInventory;
    private Vector3 position;
    private Vector3 rotation;

    private Mesh worldMesh;
    private Material worldTexture;
    private Sprite inventorySprite;
}

The Item data is stored in the BaseItem Behavior, The base methods that are in here are used by all Item Behavior

public class BaseItem : MonoBehaviour
{
    public Item itemData;

    public void InventoryToWorld();

    public void WorldToInventory();

}

Finally we have the Item (aka GameLogic) that will Inherit from BaseItem, or one of its derivations, in this example I will show an item called Ramp (just an example, couldn’t think of anything else)

public class Ramp : BaseItem, IItem
{
    private string _name = "Ramp";
    private string _description = "A small ramp that you can walk on";
    public string description { get => _description; set => _description = value; }
    public string itemName { get => _name; set => _name = value; }

    public void OnClick()
    {
        Debug.Log($"You have clicked a {_name}");
    }
}

Ignore the IItem interface, I will probably get rid of it soon.

Then I have a method called CreateItem() that takes in the itemdata, a bool to decide if it is in the inventory or not, a position and a rotation.

Same, I wonder how similar are item systems are? I just felt tired with every game using a grid inventory, and wanted to make something a little unique.

I’m doing the former :frowning: I guess I should change my system but its going to be a pain to do so.

I’ll be honest, from the general description you gave, this is going to save you dramatically more work in the long term than trying to keep the current system manageable and extensible. To build off the idea of a component based system, you can actually integrate this sort of thing into a scriptableobject setup as well. Unity has a very decent guide for doing something like this that focuses more on AI, but it can easily drive an inventory system where you can add components to items as needed.

https://learn.unity.com/tutorial/pluggable-ai-with-scriptable-objects

Yeah from what I can see you’re going to have a hard time keeping this expandable or even maintainable. I agree you get some form of flexibility by being able quickly populate new items by using your monobehaviour component, but having to hard/hand write every item like that is also going to bloat your code base and leave you with lots of repeated code (remember DRY!).

Definitely take a look at scriptable objects. You should in theory be able to just have the one item scriptable object class and use a component structure to add all the functionality you need. Then a wrapper class can be used to add modified data. Having items as assets as being able to drag them into object fields is also a huge benefit over monobehaviours.

As mentioned above you could do a Unity default component structure with scriptable objects, though I find that unwieldy. If you are keen on making a deep item system, I would also say it’s worth investing in an inspector plugin like Odin Inspector. You’ll be saving yourself months of time and headache.

Don’t worry, we’ve all been there. I had a different item system before as well, and had to make the same decision you had. My advice is to make the new system in parallel, rather than trying to change all your existing code. That way you don’t have to deal with each change breaking everything else, and once you’ve built up the code of the new system and it’s all integrated, you just have to ensure the old system doesn’t have threads anywhere before retiring it for good.

My inventories are just a list. I’ll post a screenshot of my prototype UI:

Took this when I was just finishing up with my meta data system. Both Stone and Dense Stone are the same core item scriptable object, just Dense Stone has some meta data components changing it’s name, weight and volume. (Also I’ve literally just noticed the units aren’t being converted in the summary on the right, gotta fix that).

Don’t worry mate, item systems are one of those things that look real fun to code but turn out to be a complicated beast. All part of the learning adventure!

WOW, that’s one heavy rock! Yet its worth so little :slight_smile:

Thanks for your help, @spiney199 , I will change my item system to use one behavior for multiple alike items instead of my current system where I use a separate behavior for each item. I will also look into a component system as well. Just do be clear though are you saying that I have one monobehavior and in that, a list of components? Or just multiple components in the Gameobject?

i have heard of OdinInspector and it looks pretty cool. OdinSerializer looks pretty cool as well. By any chance have you tried it? I was just going to use json.net

Also Thanks for the link, @Murgilod , I’ll check it out soon!

Honestly I’m saying you should use scriptable objects. I think if you continue using monobehaviours and game objects for game items, you’ll very quickly hit complications you can’t easily overcome. You can of course have items in the world, but they should just be game objects that reflect the data of a scriptable object they’re holding a reference to.

Odin was the first plugin I bought when I started using Unity and it is by far the best addon I have (with PEEK just behind).

For example, this is what the inspector for my items looks like:

7830438--991668--upload_2022-1-22_12-7-36.png

And not a single property drawer to do so! The serialiser is also very handy as you’ll 100% come across a data type Unity can’t serialise, and working around it is usually an epic on its own. Not that you should abuse it as it does have an overhead compared to just letting Unity serialise its assets.

As far as using the serialiser to output data like save files, I haven’t used that yet but it’s on my list of things to try out, as it’s meant to be a lot faster and performant than serialisers like Newtonsoft.JSON.

Absolutely yes. Any form of item or object system will always haunt programmers. I haven’t looked at your code, but this is just something that will always haunt programmers. Maybe that’s worth something.

At the moment you’re working towards making your life more difficult by fighting the framework.

Problems.

  1. You’re trying to avoid scriptable objects in situations where they’re really useful.
  2. You’re trying to avoid use of prefabs in situations where they’re useful.
  3. You’re using inheritance to implement item types.
  4. You’re mixing naked C# classes with unity types such as material.
  5. You’re using references to Materials and Meshes instead of prefabs.

There’s no good reason to make Jacket a class. Instead “Jacket” should be an instance of an “ItemConfiguration” which would be able to describe any item in the game. The “ItemConfiguration” should be implemneted as a ScriptableObject.

It might help to think “how would I store item configurations in a database”. Database does not really have inheritance, and stores items in uniform fashion. Then use that.

So, yes. It is bad and going to haunt you.

2 Likes

Can you expand a bit on your List? Are those actually nested monobehaviours since some of them seem to deal with Unity types? My brain malfunctioned.

Thanks for your reply @neginfinity ,

  1. You’re trying to avoid scriptable objects in situations where they’re really useful.

I’m still a little confused on how they will help me. So far I have a class that contains the inventory sprite, the mesh, and the material. That class is put into a list and through the unity inspector I add those references. When I need to use these assets I just reference them when creating the list of items.

  1. You’re trying to avoid use of prefabs in situations where they’re useful.

I Don’t really see how prefabs will help me. It seems time consuming to create a prefab for every item instead of having a list of item data and creating a game object at runtime.

  1. You’re using inheritance to implement item types.

I agree that my item types shouldn’t really be done the way they are. I will change it so that there is one Behavior for common item types.

  1. You’re mixing naked C# classes with unity types such as material.

Is there any cons of doing this? I never saw any issues doing it this way, but I also don’t know much

  1. You’re using references to Materials and Meshes instead of prefabs.

The thing is, I use the same Gameobject for when the item is in the inventory and out of the inventory. For example if an item falls out of your inventory, I take off the sprite render, and 2d related stuff, and add a mesh renderer, mesh collider, and etc. Then I change its position to be in front of the player.

Sorry if my answer’s don’t make any sense. Im really confused now what to do. Do I use composition for my items?

Scriptable object is a datablock that sits in the project, can be referenced from components, and automatically receives inspector support. So if you make an “ItemConfig” derive from scriptable object, then in any component declare public ItenConfig itemConfig;, that will create an field in inspector window, where you’ll be able to assign itemConfgig you want, click and select fomr those available in the project and so on. You’ll be able to organize them into folders, duplicate, etc. Easily.

One of your classes had fields “Mesh”, “Material” and so on. That class screams that you’re trying to reimplement prefab and are thinking that those parameters are sufficient. They’re most likely are not sufficient, and the moment you require a particle system attached on some objects and animation on some objects, your system will go caput.

Prefab can be anything. It can be sprite, 3d model, it can have sound or animation attached, all that without changing the class. And you WILL have to implement visual representation of every item anyway.

You’re missing Inspector support. Scriptable objects automatically implement serialization in the project. If you create a ScriptableObject with a “Mesh”, “Material” or “GameObject” field and drag whatever you need into it, it’ll will be automatically serialized to disc when you edit it. In your case you’ll need to reimplement serialization yourself, which is going to be a huge waste of time.

This is overcomplicated.

You can store two prefab references in item config. “worldObjectPrefab” and “inventoryScreenPrefab”. In both of those you can attach a single component which will reference “ItemConfig” related to it. The reference can be set automatically on spawn. When you need to spawn the visual in the world or inventory, you instantiate the prefab and set itemconfig reference.

I would advise to ditch current system and implement a version using prefabs and scriptable objects.
Prefab/scriptable object implementation can be done VERY quickly. In a few evenings, it will be done.

The system you’re developing now, you can easily waste a month on it. And you’ll gain nothing in return.

Among the programming principles one I find useful is “Keep It Simple”. Right now you’re going against this principle and develop your own framework from scratch, instead of using what engine provides. By doing that you’re being sidetracked from finishing your game.

1 Like

Sure! It’s a list of a base abstract class, ItemComponentBase, serialised with SerialiseReference. OdinInspector is able to expose these values, so you get polymorphic lists using default Unity serialisation.

All it really is is this:

[SerializeReference]
private List<ItemComponentBase> itemComponents = new List<ItemComponentBase>();

As for using them, you can just write your own simple ‘GetComponent’ and ‘TryGetComponent’ equivalents. Ergo, when if (TryGetItemComponent(out ItemComponentSellable sellableComponent)) gets a hit, you know the item is sellable, and you have a reference to that component as well.

And whenever you need to add new functionality to an item, you just make a new deriving item component class.

1 Like

Why, at this point, wouldn’t you just use regular gameobjects and prefabs?

Because inventory data doesn’t need Transform in most cases and doesn’t need to exist as instanced GameObjects unless the items are actually pickable in the scene. i.e. Scriptable Objects have a smaller footprint which is what you want from your data.

Scriptable Objects also have [CreateAssetMenu] attribute so you can easily create new scriptable object instances via right click context menu. And since Scriptable Object calls Awake() when its created, you can do some configuration automatically, like generating GUID for the item if ID string isNullOrEmpty and other related setup.

It’s also easier to deal with Scriptable Object assets which are unique rather than GO instances in Inventory system context. It’s easier to populate an item database automatically if you have a scriptable object type you can look for, instead of filtering all GameObject prefabs in the project or trying to limit them to a singular folder. It’s safer and easier with Scriptable Object. And you likely can save a GetComponent call or two since you’re operating with Item scriptable object type rather than regular GameObjects.

In short, Scriptable Objects have smaller memory footprint, are literally made for a use case like an Inventory system with things like right click Create context menu, have lifecycle calls for automatic configuration upon creation, and are generally easier to handle and manage as project grows, there’s almost no room for human error when creating a Scriptable Object instance in comparison to a prefab.

If you’re talking about the component list, it can live both in a scriptable object and Monobehaviour. If Item is modifiable at runtime, you’d have it in both the item SO that defines the base behaviour and the ItemWrapperMonobehaviour for runtime changes.

As above, you genuinely don’t want to use game objects for inventory items. It’s adds tons of unnecessary burden, as half your code will be dealing with transforms when it genuinely doesn’t need to.

If anything a good inventory system should have as little to do with Unity in general. This is one of the things that you can code almost exclusively in the plain C# world, as inventories are just data. Scriptable objects often make the cut as they provide a useful way to author data in Unityland.

I like your system. I think you should keep them as plain objects because then you can use the Decorator pattern.

Using the decorate pattern means your items can have multiple logic executed on them. So you could have an item that is both Consumable and Clothing.

Here’s a basic example of decorator pattern;

public class ItemBase : IItem
{
    public void Execute() { }
}

public class ConsumableItem : IItem
{
    private IItem _instance;
   
    public ConsumableItem(IItem instance)
    {
        _instance = instance;
    }
   
    public void Execute()
    {
        _instance.Execute();
        // Add your consumption logic here.
    }
}

public class WearableItem : IItem
{
    private IItem _instance;
   
    public WearableItem(IItem instance)
    {
        _instance = instance;
    }
   
    public void Execute()
    {
        _instance.Execute();
        // Add your wearable logic here.
    }
}

You can also decorate your item in any order.

IItem WearableBanana = new WearableItem(new ConsumableItem(new ItemBase()));
IItem DeliciousHat = new ConsumableItem(new WearableItem(new ItemBase()));

Problem with plain classes, it makes it hard to author things in Unity. You can’t really drag and drop plain classes into loot tables or crafting recipes, but you can easily with scriptable objects.

You can implement multiple interfaces on scriptable object types, though this let to a lot of boilerplate, which is why I ended up with the component method.