Most optimal way to set up multiple save slots?

Greetings,

I would like to try and start on a serious project, but I’m wondering something…

See, I would like to add multiple save slots, you know, File A, B, and C? For example:

Now, I’m aware that from the Save and Load tutorial, it’s incredibly optimal and convenient to just save everything to one global structure, but with multiple files, what do I do??

Do I put all my global variables in one save file and then copy it for files B and C? And if I need to add any, wouldn’t that complicate things?


This will get extremely messy. Is there a more optimal way to do this?

Your general idea is fine, but what you’re missing is that your save slots should exclusively be for saving/loading data. You don’t use them during normal gameplay.

You should have a structure that is basically “gameState” which has the same variables, and those are the ones you use for game logic (so it’s always one set).

Then when you save/load, you can just load the slot structure that you care about to overwrite the gameState variable structure, and the inverse when saving.

Edit: Basically, you save the entire “saveSlot” structure when you want to save, and load it at the start of your game. But when you actually load a save slot, you just take that structure and load it into the gameState structure. You prebuild your gameState structure, and don’t have to prebuild any of the save slots, because you’d be overwriting the save slot with the gameState structure, then saving the parent.

As an example:

gameState
---CurrentRoom
---PlayerHP
---PlayerMaxHP
---PlayerXP

saves
---slot1
------CurrentRoom
------PlayerHP
------PlayerMaxHP
------PlayerXP
---slot2
------CurrentRoom
------PlayerHP
------PlayerMaxHP
------PlayerXP

during gameplay you’d use gameState.PlayerHP for the current HP.

When you save, you’d use the “Load JSON into Global Variable” with the expression GlobalVarToJson(gameState) as the JSON, and the global variable of “saves.slotNumberHere” to overwrite the save slot’s variables. Then you would save the entire saves structure. When the game starts, you’d load the saved data into the saves structure, and when you “load the game”, you’d do the inverse Load JSON into Global Variable, back into gameState.

3 Likes

How interesting, I wouldn’t have guessed to approach it like that. I’m gonna have to try this out later, I’ll get back if I have anything else. Thanks!

Alright, I’ve spent some time working on an example for this:


It’s still a work in progress, but it’s getting somewhere.



As of right now, I just have it hardcoded to save to one slot, as marked by the red circles. I would like for them to read Saves.save(a, b, or c, depending on what save is currently selected), but I can’t figure out the expression I need to accomplish it.

It’s currently still flawed, deleting the data is finicky, and doesn’t work unless I reload the scene, then delete again.

I have the foundation set up, but now I’m stuck on how to finalize it…

(Also, did I set everything up like you said so? If not, please let me know!)

I think you’re overcomplicating your events here. You can have one event and dynamically pull which slot data to populate. You shouldn’t need one for each slot. Also while Linking is a good idea, it’s probably more complicated then just giving each SlotData a “slotID” variable.

I would probably just give each instance a slotID variable, and load their data from the Saves structure.

Basically:

  1. Remove the “for each instance of slot” event
  2. Add a slotID variable to each instance of SlotData you have on the map, populate them with the slot they’re for (a/b/c)
  3. Update your event to just “For each SlotData” with an action of:
Change the text of SlotData = "EXP: " + Saves["Save"+SlotData.slotID].EXP + NewLine() + "Money: " + Saves.["Save" + SlotData.slotID].Money +etcetcetc"

It should have a single event that deals with all of your slots, and continue dealing as you add more.
Note: I haven’t messed with this since they added the ability to not declare the variable type. You might need to wrap SlotData.SlotID in a GlobalVariableString(), but you might not.

Your save event has the last part wrong. You would just Convert JSON string into Saves, not Saves.Savea


Your delete would delete all saves, which is fine if thats what you want to do.

Otherwise you would just use the “Clear Children” action of the saveslot variable the player chooses, then save that back to GameSave


Thank you, I was wondering how I can put them all in one single event, I wouldn’t have guessed to use brackets.

I’ve made the updates you said to do, but I’m stuck again:

The delete event isn’t finished yet, but I tried to apply the same logic from above to specify which slot I’m saving to. Turns out though, even with the changes, the game will always save to slot a, even if GameState.CurrentSlot is b or c.

Also, when changing the code from Saves.savea to simply saves, the game now has trouble specifying which slot to save to.

Am I on the right track?

image

You’re telling it to use a number variable (GlobalVariable() pulls number/value variables only) instead of a string variable, which your GameSave.CurrentSlot is. You need to use the appropriate expression for GlobalVariableString.

Thanks, the error is now gone.
Unfortunately, now nothing works. Saving doesn’t save the data to slots a, b, or c anymore, but instead it just makes an entirely new child in the structure:

Did I forget to add something?

You seem to be saving just a “slot”. You should be saving the entire save structure every time.

Otherwise you’re only going to have the slot in your saved data, and therefore overwrite just down to the top level.

Edit: I’d make sure to clear your storage entirely and see what gets saved after an attempt. Maybe your expression isn’t set up right, or maybe it’s not getting saved.

Okay, I’ll run a test:


Filled in some values (Before saving). Currently selected slot: a


After saving. Slot a didn’t change, but an entirely new field of parameters appeared in the Saves structure instead of saving to a specific slot. It even grabbed the “CurrentSlot” value and put that in there. If I change to a different slot, it simply changes the “CurrentSlot” value in saves.


After shutting down the preview and booting it back up again. The values from before are still saved in those parameters, but not loading in properly. Loading and deleting does absolutely nothing to any of the code or stored values.

Here’s all the code again, with all the updates:

I have a feeling that simply storing it in Saves isn’t going to work as intended. Do I need to add an expression to specify which slot to save it to?

