Unity_ScriptableObject_Pros
Loading Memory Optimization Performance

Should you use ScriptableObject to store settings in Unity?

While passing technical interviews, the topic of using ScriptableObjects was raised a lot of times, but not all studios use them or even know about its advantages. In this post I would like to share reasons why I use it myself and would be happy to discuss additional pros and cons in the comments.
TL;DR: Use ScriptableObject when you need to store and customize properties of your scripts.


1. Performance

Since my blog focuses on performance, the first reason is directly connected to it and affects loading times.
I have a simple script with _speed, _drag, _rotation, and prefab reference in the component itself:

public class ExampleComponent : MonoBehaviour
{
    [SerializeField] private float _speed;
    [SerializeField] private float _drag;
    [SerializeField] private Vector3 _rotation;
    [SerializeField] private GameObject _bulletPrefab;
}

Here is how it is serialized in a prefab or a scene:

You can see here at the bottom, each property is directly serialized in a scene or a prefab file. What does it mean for us performance-wise? It means for every component of that type, each of its properties serialized separately. So if you have 1000 GameObjects with that component in a scene, Unity loads the bigger scene file and has to spend more time to deserialize it.
The alternative to it is a component with the same properties, but placed into a ScriptableObject, that the component references.

public class ExampleSettings : ScriptableObject
{
    [SerializeField] private float _speed;
    [SerializeField] private float _drag;
    [SerializeField] private Vector3 _rotation;
    [SerializeField] private GameObject _bulletPrefab;

    public float Speed => _speed;
    public float Drag => _drag;
    public Vector3 Rotation => _rotation;
    public GameObject BulletPrefab => _bulletPrefab;
}
public class ExampleComponent : MonoBehaviour
{
    [SerializeField] private ExampleSettings _settings;
}

And here is how it is serialized in that case:

Here at the bottom is a reference to our ScriptableObject only. So instead of 4 repeating serialized properties for every component in a scene, we got only one. Therefore, the scene/prefab file size is smaller and faster to load and deserialize. Basically, it is the flyweight pattern in action. You most likely won’t notice any difference on a small or even middle scale, but this can be pretty significant on a large scale or for apps where every millisecond of loading is crucial.
Here is an example with a comparison of 2 approaches in terms of scene size: I’ve added 1000 units with a few very simple properties. MonoBehaviour variant is 284KB bigger or a whopping 22% difference.

Unity_ScriptableObject_Serialization_Scene_Size

Although, I believe for most mobile games the performance argument is not really that significant, as mostly such games are not relying on tens of thousands of instances using ScriptableObjects. So let’s move onto the next pros that apply to most mobile games.


2. Collaboration and Granularity

Don’t know about you, but I hate merging prefabs or scenes, especially when there are a lot of changes. Most of the time it is easier to accept another version and reapply manually all your changes in the editor again. And that goes even for small teams, because not only developers can alter those things, but also artists changing some visual stuff, game designers tweaking properties of units or items. And if for you as a developer resolving merge conflicts is tedious, but doable, for an artist it can be a harder obstacle.
So how ScriptableObject can help here? Take a look at the first point and serialization again under the collaboration angle, and you would easily get it: all properties in a prefab are stored just by a reference to a ScriptableObject and changing any of these properties won’t even affect the prefab file. It means we reduce the scope of all possible changes that could lead to a merge conflict.
What is more, resolving a conflict in a ScriptableObject asset is also pretty easy as it mostly contains only our properties without that much noise like MonoBehaviours.


3. Architecture

By using ScriptableObject you can easily separate data provider and game logic. I have seen different components over the years and some are enormous, literally, with tens of different parameters and settings. Only fields and properties declarations in these can take around 100 lines of code. With ScriptableObject you can easily split such settings into a few classes if possible to separate your code even more. And make your component with logic a lot shorter. Yes, I know that hiding 50 fields into one ScriptableObject won’t improve your legacy class or architecture overall a lot, and most likely you should refactor it into smaller components, but using ScriptableObject makes such classes cleaner almost with no effort.


Conclusion

I believe usage of ScriptableObject greatly improves the workflow and in some cases performance and build size. But as for any advice, take a moment to think does it really benefit your project or not. Maybe you are iterating super fast on a prototype, and at this point you are fine with plain MonoBehaviours.

5 Comments

  1. Bullshit, measure performance, if you use link to asset it loads with your game object. So it doesnt matter

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: