I have seen people writing about how to accomplish having a “runtime variable” that in ScriptableObjects that gets reset eachtime the Unity Editor starts to play. They suggest something like this:
public class PlayerData : ScriptableObject, ISerializationCallbackReceiver
{
public int maxLife;
[NonSerialized] public int life;
public void OnAfterDeserialize()
{
life = maxLife;
}
public void OnBeforeSerialize()
{
}
}
However I believe this relies on Domain Reloading being turned on. I think Unity used to always do Domain Reloading, but recently they allow you to go into the Editor to shut it off
(Example of how to do that)
Shutting of Domain Reloading vastly speeds up entering playmode, and I do not want to give that up.
Does anyone have an alternative suggestion to allow me to keep some runtime variables in my ScriptableObject?
You only believe, or did you actually verify to know for sure?
I don’t think it depends on domain reload since there are no static fields involved, and the ScriptableObject is an asset on disk that ought to always get deserialized into memory when entering playmode.
All I can say for sure is if I turn Domain Reload on (or cause Unity to recompile the scripts by making an insignificant code change which also reloads the domain) then the ScriptableObject will call OnAfterDeserialize and reset the value.
But if I have Domain Reload off, then it will not.
Yes it would require a domain reload (whether that be entering play mode, or just a regular one) to cause the reset, as it happens whenever an asset of that type is reserialised. It will also reset when it’s viewed in the inspector, and every repaint afterwards where the asset gets reserialised. Meaning it would break being able to view these assets at runtime for debugging.
It’s a pretty poor-man’s way of doing asset resetting. Years ago I built a system where I could simply have assets implement an interface, or decorate them with an attribute, and an editor-only system would handle all the resetting when exiting play-mode for me. It’s not a hard system to set up, either.
But you know that Debug.Log is a thing, right? You have the code. You just add a single line to your code to see if the method is executed or not. We won’t start up Unity, recreate your setup to test this out for you.
Subscribe/unsubscribe to EditorApplication.playModeStateChanged in OnEnable/OnDisable or Awake/OnDestroy and take action when the change is ExitingEditMode.
That’s what I did around 2 years ago to avoid having to write the same thing over and over again. And it’s worth pointing out, this isn’t what ISerializationCallbackReciever is meant to be used for.
Yeah, I stumbled upon this whole manifesto while researching this problem that the whole workflow of using ScriptableObjects is possibly one big, ugly hack. Now I am nervous.
Programming in general is one big ugly hack, literally always. Every approach has advantages and disadvantages. I can recommend this video which takes a look at programming paradigms and what they actually mean. Using scriptable objects as variables (as suggested by Ryan Hipple) is specifically an approach in larger teams to give game designers the freedom to change and express the functionality they have imagined without the need / help of a programmer. See his pros and cons. If you are a solo indy developer, it “probably” doesn’t make much sense to use such an approach, but it’s still possible. Not every workflow suites every development team. Those are all just tools and you want to use the tool that you are comfortable with the most and that does actually help you in your design and development process. There is no golden hammer or silver bullet that will solve all your problems
I would say it’s not wrong. But I think the really issue is that when folks see that Unite video, they think they need to make everything in their project in that manner. When really, it can just be used in a few places as needed. All things in moderation, as they say.
That said, I do agree with it that more people need to learn to use and model their data in regular plain C# classes. That’s an important requirement for implementing a lot of mechanics/systems cleaning.
This is different from the playmodeStateChanged event though, which is editor-only and will guarantee the reset either on entering or exiting playmode.
SOs fit best into designs when you use them as abstract data and function containers. While they fit into other use cases, it’s basically by coincidence and doesn’t work well.
I don’t recommend using them like you’re trying to. The edge cases are very annoying and they’re not intended to function like that. If you do want them to work that way then the best thing to do is to create create an instance of them at runtime and manipulate that instance, which dies like a normal class would. The reference/source/template SO should be preserved and not manipulated at runtime. Only read/clone.
Or at least pure clickbait DONT DO THA TERRABAL THIGNS!11!!1!!! style writing.
If even half of that was true then every Unity project I have and every commercial Unity project I’ve had the pleasure of touching is completely and utterly doomed.
ScriptableObjects have their place. They can give incredible leverage to do things better in Unity.
Like all tech they can be abused. That’s on you.
This is engineering after all, not dogma / religion.
Meanwhile, I made something called Datasacks and I use it in a fair number of my projects these days.
If you are using any version of Unity later than Unity2021, it may be necessary to add this line to your Packages/manifest.json, or add it via the Package Mangler: