Looking for a "Middle-Ground" solution between ScriptableObjects and pure C# classes...

Hi, I’ve been trying to design a way of storing item data that is flexible and is easy to create new items.

ScriptableObjects are good because new items can be created in the editor, all stemming from one “master item”, which means it’s super simple to create new items. On the other hand, pure C# classes are great because you can modify the data of instances during runtime without changing everything (e.g. say the player has a potion and it has a “purity” value, and the player combines it with some item and it raises the purity value. AFAIK you cannot do this with ScriptableObjects). C# classes don’t have any problems with serialization either, since I’m planning to use JSON serialization which is built into unity.

I’m kind of stumped on finding a middle ground between these two. What actually happens when you create a new ScriptableObject in the asset view, and assign it values? Would it be possible to create a new ScriptableObject SCRIPT and then copy over those values and modify them? That would be strange, because that goes against why ScriptableObjects exist…

MonoBehavior isn’t good for this, because items don’t need to be attached to GameObjects.

In summary, I want a way to represent item instances that aren’t universal (like how ScriptableObjects are) so that individual instances don’t have the same value. Any help is good. Thanks.

Classes don’t have to be monobehaviour, just don’t extend it. You won’t be able to use the monobehavior functions.

ScriptableObjects can’t be edited at runtime, so you should only use them for static data that’s shared between all instances of an item. “Purity”, “durability”, etc are aspects that need to handled on an instance-basis, modified at runtime, so making ScriptableObjects with that data would be pointless.

In my own inventory management systems, I use ScriptableObjects pretty extensively, but only for the information that’s not going to change at any point- for the definitions of what items are, and not the items themselves. That’s an important distinction.

I think you’re on the right track here, you just need to divide the responsibility between Definition objects, which ScriptableObjects are useful for, and Instance objects, which typical C# objects are good for. Just have each instance of an item you create keep a reference back to the Definition. Save the instance inventory data (and ID or something for the Definition reference) in your normal save file structure, as JSON or XML.

Be careful about how you use “one master item” class btw- adding a derived type to a base type collection in a ScriptableObject won’t serialize properly, so any of the derived-specific data will be lost. In other words, if you have one ScriptableObject item definition master class called “ItemDefinition”, then have derived types of “WeaponDefinition”, “ArmorDefinition”, etc, and store all of these items in a single “ItemDefinition” list, then when you go to load it later it will have lost the data specific to Weapons and Armor, and only have the generic Item data left. This is due to the serialization limitations and how the class types are stored in the XAML file.

You can actually have runtime scriptable objects based on a template object (if this is what you are looking for)

this would allow you to set dynamic properties on the SO instance and possibly save them to a new asset if you want to.

To have a runtime instance of a Scriptable object just use

TemplateSoClass instance = Object.instantiate(TemplateSOAsset)

Where TemplateSOAsset is an asset created from TemplateSoClass.

This is the way I am using it in my project and it works pretty well.

You can also decide this at a later time, so you will be able to change “static” variables during test and then make them not changeable once you are happy with them.

In my project I use it like that in a MonoBehavior Script…

        public bool CreateInstance =false;
     
        public Helicopter helicopter; //ScriptableObject

void Start () {
     
    if(CreateInstance)
                h = Object.Instantiate(helicopter);
            else h = helicopter;
}

Erm, if ItemDefintion is a ScriptableObject, linking any derived type in an array / a list of that base type does not cause any data loss.
Unless you mean something completely different.

The data loss is if the collection itself is in a ScriptableObject- when it gets written out to an XAML file, it only bothers with the data that applies to the base type, not any derived types that the objects may have originally been. If the individual object types in the collection are also ScriptableObjects, that should be fine if their data is stored in independent files, but my note was simply to be cautious of the serialization limitations here. May have phrased something wrong- been a long day.

1 Like

Ah, may bad then. Thought you meant what i described. :stuck_out_tongue:

I just create an item class inside an item container class with a list of items. Make them both System.Serializable, and I can edit them in the editor and they are right there easy to change and save. Granted, I’ve only done simple tests on it.
It seems like I’ve seen a lot of complaints about scriptable objects. They look easy, but then the limitations start coming out.