Saving object positions between scene loads (super amateur question)

I apologize in advance, but I’m very new to Unity and I’m very bad at programming. I’m sure the answer to this question is obvious, but I’ve been staring at it for too long now and I think the answer is just outside my range of knowledge.

I created two food bins for a scene in my game. Each food bin contains a sprite of food that moves down on the Y axis at 2 different points throughout the day (9am & 1pm). So far I’ve succeeded in making the food items move down the Y coordinate when those times of day are reached.

Here’s my issue:
I need to save the position of the food items in each bin so that the position can be reloaded if the player exits the building scene to the overworld, then re-enters the buildings scene. The problem that I’m having is that I can’t seem to figure out how to save and reload the position of the food items appropriately.

If you watch this recording you will see the food in “test bin 1” (the red food) remains in the appropriate transform position between scene loads. However, the brown / tan colored food in “test bin 2” moves to the same SavePosition as the red food from “test bin 1” after the player exits and re-enters the scene. You’ll see around ~1 minute in to the video if I hide the “test bin 1” sprites the food from test bin 2 is hiding underneath it after the scene reloads.

Can anyone provide me with some insight in how I might be able to go about fixing this?

I can see that FoodManager.SavePosition is being set at 2 different points in my console log, but I think only 1 of those positions is being loaded at the top of Update().

I’ve attached a screenshot of relevant scrips + a link to a video recording.



Thank you so much to anyone who can point me in the right direction.

Sounds like you wrote a bug… and that means… time to start debugging!

By debugging you can find out exactly what your program is doing so you can fix it.

Use the above techniques to get the information you need in order to reason about what the problem is.

You can also use Debug.Log(...); statements to find out if any of your code is even running. Don’t assume it is.

Once you understand what the problem is, you may begin to reason about a solution to the problem.

Remember with Unity the code is only a tiny fraction of the problem space. Everything asset- and scene- wise must also be set up correctly to match the associated code and its assumptions.

Load/Save steps:

An excellent discussion of loading/saving in Unity3D by Xarbrough:

And another excellent set of notes and considerations by karliss_coldwild:

Loading/Saving ScriptableObjects by a proxy identifier such as name:

When loading, you can never re-create a MonoBehaviour or ScriptableObject instance directly from JSON. Save data needs to be all entirely plain C# data with no Unity objects in it.

The reason is they are hybrid C# and native engine objects, and when the JSON package calls new to make one, it cannot make the native engine portion of the object, so you end up with a defective “dead” Unity object.

Instead you must first create the MonoBehaviour using AddComponent() on a GameObject instance, or use ScriptableObject.CreateInstance() to make your SO, then use the appropriate JSON “populate object” call to fill in its public fields.

If you want to use PlayerPrefs to save your game, it’s always better to use a JSON-based wrapper such as this one I forked from a fellow named Brett M Johnson on github:

Do not use the binary formatter/serializer: it is insecure, it cannot be made secure, and it makes debugging very difficult, plus it actually will NOT prevent people from modifying your save data on their computers.

A good summary / survey of the problem space:

FoodManager is a singleton, and SavePosition is a static variable. That means there is only ever just one instance of the SavePosition value. If you set SavePosition twice, the second value will just overwrite the first value. So then when you load the position, both objects will just get the last saved position.

You will probably need to keep a list, or even better a dictionary, of all food bin objects, and save the position for each of them there.

1 Like

Thank you so much for the fast responses. I’ll look all this over and post my results in the next few hours.

A number of ways to do it. One way is to have a static class with any variables (also static) that you want to persist across levels, like your food level. Static classes can’t be attached to GameObjects, so no need for DontDestroyOnLoad. In this case you don’t need to “save” anything, just modify the static variables as you need. I don’t know if this is a preferred/recommended way, but it works.

Worth mentioning that static classes don’t have the MonoBehaviour elements like Update(), Start(), etc. They’re used for (among other things) custom methods and variables and are accessible by any script in any scene without needing to get a reference to it:

myStaticClass.myVar = 10; // set variable
myStaticClass.myMethod(); // run method

You can also use static variables in non-static scripts, as you have, but those have to be attached to GO’s, so…why not use a static class for scene-agnostic stuff? Great place to put utility methods too.

PlayerPrefs are great too but only needed if you want values to persist across runs of the game itself. For across scenes they’re not needed.

k … i give up … spent 20+ hours trying to understand this now and i just dont get it

I need a really good example of this or i think im going to remain lost.

I can only seem to get the position of 1 item to save between scene swaps. All I’ve managed to do is figure out how to pull the position for 1 object from a static class on scene load.

My approach was to set 2 different static vector 3 positon variables contained within a static class after the in game time reached 1pm, then check if on scene load those variables were set or not and adjust the position according if the scene was loaded after 1pm in game time. That didnt work out. Both food items in the bin still appear in the same location when the scene loads.

This is rather complicated for someone who’s brand new to Unity.

Is the position the relevant bit of data here? Presumably the underlying remaining fruit is more important, from which the height of the sprite can then be derived.

Nonetheless, here’s an example I threw together:
SaveGameExample.unitypackage (65.3 KB)

You can open the scene, go into play mode, and pressing space will reduce the level of a random fruit bin. You can exit play mode, go back into play mode, and see those levels maintained.

The principles here are the same for most save game and cross scene persistence situations: you need to separate your game data/game state from the actual scene representation, and generally keep this data in memory for as long as you need it (usually for as long as a save game is loaded).

In the example, a fruit bin’s actual data is a separate object to the component. In Start, it gets the save data (which loads itself on-demand), and check if any data exists for its own unique ID. If so, it uses that, and if not it registers itself with the save data.

Because FruitBinInstanceData is a class, ergo a by-reference object, modifying the instance referenced by the FruitBinHandler is of course modifying the same instance held by the save data itself. Were it a struct, you would need to pass that modified instance back to the save data.

It looks like ane example for save-data, but this example would also work across scene loads. So long as the save data remains in memory, if you were to leave the scene, and go back into, the bins would retrieve their respective FruitBinInstanceData again and update the visual representation as they were beforehand.

And that’s pretty much what it boils down in a lot of situations. And depending on the type of game you’re making, this pattern will occur across a lot of your project, perhaps requiring you to abstract and genericize the process so as to reduce repeated boilerplate.

As I already linked you yesterday, load/save game is as complicated or simple as you want.

You are welcome to keep spinning with whatever you call “My approach” but I assure you it won’t change the problem space.

There are four (4) steps to save roughly speaking and three (3) steps to restore, as linked here:

If you miss ANY of those steps, it will not work.

Here are the steps again:

This is very difficult for me to understand with my current level of knowledge, but this is the example that I believe I needed. Thank you very much.

I’ll break it down piece by piece to try and wrap my head around how you achieved this. I’ll post here again if I have any questions.

1 Like