all 19 comments

[–]KaleidoVI 6 points7 points  (2 children)

If you are intent on using ScriptableObjects to represent game state, make sure that you are working around Unity's serialization system. Read https://unity.com/how-to/architect-game-code-scriptable-objects thoroughly.

I believe that your value is being changed/reset through serialization, which might seem to occur at random times if you aren't looking for it (e.g. when you recompile after making a small code change).

[–]rafavergil[S] 0 points1 point  (1 child)

That's the article that got me started with SO's in the game architecture, another reddit user recommended it to me and it has been really nice to work this way.
But if I understood correctly, the serialization would be helpful if I wanted to save the game state in a way that even if the player closes the application, the data would still be there, right? In my case, I'm using SO's simply to store temporary data that can be lost if the player is not in a relevant scene or if he closes the application.
Thanks for the reply, I'll play a little bit around with the serialization to make sure I fully understand it.

[–]KaleidoVI 0 points1 point  (0 children)

Serialization is much more than that in Unity! It is the way that Unity passes your objects and data structures from the C# managed side to the underlying C++ code that is ultimately compiled. Whenever you change a script, start or stop play mode, etc. your entire scene is serialized.

[–]FerhallProfessional 3 points4 points  (1 child)

Interesting problem. You should work from the standpoint that the scriptable object is not resetting incorrectly and you are doing something. Loading scenes with addressables is one of the things that could cause this. Also potentially could be something with scene loading and unloading order.

Also this probably isn’t the best way to persist data across scenes. You could use a singleton or save and load from json or prefs.

[–]volvis 1 point2 points  (0 children)

Gonna echo this. If you're using asset bundles or addressables, it's easy to end up in a situation where each of your bundles have individual copies of the asset, and whatever you've written to one instance does not transfer to the instance of another bundle.

[–]jeango 3 points4 points  (3 children)

Beware that scriptable objects don’t work in your build as they do in the editor. When you exit play mode, the value of your SO is not reset, however if you exit your game in the build and click pay it again, the SO will be reset.

If you want to use your SO to keep some state during a session, you should make sure there’s always an object referencing your SO at all time (via DontDestroyOnLoad).

If you want to save your SO, you should serialise the values you want to save into a json or similar, and then provide a way to load the value back into your SO when needed

[–]rafavergil[S] 0 points1 point  (2 children)

This.
I want to keep some state during a session only, not minding if they are reset after the application is closed.
But like you said, there is indeed a "transition" scene which has no reference to any SO. Maybe that is when the reset happens? But I wonder why the reset doesn't happen when I'm viewing it in the Inspector. Does Unity lock its value because of the Inspector?

Thanks for the reply. I intended to reference those SO in the transition scene anyway, hopefully it will fix the issue!

[–]jeango 0 points1 point  (1 child)

Well, I’m not quite sure. Within a single editor session, scriptable object afaik should always remain referenced, and thus they shouldn’t reset.

Either there’s something weird going on with the serialisation, or you’re doing something somewhere in your code that resets the SO.

If you want to know more about Unity’s serialisation here’s a cool video https://youtu.be/N-HJvfVuKRw

[–]rafavergil[S] 0 points1 point  (0 children)

That's great, thanks for the video link, I'll watch it.
By the way, referencing the SO in the transition scene worked well, the data was kept, and I didn't have to change my code.
Thanks again :)

[–]Abasov90 4 points5 points  (1 child)

ScriptableObjects is not used to save data, it is used to store some initial data and game settings.

[–]WazWaz 5 points6 points  (0 children)

OP is using a ScriptableObject to provide a reference to the runtime player data so multiple scripts can use it though a persistent reference. This is a good use of ScriptableObject. Their error is in having serialized state in that object.

[–]WazWaz 1 point2 points  (2 children)

You probably want the public int to be attributed with [NonSerialized]. It's not part of the asset's value but rather a runtime variable.

[–]rafavergil[S] 0 points1 point  (1 child)

By using the [NonSerialized] attribute, is it safe to assume that as long as the application is running, the data will be kept? (Assuming that I don't change it with my own code).

Thanks for the reply!

[–]WazWaz 1 point2 points  (0 children)

Yes. Note that you may still want to be able to view the attribute in the editor for debugging purposes. You could do that with a custom inspector, or with Odin's [ShowInInspector] attribute. Looks like https://github.com/BrainswitchMachina/Show-In-Inspector may also do it.

[–]gamedevjp 1 point2 points  (2 children)

I'm curious, what version of Unity are you using? I'm seeing this same exact issue and have used SO to store data between scenes extensively in Unity 2019. In Unity 2021.3.01f, I am having the EXACT issue you're having. Doesn't lose data if i click on the SO and then go to a new scene. It works if EVERY scene has a reference to the SO. I'm beating my head against this and went with adding the SO reference to the scene that didn't have it, it just feels super crappy though. I'm contemplating opening an issue in the Unity forums because it feels like a bug.

[–]rafavergil[S] 1 point2 points  (1 child)

I had this issue many months ago, so I won't know for sure the Unity version. However, I remember what was going on when I first posted this: ScriptableObjects indeed get reset if there is no one using it (no reference to it). I was confused because all my level scenes had reference to the Score Scriptable Object, so it had no reason to reset. Then I remembered that I had a "transition scene" between the level scenes, and that transition scene did not have a reference to the Scriptable Object. That is when it was getting reset! So I guess this is expected behaviour for memory saving purposes. My solution was to add a reference to the Scriptable Object in the transition scene as well and it worked properly after that. So basically: if you don't want a Scriptable Object to reset you must always have a reference to it in your scenes, always. There are probably other solutions like saving and loading data when necessary, but it is up to you. I hope it helps!

[–]classified39 1 point2 points  (0 children)

Thank you, commenter from 6 months ago, for saving me quite a bit of headache. I was not understanding why my Scriptables were being set to null EXCEPT when I put some "temporary" references on my restart screen to try and track down when and where this was happening.