What is the purpose of ScriptableObject versus normal class?

Hey all,

This should be a pretty simple question. :slight_smile: I think the documentation is pretty vague on this point, and only states that ScriptableObjects can be used when you “want to create objects that don’t need to be attached to game objects.”

Okay, so far so good. But how exactly does that make a ScriptableObject any different from any other class I define, and then instantiate objects of with the new keyword? Those are also pretty much “objects that don’t need to be attached to game objects”.

I understand that with ScriptableObject, I get access to inherited methods such as UnityEngine.Object’s Destroy and DestroyImmediate, but what exactly do I gain from Destroy()'ing something as opposed to just setting its reference to null, and then letting the GC clean it up?

I’ve been defining and using lots and lots of my own classes and creating instances of them with the new-keyword all along, like any other programmer who’s been trained outside Unity. But when I create a class, should I actually be inheriting from ScriptableObject all the time, and should I always be using Destroy when I want to release memory? I get this cold feeling running down my back that there’s something here that it’s seriously about time I understand right. >_<

Can anyone describe some examples where you’ve used ScriptableObject, and explain to what end?

Serialization!

Unity uses it's own serialization methods to save and load objects. This serialization methods only supports classes that are derived from UnityEngine.Object.

You may say: "wait Unity can serialize every class with a [System.Serializable] attribute", well that's just partly true. If you have a MonoBehaviour class that has a public variable of your own class, Unity serializes the custom class as part of the MonoBehaviour. Unity doesn't take the true type into account.

If you have a variable of a base class in your MonoBehaviour but you hold an instance of a derived class, the class will be lost / converted into an instance of the base type when serialized / deserialized.

ScriptableObjects are true standalone objects. They are serialized on their own and the MonoBehaviour will just hold the reference to the instance, even when serialized.

If you don't need the serialization feature, use normal classes.

Keep in mind that if you want to use serialization Unity does serialize / deserialize your objects quite often.

Entering playmode: current scene is serialized, saved and immediately deserialized

Leaving playmode: The saved scene is deserialized.

Personally i don't like the ScriptableObject and Unitys serialization. Usually i use either my own way to serialize / save objects or use MonoBehaviours on empty GOs because it makes debugging easier ;).

Use ScriptableObject to represent shipping static configuration data. Use normal objects to hold runtime data structures like priority queues, R/B trees etc.

For example, if you have many types of enemies in your game, make a ScriptableObject holding health, attack types, AI tuning, etc. If you have a crafting system, make ScriptableObjects to represent resources and recipes.

Populate your GameObject configuration using your ScriptableObjects as configuration containers, instead of exposing public fields across a bunch of different components. Use the [CreateAssetMenu] attribute on your ScriptableObject to add an entry to the Assets | Create menu.

Don’t create ScriptableObjects with new. Create and load them using the Asset system.

Now, with a game designer hat on, you can churn out dozens of enemy variations or build a giant economy right in the editor.

With a bizdev or release management hat on, you can do some cool things with AssetBundles to support content patches, DLC, in-app purchases, etc.

The serialization story is actually important and you do use those features all the time: whenever Unity builds the game for you on the way to play mode. As your project grows larger in serialized size, your iteration speed goes down; your launch, load, and download times go up.

If you populate your scene with hundreds of entities that store configuration data with simple data types and Serializable objects, you’ll get hundreds of copies of that data in your build. If you populate your scene with hundreds of entities referencing the same ScriptableObject, you’ll get exactly one copy of it in your build.

I use ScriptableObject cause Unity’s editor really wants me to. If you wanted to reference an asset with a MyMonoBehavior instance on it that’s intended to be a shared data set, you can do that, but the editor will not bring up a list of those objects when assigning it, forcing you to manually find it and drag/drop it. So if you had a concept like Material where you would like it to be a shared resource but also a usage unique/animated resource then you have to accept reduced usage quality or the hackey, bug spawning solutions like Renderer.Material/SharedMaterial.

IMO, ScriptableObject is a symptom of a design flaw, and I abhor using it… But I still do for the editor.

Don’t try to save references to SO’s to a flat file tho (ie: serialize), since Unity doesn’t retain the identifiers between builds. Sadly, this really cuts the usefulness of SO’s in half, or even less than half. A great enhancement to Unity would be to reserve the internal identifiers for SO’s so you can simply serialize references to them. I followed the tutorial about SO’s where they claimed you can use SO’s like Enums. Everything was great until I realized I need to serialize it, which required me to create my Enum list in C# anyways as a complete duplicate of my SO structure, and also then create a Dictionary lookup of Key to Data values, and create a reference list of them too on a GO. None of that would be needed if Unity just retained these internal id’s…

so close… but still so far.