- Home /
How Are You Resetting Your Scriptable Objects Between Play Tests?
Every video/guide about why you should use Scriptable Objects seems to very quickly gloss over the fact that the data in a Scriptable Object does not get reset when you exit play mode in Unity.
i.e. If I have player health stored in an SO and I enter play mode to test/debug my game, and my player loses some health in that play session, the health in the SO stays at that level next time I enter play mode. Obviously making it a nightmare to play test and debug.
Potential Solution 1 - Create Instances
I've seen some people say they just create an instance of the SO at runtime, then the changes get made to that instance and the instance dies when you exit play mode so you're all good... but that seems to have a huge problem unless I'm missing something. In most cases we're using SOs to hold shared data, so if you create a separate instance then that's no longer shared data - its a unique instance and other items referencing the SO will not see the same data. Now of course you could just create one instance and have all your other components refer to that one instance, but aren't you basically ending up with a singleton and defeating the point of using SOs in the first place?
Potential Solution 2 - Reset Values To Defaults
So given how bad the first "solution" seems to be, this is the only real option I can think of. The idea being that you have a script that runs when the game first loads (or on a per scene basis) and automatically resets values on every SO to be their correct default values.
But where should that script get its values from? Obviously I want to avoid hard coding them in the script. So these are the two best options I can think of:
1. It could pull them from an external file (XML etc) that I manually set the correct default values in.
2. Instead of getting the values from an external file, the script would reference another SO in my project that acts as a "template". This template SO would never be referenced elsewhere in my code so it would stick to whatever values I enter in the designer and never change. This approach seems better as it keeps everything easily visible and editable in the Unity designer, but it also feels a bit hacky and weird having to create two SOs for every SO. They would both be holding essentially the same data but one of them I just let get messed up by play testing and one of them I have to remember to never drag into any scenes or access in code (other than the initial loading script). Doesn't that end up being just as bad as using uninstantiated prefabs instead of SOs, where you have to remember certain prefabs are "special" and not to be used in scenes like others. I guess I could just have one master "default settings" SO that contains default values for all other SOs, and the script reads from that and populates the relevant SOs, which feels a bit less weird but still not ideal.
With either of those options though there's several issues I'm not keen on or unsure about. Like how this means for every single SO in the entire game, I need to remember to add its default values to my template/master list, and then add code to reset it to those values.
So yeah I'm keen to hear how other people deal with this stuff in the real world, and if I'm on the right track or if there's some better alternative I've not thought of (quite likely). I should point out I'm very new to game development (but have several years of experience with .NET desktop app development) so by all means correct things I've got wrong or point me in a different direction if I'm suggesting doing things in a weird way :)
I don't think you're suppose to change things in scriptable objects. They're for "stats" type data. Like an army tank prefab would link to the scriptableObject with data on that tank type.
Yeah the more I'm playing around and actually trying to use them in a project, the more I'm inclined to agree. What was tripping me up was that I was using player health as my first example, as that is something that multiple areas of my project are going to need to reference and I wanted to use a scriptable object for that because it is shared state (ins$$anonymous$$d of a singleton that all of these areas would have to reference).
But now I've realised I can just have my player/enemy health scriptable objects store the max health each entity should start with, then the actual runtime value of how much health the player currently has can be on that same scriptable object (so that everything can have access to it) but marked as [NonSerializable]. I'm not sure if this has any unwanted side effects though... but so far it seems to make it work how I want. Unity won't store that value in the SO asset, so it doesn't persist anywhere at all.
Answer by Meishin · Sep 10, 2019 at 04:26 AM
If you don't want to instance SO the easiest way to deal with that is by creating a/any serialized private field used in editor, and another during runtime
public class Ability : ScriptableObject
{
// Editor value
[SerializeField] private float baseCoolDown = 1f; // Base cooldown
// Internal variables
// Ability CoolDown
private float coolDown;
public float CoolDown { get { return coolDown; } }
// Initialize coolDown with editor's value
private void OnEnable()
{
coolDown = baseCoolDown;
}
// You can also use OnAfterDeserialize for the other way around
public void OnAfterDeserialize()
{
}
}
Sure it kinda looks clumzy tho if your code gets more complicated it allows you to easily and safely control your class own properties.
Also instancing SO could make sense since you are changing your SO's values at runtime which means you're using SO as any custom class but with standardized initial values - like for spells in a fantasy game for ex, if i want my player A and B to have a cooldowns on this ability depending on their stats, i'll define a baseCooldown and then at initialization (or even when the ability is triggered) modify their cooldown depending on their stats (i.e. independantly for player A and B).
Yeah I ended up co$$anonymous$$g to that conclusion and doing basically the same thing but in reverse, by marking my runtime field with [NonSerialized]. Same end result really.
Answer by Djaydino · May 26, 2021 at 09:56 PM
Hi. We used a different way which worked for us :
have an editor script with a
private static void OnPlayModeChanged(PlayModeStateChange state)
{
if (!EditorApplication.isPlaying)
{
}
else
{
}
{
When start play, store the things that you not want to change
And when stopped you can have a popup to accept / decline for some that you might want to change after all. (don't forget to use a EditorUtility.SetDirty(); on the scriptables )
Answer by Reijii · Dec 03, 2021 at 09:35 PM
Alternatively, you can save SO values as Preset in upper right corner
I have been checking this, and tried some things in my project. But still I don't find how I can use Presets to keep my SO state get reseted when I exit Play Mode. Can you expand the explanation?