Edit: Here’s the global variable structure too if it helps:

--------------------UPDATE--------------------
I’ve changed the code to look like this now:

As a result, saving now works! It now properly saves to the selected slot!

However, as you can see in the screenshot above, the CurrentSlot value is added as a child of the structure, which ideally I don’t want. Will I need to move it outside of the GameState variable?

Also, loading is still kind of finicky…
Here I have all 3 slots filled up with something. When I restart the scene, everything stays loaded in their slots:

But once I close the preview and relaunch it:

Only slot a has it’s values stored. Slot b and c were wiped.
In fact, slot a literally stole the values from slot c???

Deleting still doesn’t work properly, but it’s likely because I still have some of it’s code disabled for now. We’ll cross that bridge when we get there.

I’ll keep tinkering, but do you have any pointers?

That looks like the save slot child structure isn’t being read for saving and/or created upon loading. You might need to reevaluate your dynamic expression, and you might need to wrap the whole thing within the brackets inside of a ToString expression.

How would I do that? I’m not sure where I need to put ToString, and there’s no autocompletion to work with either. Everything I try to type returns with an error.

There’s a few things going odd here.

  1. I still think you should not be saving GameState at all. you should be applying the GameState to your save slot then save the entire save structure.
  2. You changed from brackets to parenthesis, which won’t work. It’d be something like Saves[ToString(“Saves” + CurrentSaveSlot)]
  3. You should probably remove the “current slot” from your GameState and have it as a separate global variable, especially as it doesn’t need to be saved.

Alright, I’ve switched to brackets and added ToString, but I still get an error thrown back at me:

I’m confused on not saving GameState though. Do you mean I don’t even need the GameState variable at all, and I should just be saving directly to one of the three slots depending on which slot is chosen?

If so, then going back to this code for example…


Does this imply that I need to replace every single instance of GameState with “Saves[“Save”+CurrentSaveSlot]?”
For example: Saves[“Save”+CurrentSaveSlot].EXP, Saves[“Save”+CurrentSaveSlot].Money, etc etc?

No, Gamestate is for your currently running game. Not saving

You apply the gamestate to your saveslot child variable right before you save, then save the saveslot structure variable in full. Gamestate itself should never be saved to storage. This is important incase the player realizes they screwed up a choice/death/etc and wants to reload the game without daving. If you are directly using the save slot for game logic it becomes much harder to go back without messing with storage even more.

The inverse is true when loading. At the start of the game you should load the entire saveslot structure, then when selecting a game slot you load that slot child variable over gamestate in full and then start the the game.

Super high level.
Save: Gamestate > Slot A > SaveSlots to Storage

Load;Storage to temp variable > temp variable to Saveslots > Select SlotA > Gamestate

I don’t have time to check proper dynamic variable syntax, but if you fix the above it will help you overall. I’ll try to check specifics sometime this week.

Ahh yes, I see, I have to take what’s in GameState and save it inside of Saves.
The thing I’m struggling with is HOW to do that…
According to this post, it can be done with JSON. In that case…
Do I need to use JSON to move the values to another variable, and THEN save them in Saves?


What I’m trying to do in the above code:

  1. Use JSON to move the values from GameState to Saves.Save(CurrentSaveSlot) in this case, a.
  2. Save the entire Saves structure.
  3. Immediately load the entire Saves structure, and parse the loaded JSON so that the text updates dynamically.

When I tried it this way, it didn’t work. The debugger shows that it’s just creating a new structure in the Saves structure instead of saving it in the selected slot. The error says that it’s unable to parse the values too.
I can’t use the ToString you suggested because it still gives me an error when it’s typed.

I’m aware WHAT I have to to, but I have no idea HOW to do it, or what events I have to use to accomplish it. Is using JSON to move the structure correct?

So I did a quick test:

  1. You should separate out the “CurrentSlot” variable into it’s own separate global variable, this is tripping you up and bloating your structure. It should not be saved at any point of the game and is only used when the player selects a save slot
  2. The new variable format structure makes it a bit easier, so it’d just be Saves[“Save”+CurrentSaveSlot], or if you’re not declaring variables, it’d be Saves[“Save” + GlobalVariableString(CurrentSaveSlot)], but you shouldn’t need this
  3. You will use the “GlobalVariabletoJSON” expression AND the “Convert Json String to global variable” in a few different places.

Here’s a quick mockup, I tested it successfully. Note that I am using “VariableString” in a few places as a test/old habits, so long as you are pre-defining your variables you can just use their name directly.

I did read some of your conversation but well too much for my dyslexia to go trough

I did read what Silver suggest to you
But i wonder why not simply use different Storage for save slots?

You have saving and loading as simple as it gets

Which you most likely are able to do

NOW
In my post i save to Storage named Storage

Assuming you have 3 buttons (You could have as many as you want 3 is just for example) there where they have different colors

Now imagine for Red button i am saving to storage named Slot1
Now i copy paste that event and for when Blue button is clicked i save to storage named Slot2
And so go on

And now you only care about about what button is pressed and to what storage name you are saving (I think you could even go with different group names like Group1 and Group2 and so go on instead of storage with different name)

I would say it is a lot easier than managing actual slots via strings

Separate storage will totally work fine, but requires more events.

The original ask was for most optimal way to do multiple slots, so the dynamic variables used in the events example I gave leads to the least amount of events needed, and if they decide to add more slots down the road, no new save/load events would be needed

Instead of storage name as Storage1 Storage2 and so go on
Where one for each slot would require separate event
It could be storage name → Stoarge(GlobalVariable(SlotID))

And now there would be only need of manipulating one variable instead of duplicating events

Wouldn’t that be better?