ScriptableObject is actively pushed by UT as Architectual solution:
- Better Data with Scriptable Objects in Unity! (Tutorial)
- Unite 2016 - Overthrowing the MonoBehaviour Tyranny in a Glorious Scriptable Object Revolution
- Game architecture with ScriptableObjects | Open Projects Devlog
- Unite Austin 2017 - Game Architecture with Scriptable Objects
And you can find hundreds tutorials from community with this approach
Today at 2022 is it still optimal solution? Is it really universal, at all approaches? Lets Consider different approaches to data in development, and how we can (if can) resolve different requirements by ScriptableObject, and is this solution optimal or we need better solution from UT.
Shared State
Data example: Max HP
This approach is in every video-tutorial, as you can found, ScriptableObject is greatly share data between many MonoBehaviour-Instances.
How we can resolve this with ScriptableObject?
We create one ScriptableObject and assign it to all our target-user-scripts
Are available solutions optimal and by my opinion fully resolve approach?
- yes
Non shared State
Data example: Current HP
The opposite for Shared, this approach expect that data is unique for every instance of target user-script.
How we can resolve this with ScriptableObject?
We can use ScriptableObject.CreateInstance() in MonoBehaviour.Start()
Are available solutions optimal and by my opinion fully resolve approach?
- almost. only one problem is that if you create ScriptableObject dynamicly it has no file, and you can’t observe them all in one list. We can write some DynamicScriptableObjectExplorer, but may be UT should do this for us?
Temporal
Data example: Current HP
Expects that value shouldn’t be saved between playmode-sessions and between buildrun-sessions.
How we can resolve this with ScriptableObject?
Buildrun: default ScriptableObject behaviour resets values to defaults at buildrun
Playmode: We can use MonoBehaviour.Start() to reset values in ScriptableObjects or InitializeOnEnterPlayMode attribute, but it should be used with static function so you need to implement some logic reinit all your instances of scriptableObjects.
Are available solutions optimal and by my opinion fully resolve approach?
- partialiy resolved. In terms of Shared non-shared state Temporal values are always a problem, cause sometime we wanna share Temporal values between systems (share current HP for DamageSystem and HUD), but not share them between instances (Player, Enemy1, Enemy2 etc).
What is your expirience and approach with Temporal Data in ScriptableObjects?
Default for Temporal
Data example: Start HP
Shared or non-shared this value need to be available for our ResetTemporal() logic.
How we can resolve this with ScriptableObject?
Shared approach work fine here, we create one ScriptableObject and assign reference in our MonoBehaviours to it
Are available solutions optimal and by my opinion fully resolve approach?
- yes
Hardcoded
Data example: Item ID
At this moment mutabilty of values of ScriptableObject is bad for identifiers. It can lead to human-factor errors, duplicative IDs, force developer to write boilerplate validation-systems.
How we can resolve this with ScriptableObject?
At this moment UT itself use code generation for hardcoded data, you can found example of this in UT Input System. It’s widely used to generate Enum, with hardcoded items list, this can be used for Item ID, with human-readable names, instead of simple Int32.
Are available solutions optimal and by my opinion fully resolve approach?
- yes
Non Hardcoded
Data example: Item Database
We need to allow gamedesigners to expand item database, if possible - without programmer participation.
How we can resolve this with ScriptableObject?
We create Item : ScriptableObject and input-point for ScriptableObject-list (separated ItemsDB : ScriptableObject or allow to load Item : ScriptableObject by ItemID : int / string , from fixed address ).
Are available solutions optimal and by my opinion fully resolve approach?
- yes
Overrides (prefab variant analogue)
Data example: Item Database
Items in database can differs from eachother only few values, for example Heal Potion 50%, Heal Potion 10% - this items has same Name, Icon, Description, behaviour, pickup prefab, pickup sound, only difference - heal value. Overrides allow to simplify long support, when we have dozens of mostly similar items.
How we can resolve this with ScriptableObject?
- there is user-defined solutions for ScriptableObject variants, based on reflection:
– free: GitHub - GieziJo/ScriptableObjectVariant: Unity Odin editor helper which permits to set a "SOVariant" attribute to a ScriptableObject and override, or not, certain fields (similar to prefab variants but for scriptable objects). has specific GUI
– paid: Asset Variants | Utilities Tools | Unity Asset Store - we can use prefabs instead of ScriptableObjects, but we lose serialization.
- in Unity 2022 Material Variants are implemented, we are really waiting same for ScriptableObjects
Are available solutions optimal and by my opinion fully resolve approach?
- no. We are waiting builtin ScriptableObject Variants
Later:
Hierarchical
Flat (Sibling)
Serialization
polymorphysm
Version Control (Git)