- Home /
Are there any reasons why I should use ScriptableObject instead of MonoBehaviour (or vice-versa)?
I have a custom toolkit written in C#, complete with a custom inspector, that uses a tree of abstract classes to store data in a modular and easily-extensible way. In order to get data to serialize properly in the editor, I (reluctantly) made all the base classes derive from MonoBehaviour. However, I am aware that ScriptableObject also plays nicely with inheritance and serialization. Before I commit to using MonoBehaviour, is there any reason why ScriptableObject would be a better choice? Performance, scripting utility, whatever. Just want to make sure I'm not building any castles on sand here.
Oh yeah, there will be many of these. Anywhere from dozens to hundreds to maybe over a thousand (maybe) in a given scene.
Answer by Statement · Dec 13, 2013 at 12:17 AM
A ScriptableObject has pros and cons, and while this may not be a complete list, this is as exhaustive as I can think of:
Pros:
Serializes nicely by reference and actual type.
Instances can be shared.
Instances can be subclassed.
Avoids polluting the inspector.
I guess it's more light weight than a component.
Doesn't belong to a game object, so destroying game objects doesn't destroy your object.
Can be stored in the scene (as long as any object in the scene has a reference to it).
Can be made into an asset so it can be reused in multiple scenes, using AssetDatabase.
Cons (and some of these are very minor to me, use your own judgement):
Invisible in scene by default - user doesn't know how many ScriptableObjects there are in a scene.
Users may implictly destroy them if they destroy the last game object referencing them (in a scene, still).
More awkward to make "prefabs" of them.
Less known for the public than MonoBehaviour (minor con, dunno if you need users to extend them and use them).
Leaked objects will log a warning about cleaning up leaked items when no longer referenced, some users may think they corrupted their project etc.
More awkward to debug them since there's no way to show them in the heirarchy that I know of (not a problem if you store them as assets, I guess).
Neutral:
No clear ownership of the object. When it it supposed to die? What scope does it belong to?
If you chose MonoBehaviour as your base, you could consider these hints as well:
MonoBehaviour Tricks/Hints:
Can avoid inspector pollution if setting hideFlags on instances.
Clear ownership - if a game object is destroyed, so are all of its components.
Users are familiar with MonoBehaviour.
Easy to make prefabs that contain (visible or hidden) components by default.
Can accept events such as Update, OnCollision, anything passed via SendMessage.
Easy for users to discover via GetComponents.
Possible problem with both approaches:
Large quantities of these objects (or any objects in general) may be detrimental for app performance, especially for mobile devices. This is because if/when the garbage collector need to mark live objects, it has to traverse the object graph to find objects that are no longer in use. Likewise, Unity has to do the same to find orphaned items (like discarded textures or scriptable objects, any kind of UnityEngine.Object). While this alone won't cause the GC to run or Unity to start trying to collect discarded items, the time it takes to traverse the graph will increase. If you can avoid using as many items, your GC.Collect and Resource.UnloadUnusedAssets performance may go up. However, it's been a while since I really saw any problems with Resources.UnloadUnusedAssets so you may want to do a stress test. Create 10k items and do an unload (you don't actually need to unload any of them, just force Unity to check the tree). Check the profiler for any spikes. Now do the same again with 0 items and see if there's a noticable difference. Please run some tests on performance with varying object counts and see if this is a problem or is just a phantom worry I impose on you. I don't have the numbers myself and the test is very easy for you to carry out yourself.
Originally, ScriptableObject was designed to hold large quantities of data that would be inappropriate to store on each MonoBehaviour. But technically we can use it any way we see fit. This is to reduce data duplication and to reduce memory by sharing a single object. At least, that's what I've heard from very reliable sources - it may not be strictly designed for that task alone.
This list is at least partially wrong. For example, SO's don't live as objects in a scene like Mono's so there are no active memory considerations and negligible performance issues as their data is only accessed on the creation of a referencing object. Better yet, just watch the Unity Tutorial on Scriptable Objects here.
Answer by logicandchaos · Jan 20, 2021 at 03:00 PM
MonoBehaviours live in the scene, ScriptableObjects live in the project folders. If you need a game object in the scene than use a MB, if you want to have data across scenes use an SO. There are many situations where you could use either or, or even using a class that inherits from neither.
I think you should check out Scriptable Object architecture to see all the uses and benefits to Scriptable Objects!
https://www.youtube.com/watch?v=6vmRwLYWNRo&t=81s
Answer by nventimiglia · Jul 15, 2014 at 05:47 AM
Plug into the Editor Inspector, so you can adjust fields (like mono)
Are Serialized by the Editor, so the fields are saved (like mono)
Live inside the hierarchy (not in the scene) (unlike mono)
Perfect for static services. You see them used by third party service providers.